1 /*
2 	vfdshmenu.cpp
3 
4 	Virtual Floppy Drive for Windows
5 	Driver control library
6 	COM shell extension class context menu functions
7 
8 	Copyright (c) 2003-2005 Ken Kato
9 */
10 
11 #define WIN32_LEAN_AND_MEAN
12 #include <windows.h>
13 #include <shellapi.h>
14 #include <shlobj.h>
15 
16 #include "vfdtypes.h"
17 #include "vfdapi.h"
18 #include "vfdlib.h"
19 #ifndef __REACTOS__
20 #include "vfdmsg.h"
21 #else
22 #include "vfdmsg_lib.h"
23 #endif
24 
25 //	class header
26 #include "vfdshext.h"
27 
28 //
29 //	Undocumented windows API to handle shell property sheets
30 //
31 
32 typedef BOOL (WINAPI *SHOBJECTPROPERTIES)(
33 	HWND hwnd, DWORD dwType, LPCWSTR lpObject, LPCWSTR lpPage);
34 
35 #ifndef SHOP_FILEPATH
36 #define SHOP_FILEPATH				0x00000002
37 #endif
38 
39 #define SHOP_EXPORT_ORDINAL			178
40 
41 //
42 //	Context Menu Items
43 //
44 enum {
45 	VFD_CMD_OPEN = 0,
46 	VFD_CMD_SAVE,
47 	VFD_CMD_CLOSE,
48 	VFD_CMD_PROTECT,
49 	VFD_CMD_DROP,
50 	VFD_CMD_PROP,
51 	VFD_CMD_MAX
52 };
53 
54 static struct _vfd_menu {
55 	UINT	textid;			//	menu item text id
56 	UINT	helpid;			//	menu item help id
57 #ifndef __REACTOS__
58 	PCHAR	verbA;			//	ansi verb text
59 	PWCHAR	verbW;			//	unicode verb text
60 #else
61 	LPCSTR	verbA;			//	ansi verb text
62 	LPCWSTR	verbW;			//	unicode verb text
63 #endif
64 }
65 g_VfdMenu[VFD_CMD_MAX] = {
66 	{ MSG_MENU_OPEN,	MSG_HELP_OPEN,		"vfdopen",	L"vfdopen"	},
67 	{ MSG_MENU_SAVE,	MSG_HELP_SAVE,		"vfdsave",	L"vfdsave"	},
68 	{ MSG_MENU_CLOSE,	MSG_HELP_CLOSE,		"vfdclose",	L"vfdclose"	},
69 	{ MSG_MENU_PROTECT,	MSG_HELP_PROTECT,	"protect",	L"protect"	},
70 	{ MSG_MENU_DROP,	MSG_HELP_DROP,		"vfddrop",	L"vfddrop"	},
71 	{ MSG_MENU_PROP,	MSG_HELP_PROP,		"vfdprop",	L"vfdprop"	},
72 };
73 
74 //
75 //	local functions
76 //
AddMenuItem(HMENU hMenu,UINT uPos,UINT uFlags,UINT uCmd,UINT uText)77 static void AddMenuItem(
78 	HMENU			hMenu,
79 	UINT			uPos,
80 	UINT			uFlags,
81 	UINT			uCmd,
82 	UINT			uText)
83 {
84 	PSTR text = ModuleMessage(uText);
85 
86 	if (text) {
87 		InsertMenu(hMenu, uPos, uFlags, uCmd, text);
88 		LocalFree(text);
89 	}
90 }
91 
92 
93 //
94 //	FUNCTION: CVfdShExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
95 //
96 //	PURPOSE: Called by the shell just before the context menu is displayed.
97 //			 This is where you add your specific menu items.
98 //
99 //	PARAMETERS:
100 //	  hMenu		 - Handle to the context menu
101 //	  indexMenu	 - Index of where to begin inserting menu items
102 //	  idCmdFirst - Lowest value for new menu ID's
103 //	  idCmtLast	 - Highest value for new menu ID's
104 //	  uFlags	 - Specifies the context of the menu event
105 //
QueryContextMenu(HMENU hMenu,UINT indexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)106 STDMETHODIMP CVfdShExt::QueryContextMenu(
107 	HMENU			hMenu,
108 	UINT			indexMenu,
109 	UINT			idCmdFirst,
110 	UINT			idCmdLast,
111 	UINT			uFlags)
112 {
113 	UNREFERENCED_PARAMETER(idCmdLast);
114 	VFDTRACE(0, ("CVfdShExt::QueryContextMenu()\n"));
115 
116 	//
117 	//	Check if menu items should be added
118 	//
119 	if ((CMF_DEFAULTONLY & uFlags) ||
120 		!m_pDataObj || m_nDevice == (ULONG)-1) {
121 
122 		VFDTRACE(0, ("Don't add any items.\n"));
123 		return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
124 	}
125 
126 	//
127 	//	Drag & Drop handler?
128 	//
129 	if (m_bDragDrop) {
130 
131 		VFDTRACE(0, ("Invoked as the Drop handler.\n"));
132 
133 		if (GetFileAttributes(m_sTarget) & FILE_ATTRIBUTE_DIRECTORY) {
134 
135 			// if the dropped item is a directory, nothing to do here
136 			VFDTRACE(0, ("Dropped object is a directory.\n"));
137 
138 			return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
139 		}
140 
141 		//	Add a drop context menu item
142 		AddMenuItem(
143 			hMenu,
144 			indexMenu,
145 			MF_BYPOSITION | MF_STRING,
146 			idCmdFirst + VFD_CMD_DROP,
147 			g_VfdMenu[VFD_CMD_DROP].textid);
148 
149 		return MAKE_HRESULT(SEVERITY_SUCCESS, 0, VFD_CMD_DROP + 1);
150 	}
151 
152 	//
153 	//	Context menu handler
154 	//
155 	VFDTRACE(0, ("Invoked as the context menu handler.\n"));
156 
157 	//
158 	//	Get the VFD media state
159 	//
160 	HANDLE hDevice = VfdOpenDevice(m_nDevice);
161 
162 	if (hDevice == INVALID_HANDLE_VALUE) {
163 		VFDTRACE(0, ("device open failed.\n"));
164 		return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
165 	}
166 
167 	DWORD status = VfdGetMediaState(hDevice);
168 
169 	CloseHandle(hDevice);
170 
171 	//
172 	//	Add context menu items
173 	//
174 
175 	InsertMenu(hMenu, indexMenu++,
176 		MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
177 
178 	if (status == ERROR_SUCCESS ||
179 		status == ERROR_WRITE_PROTECT) {
180 
181 		//	An image is opened
182 
183 		//	insert the "save" menu item
184 
185 		AddMenuItem(
186 			hMenu,
187 			indexMenu++,
188 			MF_BYPOSITION | MF_STRING,
189 			idCmdFirst + VFD_CMD_SAVE,
190 			g_VfdMenu[VFD_CMD_SAVE].textid);
191 
192 		//	insert the "close" menu item
193 
194 		AddMenuItem(
195 			hMenu,
196 			indexMenu++,
197 			MF_BYPOSITION | MF_STRING,
198 			idCmdFirst + VFD_CMD_CLOSE,
199 			g_VfdMenu[VFD_CMD_CLOSE].textid);
200 
201 		//	insert the "protect" menu item
202 
203 		AddMenuItem(
204 			hMenu,
205 			indexMenu++,
206 			MF_BYPOSITION | MF_STRING,
207 			idCmdFirst + VFD_CMD_PROTECT,
208 			g_VfdMenu[VFD_CMD_PROTECT].textid);
209 
210 		//	check "protect" menu item
211 
212 		if (status == ERROR_WRITE_PROTECT) {
213 			CheckMenuItem(hMenu, indexMenu - 1,
214 				MF_BYPOSITION | MF_CHECKED);
215 		}
216 	}
217 	else {
218 		//	The drive is empty
219 
220 		//	insert the "open" menu item
221 
222 		AddMenuItem(
223 			hMenu,
224 			indexMenu++,
225 			MF_BYPOSITION | MF_STRING,
226 			idCmdFirst + VFD_CMD_OPEN,
227 			g_VfdMenu[VFD_CMD_OPEN].textid);
228 	}
229 
230 	//	Insert the "proterty" menu item
231 
232 	AddMenuItem(
233 		hMenu,
234 		indexMenu++,
235 		MF_BYPOSITION | MF_STRING,
236 		idCmdFirst + VFD_CMD_PROP,
237 		g_VfdMenu[VFD_CMD_PROP].textid);
238 
239 	//	Insert a separator
240 
241 	InsertMenu(hMenu, indexMenu,
242 		MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
243 
244 	return MAKE_HRESULT(SEVERITY_SUCCESS, 0, VFD_CMD_PROP + 1);
245 }
246 
247 //
248 //	FUNCTION: CVfdShExt::GetCommandString(LPCMINVOKECOMMANDINFO)
249 //
250 //	PURPOSE:	Retrieves information about a shortcut menu command,
251 //				including the Help string and the language-independent,
252 //				or canonical, name for the command.
253 //
254 //	PARAMETERS:
255 //		idCmd -		Menu command identifier offset.
256 //		uFlags -	Flags specifying the information to return.
257 //					This parameter can have one of the following values.
258 //					GCS_HELPTEXTA  Sets pszName to an ANSI string containing the Help text for the command.
259 //					GCS_HELPTEXTW  Sets pszName to a Unicode string containing the Help text for the command.
260 //					GCS_VALIDATEA  Returns S_OK if the menu item exists, or S_FALSE otherwise.
261 //					GCS_VALIDATEW Returns S_OK if the menu item exists, or S_FALSE otherwise.
262 //					GCS_VERBA Sets pszName to an ANSI string containing the language-independent command name for the menu item.
263 //					GCS_VERBW Sets pszName to a Unicode string containing the language-independent command name for the menu item.
264 //		pwReserved - Reserved. Applications must specify NULL when calling this method, and handlers must ignore this parameter when called.
265 //		pszName -	Address of the buffer to receive the null-terminated string being retrieved.
266 //		cchMax -	Size of the buffer to receive the null-terminated string.
267 //
268 
GetCommandString(UINT idCmd,UINT uFlags,UINT * reserved,LPSTR pszName,UINT cchMax)269 STDMETHODIMP CVfdShExt::GetCommandString(
270 #ifndef __REACTOS__
271 	UINT			idCmd,
272 #else
273     UINT_PTR		idCmd,
274 #endif
275 	UINT			uFlags,
276 	UINT			*reserved,
277 	LPSTR			pszName,
278 	UINT			cchMax)
279 {
280 	VFDTRACE(0,
281 		("CVfdShExt::GetCommandString(%u,...)\n", idCmd));
282 
283 	UNREFERENCED_PARAMETER(reserved);
284 
285 	if (idCmd >= sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0])) {
286 		return S_FALSE;
287 	}
288 
289 	switch (uFlags) {
290 	case GCS_HELPTEXTA:
291 		FormatMessageA(
292 			FORMAT_MESSAGE_FROM_HMODULE |
293 			FORMAT_MESSAGE_IGNORE_INSERTS,
294 			g_hDllModule, g_VfdMenu[idCmd].helpid,
295 			0, pszName, cchMax, NULL);
296 
297 		VFDTRACE(0, ("HELPTEXTA: %s\n", pszName));
298 		break;
299 
300 	case GCS_HELPTEXTW:
301 		FormatMessageW(
302 			FORMAT_MESSAGE_FROM_HMODULE |
303 			FORMAT_MESSAGE_IGNORE_INSERTS,
304 			g_hDllModule, g_VfdMenu[idCmd].helpid,
305 			0, (LPWSTR)pszName, cchMax, NULL);
306 
307 		VFDTRACE(0, ("HELPTEXTW: %ws\n", pszName));
308 		break;
309 
310 	case GCS_VERBA:
311 		lstrcpynA(pszName, g_VfdMenu[idCmd].verbA, cchMax);
312 		break;
313 
314 	case GCS_VERBW:
315 		lstrcpynW((LPWSTR)pszName, g_VfdMenu[idCmd].verbW, cchMax);
316 		break;
317 	}
318 
319 	return NOERROR;
320 }
321 
322 //
323 //	FUNCTION: CVfdShExt::InvokeCommand(LPCMINVOKECOMMANDINFO)
324 //
325 //	PURPOSE: Called by the shell after the user has selected on of the
326 //			 menu items that was added in QueryContextMenu().
327 //
328 //	PARAMETERS:
329 //	  lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
330 //
331 
InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)332 STDMETHODIMP CVfdShExt::InvokeCommand(
333 	LPCMINVOKECOMMANDINFO	lpcmi)
334 {
335 	VFDTRACE(0, ("CVfdShExt::InvokeCommand()\n"));
336 
337 	BOOL	unicode = FALSE;
338 	UINT	id;
339 	DWORD	ret;
340 	CMINVOKECOMMANDINFOEX *excmi = (CMINVOKECOMMANDINFOEX *)lpcmi;
341 
342 #ifdef __REACTOS__
343 	unicode = lpcmi->cbSize >= FIELD_OFFSET(CMINVOKECOMMANDINFOEX, ptInvoke) &&
344 	          (lpcmi->fMask & CMIC_MASK_UNICODE);
345 #else
346 	if (lpcmi->cbSize >= sizeof(CMINVOKECOMMANDINFOEX) &&
347 		(lpcmi->fMask & CMIC_MASK_UNICODE)) {
348 
349 		unicode = TRUE;
350 	}
351 #endif
352 
353 	if (!unicode && HIWORD(lpcmi->lpVerb)) {
354 
355 		VFDTRACE(0, ("ANSI: %s\n", lpcmi->lpVerb));
356 
357 		// ANSI verb
358 		for (id = 0; id < sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0]); id++) {
359 			if (!lstrcmpi(lpcmi->lpVerb, g_VfdMenu[id].verbA)) {
360 				break;
361 			}
362 		}
363 	}
364 	else if (unicode && HIWORD(excmi->lpVerbW)) {
365 
366 		VFDTRACE(0, ("UNICODE: %ws\n", excmi->lpVerbW));
367 
368 		// UNICODE verb
369 		for (id = 0; id < sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0]); id++) {
370 			if (!lstrcmpiW(excmi->lpVerbW, g_VfdMenu[id].verbW)) {
371 				break;
372 			}
373 		}
374 	}
375 	else {
376 
377 		VFDTRACE(0, ("Command: %u\n", LOWORD(lpcmi->lpVerb)));
378 
379 		// Command ID
380 		id = LOWORD(lpcmi->lpVerb);
381 	}
382 
383 	VFDTRACE(0, ("MenuItem: %u\n", id));
384 
385 	switch (id) {
386 	case VFD_CMD_OPEN:
387 		ret = DoVfdOpen(lpcmi->hwnd);
388 
389 		if (ret == ERROR_SUCCESS) {
390 			VfdImageTip(lpcmi->hwnd, m_nDevice);
391 		}
392 		break;
393 
394 	case VFD_CMD_SAVE:
395 		ret = DoVfdSave(lpcmi->hwnd);
396 		break;
397 
398 	case VFD_CMD_CLOSE:
399 		ret = DoVfdClose(lpcmi->hwnd);
400 		break;
401 
402 	case VFD_CMD_PROTECT:
403 		ret = DoVfdProtect(lpcmi->hwnd);
404 
405 		if (ret == ERROR_SUCCESS) {
406 			VfdImageTip(lpcmi->hwnd, m_nDevice);
407 		}
408 		else if (ret == ERROR_WRITE_PROTECT) {
409 			VfdImageTip(lpcmi->hwnd, m_nDevice);
410 			ret = ERROR_SUCCESS;
411 		}
412 		break;
413 
414 	case VFD_CMD_DROP:
415 		ret = DoVfdDrop(lpcmi->hwnd);
416 
417 		if (ret == ERROR_SUCCESS) {
418 			VfdImageTip(lpcmi->hwnd, m_nDevice);
419 		}
420 		break;
421 
422 	case VFD_CMD_PROP:
423 		{
424 			SHOBJECTPROPERTIES pSHObjectProperties;
425 			WCHAR path[4] = L" :\\";
426 
427 			pSHObjectProperties = (SHOBJECTPROPERTIES)GetProcAddress(
428 				LoadLibrary("shell32"), "SHObjectProperties");
429 
430 			if (!pSHObjectProperties) {
431 				pSHObjectProperties = (SHOBJECTPROPERTIES)GetProcAddress(
432 					LoadLibrary("shell32"), (LPCSTR)SHOP_EXPORT_ORDINAL);
433 			}
434 
435 			if (pSHObjectProperties) {
436 				path[0] = m_sTarget[0];
437 
438 				pSHObjectProperties(lpcmi->hwnd,
439 					SHOP_FILEPATH, path, L"VFD");
440 			}
441 		}
442 		ret = ERROR_SUCCESS;
443 		break;
444 
445 	default:
446 		return E_INVALIDARG;
447 	}
448 
449 	if (ret != ERROR_SUCCESS &&
450 		ret != ERROR_CANCELLED) {
451 
452 		MessageBox(lpcmi->hwnd,
453 			SystemMessage(ret), VFD_MSGBOX_TITLE, MB_ICONSTOP);
454 	}
455 
456 	return NOERROR;
457 }
458 
459 //=====================================
460 //	perform VFD menu operation
461 //=====================================
462 
DoVfdOpen(HWND hParent)463 DWORD CVfdShExt::DoVfdOpen(
464 	HWND			hParent)
465 {
466 	DWORD ret = VfdGuiOpen(hParent, m_nDevice);
467 
468 	if (ret != ERROR_SUCCESS && ret != ERROR_CANCELLED) {
469 		MessageBox(hParent, SystemMessage(ret),
470 			VFD_MSGBOX_TITLE, MB_ICONSTOP);
471 	}
472 
473 	return ret;
474 }
475 
476 //
477 //	Save the VFD image
478 //
DoVfdSave(HWND hParent)479 DWORD CVfdShExt::DoVfdSave(
480 	HWND			hParent)
481 {
482 	return VfdGuiSave(hParent, m_nDevice);
483 }
484 
485 //
486 //	Close current VFD image
487 //
DoVfdClose(HWND hParent)488 DWORD CVfdShExt::DoVfdClose(
489 	HWND			hParent)
490 {
491 	return VfdGuiClose(hParent, m_nDevice);
492 }
493 
494 //
495 //	Enable/disable media write protection
496 //
DoVfdProtect(HWND hParent)497 DWORD CVfdShExt::DoVfdProtect(
498 	HWND			hParent)
499 {
500 	HANDLE			hDevice;
501 	DWORD			ret;
502 
503 	UNREFERENCED_PARAMETER(hParent);
504 	VFDTRACE(0, ("CVfdShExt::DoVfdProtect()\n"));
505 
506 	hDevice = VfdOpenDevice(m_nDevice);
507 
508 	if (hDevice == INVALID_HANDLE_VALUE) {
509 		return GetLastError();
510 	}
511 
512 	ret = VfdGetMediaState(hDevice);
513 
514 	if (ret == ERROR_SUCCESS) {
515 		ret = VfdWriteProtect(hDevice, TRUE);
516 	}
517 	else if (ret == ERROR_WRITE_PROTECT) {
518 		ret = VfdWriteProtect(hDevice, FALSE);
519 	}
520 
521 	if (ret == ERROR_SUCCESS) {
522 		ret = VfdGetMediaState(hDevice);
523 	}
524 
525 	CloseHandle(hDevice);
526 
527 	return ret;
528 }
529 
530 //
531 //	Open dropped file with VFD
532 //
DoVfdDrop(HWND hParent)533 DWORD CVfdShExt::DoVfdDrop(
534 	HWND			hParent)
535 {
536 	HANDLE			hDevice;
537 	DWORD			file_attr;
538 	ULONG			file_size;
539 	VFD_FILETYPE	file_type;
540 
541 	VFD_DISKTYPE	disk_type;
542 	VFD_MEDIA		media_type;
543 
544 	DWORD			ret;
545 
546 	VFDTRACE(0, ("CVfdShExt::DoVfdDropOpen()\n"));
547 
548 	//	check if dropped file is a valid image
549 
550 	ret = VfdCheckImageFile(
551 		m_sTarget, &file_attr, &file_type, &file_size);
552 
553 	if (ret != ERROR_SUCCESS) {
554 		return ret;
555 	}
556 
557 	//	check file size
558 	media_type = VfdLookupMedia(file_size);
559 
560 	if (!media_type) {
561 		PSTR msg = ModuleMessage(MSG_FILE_TOO_SMALL);
562 
563 		MessageBox(hParent, msg ? msg : "Bad size",
564 			VFD_MSGBOX_TITLE, MB_ICONSTOP);
565 
566 		if (msg) {
567 			LocalFree(msg);
568 		}
569 
570 		return ERROR_CANCELLED;
571 	}
572 
573 	if ((file_type == VFD_FILETYPE_ZIP) ||
574 		(file_attr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED))) {
575 
576 		disk_type = VFD_DISKTYPE_RAM;
577 	}
578 	else {
579 		disk_type = VFD_DISKTYPE_FILE;
580 	}
581 
582 	//	close current image (if opened)
583 
584 	ret = DoVfdClose(hParent);
585 
586 	if (ret != ERROR_SUCCESS &&
587 		ret != ERROR_NOT_READY) {
588 		return ret;
589 	}
590 
591 	//	open dropped file
592 
593 	hDevice = VfdOpenDevice(m_nDevice);
594 
595 	if (hDevice == INVALID_HANDLE_VALUE) {
596 		return GetLastError();
597 	}
598 
599 	ret = VfdOpenImage(
600 		hDevice, m_sTarget, disk_type, media_type, FALSE);
601 
602 	CloseHandle(hDevice);
603 
604 	return ret;
605 }
606