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