Reactos
at master 606 lines 13 kB view raw
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 32typedef 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// 44enum { 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 54static 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} 65g_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// 77static 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// 106STDMETHODIMP 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 269STDMETHODIMP 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 332STDMETHODIMP 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 464DWORD 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// 480DWORD CVfdShExt::DoVfdSave( 481 HWND hParent) 482{ 483 return VfdGuiSave(hParent, m_nDevice); 484} 485 486// 487// Close current VFD image 488// 489DWORD CVfdShExt::DoVfdClose( 490 HWND hParent) 491{ 492 return VfdGuiClose(hParent, m_nDevice); 493} 494 495// 496// Enable/disable media write protection 497// 498DWORD 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// 534DWORD 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}