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 #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 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 // 479 DWORD CVfdShExt::DoVfdSave( 480 HWND hParent) 481 { 482 return VfdGuiSave(hParent, m_nDevice); 483 } 484 485 // 486 // Close current VFD image 487 // 488 DWORD CVfdShExt::DoVfdClose( 489 HWND hParent) 490 { 491 return VfdGuiClose(hParent, m_nDevice); 492 } 493 494 // 495 // Enable/disable media write protection 496 // 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 // 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