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