4 Virtual Floppy Drive for Windows
6 COM shell extension class context menu functions
8 Copyright (c) 2003-2005 Ken Kato
11 #define WIN32_LEAN_AND_MEAN
22 #include "vfdmsg_lib.h"
29 // Undocumented windows API to handle shell property sheets
32 typedef BOOL (WINAPI
*SHOBJECTPROPERTIES
)(
33 HWND hwnd
, DWORD dwType
, LPCWSTR lpObject
, LPCWSTR lpPage
);
36 #define SHOP_FILEPATH 0x00000002
39 #define SHOP_EXPORT_ORDINAL 178
54 static struct _vfd_menu
{
55 UINT textid
; // menu item text id
56 UINT helpid
; // menu item help id
58 PCHAR verbA
; // ansi verb text
59 PWCHAR verbW
; // unicode verb text
61 LPCSTR verbA
; // ansi verb text
62 LPCWSTR verbW
; // unicode verb text
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" },
77 static void AddMenuItem(
84 PSTR text
= ModuleMessage(uText
);
87 InsertMenu(hMenu
, uPos
, uFlags
, uCmd
, text
);
94 // FUNCTION: CVfdShExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
96 // PURPOSE: Called by the shell just before the context menu is displayed.
97 // This is where you add your specific menu items.
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
106 STDMETHODIMP
CVfdShExt::QueryContextMenu(
113 UNREFERENCED_PARAMETER(idCmdLast
);
114 VFDTRACE(0, ("CVfdShExt::QueryContextMenu()\n"));
117 // Check if menu items should be added
119 if ((CMF_DEFAULTONLY
& uFlags
) ||
120 !m_pDataObj
|| m_nDevice
== (ULONG
)-1) {
122 VFDTRACE(0, ("Don't add any items.\n"));
123 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, 0);
127 // Drag & Drop handler?
131 VFDTRACE(0, ("Invoked as the Drop handler.\n"));
133 if (GetFileAttributes(m_sTarget
) & FILE_ATTRIBUTE_DIRECTORY
) {
135 // if the dropped item is a directory, nothing to do here
136 VFDTRACE(0, ("Dropped object is a directory.\n"));
138 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, 0);
141 // Add a drop context menu item
145 MF_BYPOSITION
| MF_STRING
,
146 idCmdFirst
+ VFD_CMD_DROP
,
147 g_VfdMenu
[VFD_CMD_DROP
].textid
);
149 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, VFD_CMD_DROP
+ 1);
153 // Context menu handler
155 VFDTRACE(0, ("Invoked as the context menu handler.\n"));
158 // Get the VFD media state
160 HANDLE hDevice
= VfdOpenDevice(m_nDevice
);
162 if (hDevice
== INVALID_HANDLE_VALUE
) {
163 VFDTRACE(0, ("device open failed.\n"));
164 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, 0);
167 DWORD status
= VfdGetMediaState(hDevice
);
169 CloseHandle(hDevice
);
172 // Add context menu items
175 InsertMenu(hMenu
, indexMenu
++,
176 MF_BYPOSITION
| MF_SEPARATOR
, 0, NULL
);
178 if (status
== ERROR_SUCCESS
||
179 status
== ERROR_WRITE_PROTECT
) {
181 // An image is opened
183 // insert the "save" menu item
188 MF_BYPOSITION
| MF_STRING
,
189 idCmdFirst
+ VFD_CMD_SAVE
,
190 g_VfdMenu
[VFD_CMD_SAVE
].textid
);
192 // insert the "close" menu item
197 MF_BYPOSITION
| MF_STRING
,
198 idCmdFirst
+ VFD_CMD_CLOSE
,
199 g_VfdMenu
[VFD_CMD_CLOSE
].textid
);
201 // insert the "protect" menu item
206 MF_BYPOSITION
| MF_STRING
,
207 idCmdFirst
+ VFD_CMD_PROTECT
,
208 g_VfdMenu
[VFD_CMD_PROTECT
].textid
);
210 // check "protect" menu item
212 if (status
== ERROR_WRITE_PROTECT
) {
213 CheckMenuItem(hMenu
, indexMenu
- 1,
214 MF_BYPOSITION
| MF_CHECKED
);
218 // The drive is empty
220 // insert the "open" menu item
225 MF_BYPOSITION
| MF_STRING
,
226 idCmdFirst
+ VFD_CMD_OPEN
,
227 g_VfdMenu
[VFD_CMD_OPEN
].textid
);
230 // Insert the "proterty" menu item
235 MF_BYPOSITION
| MF_STRING
,
236 idCmdFirst
+ VFD_CMD_PROP
,
237 g_VfdMenu
[VFD_CMD_PROP
].textid
);
239 // Insert a separator
241 InsertMenu(hMenu
, indexMenu
,
242 MF_BYPOSITION
| MF_SEPARATOR
, 0, NULL
);
244 return MAKE_HRESULT(SEVERITY_SUCCESS
, 0, VFD_CMD_PROP
+ 1);
248 // FUNCTION: CVfdShExt::GetCommandString(LPCMINVOKECOMMANDINFO)
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.
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.
269 STDMETHODIMP
CVfdShExt::GetCommandString(
277 ("CVfdShExt::GetCommandString(%u,...)\n", idCmd
));
279 UNREFERENCED_PARAMETER(reserved
);
281 if (idCmd
>= sizeof(g_VfdMenu
) / sizeof(g_VfdMenu
[0])) {
288 FORMAT_MESSAGE_FROM_HMODULE
|
289 FORMAT_MESSAGE_IGNORE_INSERTS
,
290 g_hDllModule
, g_VfdMenu
[idCmd
].helpid
,
291 0, pszName
, cchMax
, NULL
);
293 VFDTRACE(0, ("HELPTEXTA: %s\n", pszName
));
298 FORMAT_MESSAGE_FROM_HMODULE
|
299 FORMAT_MESSAGE_IGNORE_INSERTS
,
300 g_hDllModule
, g_VfdMenu
[idCmd
].helpid
,
301 0, (LPWSTR
)pszName
, cchMax
, NULL
);
303 VFDTRACE(0, ("HELPTEXTW: %ws\n", pszName
));
307 lstrcpynA(pszName
, g_VfdMenu
[idCmd
].verbA
, cchMax
);
311 lstrcpynW((LPWSTR
)pszName
, g_VfdMenu
[idCmd
].verbW
, cchMax
);
319 // FUNCTION: CVfdShExt::InvokeCommand(LPCMINVOKECOMMANDINFO)
321 // PURPOSE: Called by the shell after the user has selected on of the
322 // menu items that was added in QueryContextMenu().
325 // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
328 STDMETHODIMP
CVfdShExt::InvokeCommand(
329 LPCMINVOKECOMMANDINFO lpcmi
)
331 VFDTRACE(0, ("CVfdShExt::InvokeCommand()\n"));
333 BOOL unicode
= FALSE
;
336 CMINVOKECOMMANDINFOEX
*excmi
= (CMINVOKECOMMANDINFOEX
*)lpcmi
;
338 if (lpcmi
->cbSize
>= sizeof(CMINVOKECOMMANDINFOEX
) &&
339 (lpcmi
->fMask
& CMIC_MASK_UNICODE
)) {
345 if (!unicode
&& HIWORD(lpcmi
->lpVerb
)) {
347 VFDTRACE(0, ("ANSI: %s\n", lpcmi
->lpVerb
));
350 for (id
= 0; id
< sizeof(g_VfdMenu
) / sizeof(g_VfdMenu
[0]); id
++) {
351 if (!lstrcmpi(lpcmi
->lpVerb
, g_VfdMenu
[id
].verbA
)) {
356 else if (unicode
&& HIWORD(excmi
->lpVerbW
)) {
358 VFDTRACE(0, ("UNICODE: %ws\n", excmi
->lpVerbW
));
361 for (id
= 0; id
< sizeof(g_VfdMenu
) / sizeof(g_VfdMenu
[0]); id
++) {
362 if (!lstrcmpiW(excmi
->lpVerbW
, g_VfdMenu
[id
].verbW
)) {
369 VFDTRACE(0, ("Command: %u\n", LOWORD(lpcmi
->lpVerb
)));
372 id
= LOWORD(lpcmi
->lpVerb
);
375 VFDTRACE(0, ("MenuItem: %u\n", id
));
379 ret
= DoVfdOpen(lpcmi
->hwnd
);
381 if (ret
== ERROR_SUCCESS
) {
382 VfdImageTip(lpcmi
->hwnd
, m_nDevice
);
387 ret
= DoVfdSave(lpcmi
->hwnd
);
391 ret
= DoVfdClose(lpcmi
->hwnd
);
394 case VFD_CMD_PROTECT
:
395 ret
= DoVfdProtect(lpcmi
->hwnd
);
397 if (ret
== ERROR_SUCCESS
) {
398 VfdImageTip(lpcmi
->hwnd
, m_nDevice
);
400 else if (ret
== ERROR_WRITE_PROTECT
) {
401 VfdImageTip(lpcmi
->hwnd
, m_nDevice
);
407 ret
= DoVfdDrop(lpcmi
->hwnd
);
409 if (ret
== ERROR_SUCCESS
) {
410 VfdImageTip(lpcmi
->hwnd
, m_nDevice
);
416 SHOBJECTPROPERTIES pSHObjectProperties
;
417 WCHAR path
[4] = L
" :\\";
419 pSHObjectProperties
= (SHOBJECTPROPERTIES
)GetProcAddress(
420 LoadLibrary("shell32"), "SHObjectProperties");
422 if (!pSHObjectProperties
) {
423 pSHObjectProperties
= (SHOBJECTPROPERTIES
)GetProcAddress(
424 LoadLibrary("shell32"), (LPCSTR
)SHOP_EXPORT_ORDINAL
);
427 if (pSHObjectProperties
) {
428 path
[0] = m_sTarget
[0];
430 pSHObjectProperties(lpcmi
->hwnd
,
431 SHOP_FILEPATH
, path
, L
"VFD");
441 if (ret
!= ERROR_SUCCESS
&&
442 ret
!= ERROR_CANCELLED
) {
444 MessageBox(lpcmi
->hwnd
,
445 SystemMessage(ret
), VFD_MSGBOX_TITLE
, MB_ICONSTOP
);
451 //=====================================
452 // perform VFD menu operation
453 //=====================================
455 DWORD
CVfdShExt::DoVfdOpen(
458 DWORD ret
= VfdGuiOpen(hParent
, m_nDevice
);
460 if (ret
!= ERROR_SUCCESS
&& ret
!= ERROR_CANCELLED
) {
461 MessageBox(hParent
, SystemMessage(ret
),
462 VFD_MSGBOX_TITLE
, MB_ICONSTOP
);
469 // Save the VFD image
471 DWORD
CVfdShExt::DoVfdSave(
474 return VfdGuiSave(hParent
, m_nDevice
);
478 // Close current VFD image
480 DWORD
CVfdShExt::DoVfdClose(
483 return VfdGuiClose(hParent
, m_nDevice
);
487 // Enable/disable media write protection
489 DWORD
CVfdShExt::DoVfdProtect(
495 UNREFERENCED_PARAMETER(hParent
);
496 VFDTRACE(0, ("CVfdShExt::DoVfdProtect()\n"));
498 hDevice
= VfdOpenDevice(m_nDevice
);
500 if (hDevice
== INVALID_HANDLE_VALUE
) {
501 return GetLastError();
504 ret
= VfdGetMediaState(hDevice
);
506 if (ret
== ERROR_SUCCESS
) {
507 ret
= VfdWriteProtect(hDevice
, TRUE
);
509 else if (ret
== ERROR_WRITE_PROTECT
) {
510 ret
= VfdWriteProtect(hDevice
, FALSE
);
513 if (ret
== ERROR_SUCCESS
) {
514 ret
= VfdGetMediaState(hDevice
);
517 CloseHandle(hDevice
);
523 // Open dropped file with VFD
525 DWORD
CVfdShExt::DoVfdDrop(
531 VFD_FILETYPE file_type
;
533 VFD_DISKTYPE disk_type
;
534 VFD_MEDIA media_type
;
538 VFDTRACE(0, ("CVfdShExt::DoVfdDropOpen()\n"));
540 // check if dropped file is a valid image
542 ret
= VfdCheckImageFile(
543 m_sTarget
, &file_attr
, &file_type
, &file_size
);
545 if (ret
!= ERROR_SUCCESS
) {
550 media_type
= VfdLookupMedia(file_size
);
553 PSTR msg
= ModuleMessage(MSG_FILE_TOO_SMALL
);
555 MessageBox(hParent
, msg
? msg
: "Bad size",
556 VFD_MSGBOX_TITLE
, MB_ICONSTOP
);
562 return ERROR_CANCELLED
;
565 if ((file_type
== VFD_FILETYPE_ZIP
) ||
566 (file_attr
& (FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_COMPRESSED
| FILE_ATTRIBUTE_ENCRYPTED
))) {
568 disk_type
= VFD_DISKTYPE_RAM
;
571 disk_type
= VFD_DISKTYPE_FILE
;
574 // close current image (if opened)
576 ret
= DoVfdClose(hParent
);
578 if (ret
!= ERROR_SUCCESS
&&
579 ret
!= ERROR_NOT_READY
) {
585 hDevice
= VfdOpenDevice(m_nDevice
);
587 if (hDevice
== INVALID_HANDLE_VALUE
) {
588 return GetLastError();
592 hDevice
, m_sTarget
, disk_type
, media_type
, FALSE
);
594 CloseHandle(hDevice
);