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 //
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 //
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 
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 
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 	if (lpcmi->cbSize >= sizeof(CMINVOKECOMMANDINFOEX) &&
343 		(lpcmi->fMask & CMIC_MASK_UNICODE)) {
344 
345 		unicode = TRUE;
346 	}
347 
348 
349 	if (!unicode && HIWORD(lpcmi->lpVerb)) {
350 
351 		VFDTRACE(0, ("ANSI: %s\n", lpcmi->lpVerb));
352 
353 		// ANSI verb
354 		for (id = 0; id < sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0]); id++) {
355 			if (!lstrcmpi(lpcmi->lpVerb, g_VfdMenu[id].verbA)) {
356 				break;
357 			}
358 		}
359 	}
360 	else if (unicode && HIWORD(excmi->lpVerbW)) {
361 
362 		VFDTRACE(0, ("UNICODE: %ws\n", excmi->lpVerbW));
363 
364 		// UNICODE verb
365 		for (id = 0; id < sizeof(g_VfdMenu) / sizeof(g_VfdMenu[0]); id++) {
366 			if (!lstrcmpiW(excmi->lpVerbW, g_VfdMenu[id].verbW)) {
367 				break;
368 			}
369 		}
370 	}
371 	else {
372 
373 		VFDTRACE(0, ("Command: %u\n", LOWORD(lpcmi->lpVerb)));
374 
375 		// Command ID
376 		id = LOWORD(lpcmi->lpVerb);
377 	}
378 
379 	VFDTRACE(0, ("MenuItem: %u\n", id));
380 
381 	switch (id) {
382 	case VFD_CMD_OPEN:
383 		ret = DoVfdOpen(lpcmi->hwnd);
384 
385 		if (ret == ERROR_SUCCESS) {
386 			VfdImageTip(lpcmi->hwnd, m_nDevice);
387 		}
388 		break;
389 
390 	case VFD_CMD_SAVE:
391 		ret = DoVfdSave(lpcmi->hwnd);
392 		break;
393 
394 	case VFD_CMD_CLOSE:
395 		ret = DoVfdClose(lpcmi->hwnd);
396 		break;
397 
398 	case VFD_CMD_PROTECT:
399 		ret = DoVfdProtect(lpcmi->hwnd);
400 
401 		if (ret == ERROR_SUCCESS) {
402 			VfdImageTip(lpcmi->hwnd, m_nDevice);
403 		}
404 		else if (ret == ERROR_WRITE_PROTECT) {
405 			VfdImageTip(lpcmi->hwnd, m_nDevice);
406 			ret = ERROR_SUCCESS;
407 		}
408 		break;
409 
410 	case VFD_CMD_DROP:
411 		ret = DoVfdDrop(lpcmi->hwnd);
412 
413 		if (ret == ERROR_SUCCESS) {
414 			VfdImageTip(lpcmi->hwnd, m_nDevice);
415 		}
416 		break;
417 
418 	case VFD_CMD_PROP:
419 		{
420 			SHOBJECTPROPERTIES pSHObjectProperties;
421 			WCHAR path[4] = L" :\\";
422 
423 			pSHObjectProperties = (SHOBJECTPROPERTIES)GetProcAddress(
424 				LoadLibrary("shell32"), "SHObjectProperties");
425 
426 			if (!pSHObjectProperties) {
427 				pSHObjectProperties = (SHOBJECTPROPERTIES)GetProcAddress(
428 					LoadLibrary("shell32"), (LPCSTR)SHOP_EXPORT_ORDINAL);
429 			}
430 
431 			if (pSHObjectProperties) {
432 				path[0] = m_sTarget[0];
433 
434 				pSHObjectProperties(lpcmi->hwnd,
435 					SHOP_FILEPATH, path, L"VFD");
436 			}
437 		}
438 		ret = ERROR_SUCCESS;
439 		break;
440 
441 	default:
442 		return E_INVALIDARG;
443 	}
444 
445 	if (ret != ERROR_SUCCESS &&
446 		ret != ERROR_CANCELLED) {
447 
448 		MessageBox(lpcmi->hwnd,
449 			SystemMessage(ret), VFD_MSGBOX_TITLE, MB_ICONSTOP);
450 	}
451 
452 	return NOERROR;
453 }
454 
455 //=====================================
456 //	perform VFD menu operation
457 //=====================================
458 
459 DWORD CVfdShExt::DoVfdOpen(
460 	HWND			hParent)
461 {
462 	DWORD ret = VfdGuiOpen(hParent, m_nDevice);
463 
464 	if (ret != ERROR_SUCCESS && ret != ERROR_CANCELLED) {
465 		MessageBox(hParent, SystemMessage(ret),
466 			VFD_MSGBOX_TITLE, MB_ICONSTOP);
467 	}
468 
469 	return ret;
470 }
471 
472 //
473 //	Save the VFD image
474 //
475 DWORD CVfdShExt::DoVfdSave(
476 	HWND			hParent)
477 {
478 	return VfdGuiSave(hParent, m_nDevice);
479 }
480 
481 //
482 //	Close current VFD image
483 //
484 DWORD CVfdShExt::DoVfdClose(
485 	HWND			hParent)
486 {
487 	return VfdGuiClose(hParent, m_nDevice);
488 }
489 
490 //
491 //	Enable/disable media write protection
492 //
493 DWORD CVfdShExt::DoVfdProtect(
494 	HWND			hParent)
495 {
496 	HANDLE			hDevice;
497 	DWORD			ret;
498 
499 	UNREFERENCED_PARAMETER(hParent);
500 	VFDTRACE(0, ("CVfdShExt::DoVfdProtect()\n"));
501 
502 	hDevice = VfdOpenDevice(m_nDevice);
503 
504 	if (hDevice == INVALID_HANDLE_VALUE) {
505 		return GetLastError();
506 	}
507 
508 	ret = VfdGetMediaState(hDevice);
509 
510 	if (ret == ERROR_SUCCESS) {
511 		ret = VfdWriteProtect(hDevice, TRUE);
512 	}
513 	else if (ret == ERROR_WRITE_PROTECT) {
514 		ret = VfdWriteProtect(hDevice, FALSE);
515 	}
516 
517 	if (ret == ERROR_SUCCESS) {
518 		ret = VfdGetMediaState(hDevice);
519 	}
520 
521 	CloseHandle(hDevice);
522 
523 	return ret;
524 }
525 
526 //
527 //	Open dropped file with VFD
528 //
529 DWORD CVfdShExt::DoVfdDrop(
530 	HWND			hParent)
531 {
532 	HANDLE			hDevice;
533 	DWORD			file_attr;
534 	ULONG			file_size;
535 	VFD_FILETYPE	file_type;
536 
537 	VFD_DISKTYPE	disk_type;
538 	VFD_MEDIA		media_type;
539 
540 	DWORD			ret;
541 
542 	VFDTRACE(0, ("CVfdShExt::DoVfdDropOpen()\n"));
543 
544 	//	check if dropped file is a valid image
545 
546 	ret = VfdCheckImageFile(
547 		m_sTarget, &file_attr, &file_type, &file_size);
548 
549 	if (ret != ERROR_SUCCESS) {
550 		return ret;
551 	}
552 
553 	//	check file size
554 	media_type = VfdLookupMedia(file_size);
555 
556 	if (!media_type) {
557 		PSTR msg = ModuleMessage(MSG_FILE_TOO_SMALL);
558 
559 		MessageBox(hParent, msg ? msg : "Bad size",
560 			VFD_MSGBOX_TITLE, MB_ICONSTOP);
561 
562 		if (msg) {
563 			LocalFree(msg);
564 		}
565 
566 		return ERROR_CANCELLED;
567 	}
568 
569 	if ((file_type == VFD_FILETYPE_ZIP) ||
570 		(file_attr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED))) {
571 
572 		disk_type = VFD_DISKTYPE_RAM;
573 	}
574 	else {
575 		disk_type = VFD_DISKTYPE_FILE;
576 	}
577 
578 	//	close current image (if opened)
579 
580 	ret = DoVfdClose(hParent);
581 
582 	if (ret != ERROR_SUCCESS &&
583 		ret != ERROR_NOT_READY) {
584 		return ret;
585 	}
586 
587 	//	open dropped file
588 
589 	hDevice = VfdOpenDevice(m_nDevice);
590 
591 	if (hDevice == INVALID_HANDLE_VALUE) {
592 		return GetLastError();
593 	}
594 
595 	ret = VfdOpenImage(
596 		hDevice, m_sTarget, disk_type, media_type, FALSE);
597 
598 	CloseHandle(hDevice);
599 
600 	return ret;
601 }
602