1 /* 2 * PROJECT: Safely Remove Hardware Applet 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/cpl/hotplug/hotplug.c 5 * PURPOSE: applet initialization 6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org) 7 */ 8 9 #include "hotplug.h" 10 11 #include <initguid.h> 12 #include <devguid.h> 13 14 #define NDEBUG 15 #include <debug.h> 16 17 18 typedef struct _HOTPLUG_DATA 19 { 20 HICON hIcon; 21 HICON hIconSm; 22 SP_CLASSIMAGELIST_DATA ImageListData; 23 HMENU hPopupMenu; 24 DWORD dwFlags; 25 } HOTPLUG_DATA, *PHOTPLUG_DATA; 26 27 28 // globals 29 HINSTANCE hApplet = 0; 30 31 /* Applets */ 32 APPLET Applets[NUM_APPLETS] = 33 { 34 {IDI_HOTPLUG, IDS_CPLNAME, IDS_CPLDESCRIPTION, InitApplet} 35 }; 36 37 static 38 DWORD 39 GetHotPlugFlags(VOID) 40 { 41 HKEY hKey = NULL; 42 DWORD dwFlags = 0; 43 DWORD dwSize, dwError; 44 45 dwError = RegOpenKeyExW(HKEY_CURRENT_USER, 46 REGSTR_PATH_SYSTRAY, 47 0, 48 KEY_READ, 49 &hKey); 50 if (dwError != ERROR_SUCCESS) 51 goto done; 52 53 dwSize = sizeof(dwFlags); 54 dwError = RegQueryValueExW(hKey, 55 L"HotPlugFlags", 56 NULL, 57 NULL, 58 (LPBYTE)&dwFlags, 59 &dwSize); 60 if (dwError != ERROR_SUCCESS) 61 dwFlags = 0; 62 63 done: 64 if (hKey != NULL) 65 RegCloseKey(hKey); 66 67 return dwFlags; 68 } 69 70 71 static 72 DWORD 73 SetHotPlugFlags( 74 _In_ DWORD dwFlags) 75 { 76 HKEY hKey = NULL; 77 DWORD dwError; 78 79 dwError = RegCreateKeyExW(HKEY_CURRENT_USER, 80 REGSTR_PATH_SYSTRAY, 81 0, 82 NULL, 83 REG_OPTION_NON_VOLATILE, 84 KEY_WRITE, 85 NULL, 86 &hKey, 87 NULL); 88 if (dwError != ERROR_SUCCESS) 89 goto done; 90 91 dwError = RegSetValueExW(hKey, 92 L"HotPlugFlags", 93 0, 94 REG_DWORD, 95 (LPBYTE)&dwFlags, 96 sizeof(dwFlags)); 97 98 done: 99 if (hKey != NULL) 100 RegCloseKey(hKey); 101 102 return dwError; 103 } 104 105 106 static 107 HTREEITEM 108 InsertDeviceTreeItem( 109 _In_ HWND hwndDeviceTree, 110 _In_ HTREEITEM hParent, 111 _In_ DWORD DevInst, 112 _In_ PHOTPLUG_DATA pHotplugData) 113 { 114 WCHAR szDisplayName[40]; 115 WCHAR szGuidString[MAX_GUID_STRING_LEN]; 116 TVINSERTSTRUCTW tvItem; 117 GUID ClassGuid; 118 INT nClassImage; 119 DWORD dwSize; 120 CONFIGRET cr; 121 122 /* Get the device description */ 123 dwSize = sizeof(szDisplayName); 124 cr = CM_Get_DevNode_Registry_Property(DevInst, 125 CM_DRP_DEVICEDESC, 126 NULL, 127 szDisplayName, 128 &dwSize, 129 0); 130 if (cr != CR_SUCCESS) 131 wcscpy(szDisplayName, L"Unknown Device"); 132 133 /* Get the class GUID */ 134 dwSize = sizeof(szGuidString); 135 cr = CM_Get_DevNode_Registry_Property(DevInst, 136 CM_DRP_CLASSGUID, 137 NULL, 138 szGuidString, 139 &dwSize, 140 0); 141 if (cr == CR_SUCCESS) 142 { 143 pSetupGuidFromString(szGuidString, &ClassGuid); 144 } 145 else 146 { 147 memcpy(&ClassGuid, &GUID_DEVCLASS_UNKNOWN, sizeof(GUID)); 148 } 149 150 /* Get the image for the class this device is in */ 151 SetupDiGetClassImageIndex(&pHotplugData->ImageListData, 152 &ClassGuid, 153 &nClassImage); 154 155 /* Add it to the device tree */ 156 ZeroMemory(&tvItem, sizeof(tvItem)); 157 tvItem.hParent = hParent; 158 tvItem.hInsertAfter = TVI_LAST; 159 160 tvItem.item.mask = TVIF_STATE | TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 161 tvItem.item.state = TVIS_EXPANDED; 162 tvItem.item.stateMask = TVIS_EXPANDED; 163 tvItem.item.pszText = szDisplayName; 164 tvItem.item.iImage = nClassImage; 165 tvItem.item.iSelectedImage = nClassImage; 166 tvItem.item.lParam = (LPARAM)DevInst; 167 168 return TreeView_InsertItem(hwndDeviceTree, &tvItem); 169 } 170 171 172 static 173 VOID 174 RecursiveInsertSubDevices( 175 _In_ HWND hwndDeviceTree, 176 _In_ HTREEITEM hParentItem, 177 _In_ DWORD ParentDevInst, 178 _In_ PHOTPLUG_DATA pHotplugData) 179 { 180 HTREEITEM hTreeItem; 181 DEVINST ChildDevInst; 182 CONFIGRET cr; 183 184 DPRINT("RecursiveInsertSubDevices()\n"); 185 186 cr = CM_Get_Child(&ChildDevInst, ParentDevInst, 0); 187 if (cr != CR_SUCCESS) 188 { 189 DPRINT("No child! %lu\n", cr); 190 return; 191 } 192 193 hTreeItem = InsertDeviceTreeItem(hwndDeviceTree, 194 hParentItem, 195 ChildDevInst, 196 pHotplugData); 197 if (hTreeItem != NULL) 198 { 199 RecursiveInsertSubDevices(hwndDeviceTree, 200 hTreeItem, 201 ChildDevInst, 202 pHotplugData); 203 } 204 205 for (;;) 206 { 207 cr = CM_Get_Sibling(&ChildDevInst, ChildDevInst, 0); 208 if (cr != CR_SUCCESS) 209 { 210 DPRINT("No sibling! %lu\n", cr); 211 return; 212 } 213 214 hTreeItem = InsertDeviceTreeItem(hwndDeviceTree, 215 hParentItem, 216 ChildDevInst, 217 pHotplugData); 218 if (hTreeItem != NULL) 219 { 220 RecursiveInsertSubDevices(hwndDeviceTree, 221 hTreeItem, 222 ChildDevInst, 223 pHotplugData); 224 } 225 } 226 } 227 228 229 static 230 VOID 231 EnumHotpluggedDevices( 232 HWND hwndDeviceTree, 233 PHOTPLUG_DATA pHotplugData) 234 { 235 SP_DEVINFO_DATA did = { 0 }; 236 HDEVINFO hdev; 237 int idev; 238 DWORD dwCapabilities, dwSize; 239 ULONG ulStatus, ulProblem; 240 HTREEITEM hTreeItem; 241 CONFIGRET cr; 242 243 DPRINT1("EnumHotpluggedDevices()\n"); 244 245 TreeView_DeleteAllItems(hwndDeviceTree); 246 247 hdev = SetupDiGetClassDevs(NULL, NULL, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT); 248 if (hdev == INVALID_HANDLE_VALUE) 249 return; 250 251 did.cbSize = sizeof(did); 252 253 /* Enumerate all the attached devices */ 254 for (idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); idev++) 255 { 256 ulStatus = 0; 257 ulProblem = 0; 258 259 cr = CM_Get_DevNode_Status(&ulStatus, 260 &ulProblem, 261 did.DevInst, 262 0); 263 if (cr != CR_SUCCESS) 264 continue; 265 266 dwCapabilities = 0, 267 dwSize = sizeof(dwCapabilities); 268 cr = CM_Get_DevNode_Registry_Property(did.DevInst, 269 CM_DRP_CAPABILITIES, 270 NULL, 271 &dwCapabilities, 272 &dwSize, 273 0); 274 if (cr != CR_SUCCESS) 275 continue; 276 277 /* Add devices that require safe removal to the device tree */ 278 if ( (dwCapabilities & CM_DEVCAP_REMOVABLE) && 279 !(dwCapabilities & CM_DEVCAP_DOCKDEVICE) && 280 !(dwCapabilities & CM_DEVCAP_SURPRISEREMOVALOK) && 281 ((dwCapabilities & CM_DEVCAP_EJECTSUPPORTED) || (ulStatus & DN_DISABLEABLE)) && 282 ulProblem == 0) 283 { 284 hTreeItem = InsertDeviceTreeItem(hwndDeviceTree, 285 TVI_ROOT, 286 did.DevInst, 287 pHotplugData); 288 289 if ((hTreeItem != NULL) && (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS)) 290 { 291 RecursiveInsertSubDevices(hwndDeviceTree, 292 hTreeItem, 293 did.DevInst, 294 pHotplugData); 295 } 296 } 297 } 298 299 SetupDiDestroyDeviceInfoList(hdev); 300 } 301 302 303 static 304 VOID 305 UpdateButtons( 306 HWND hwndDlg) 307 { 308 BOOL bEnabled; 309 310 bEnabled = (TreeView_GetCount(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE)) != 0); 311 312 EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_PROPERTIES), bEnabled); 313 EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_STOP), bEnabled); 314 } 315 316 317 static 318 VOID 319 ShowContextMenu( 320 HWND hwndDlg, 321 HWND hwndTreeView, 322 PHOTPLUG_DATA pHotplugData) 323 { 324 HTREEITEM hTreeItem; 325 RECT rc; 326 POINT pt; 327 328 hTreeItem = TreeView_GetSelection(hwndTreeView); 329 if (hTreeItem == NULL) 330 return; 331 332 TreeView_GetItemRect(hwndTreeView, hTreeItem, &rc, TRUE); 333 334 pt.x = (rc.left + rc.right) / 2; 335 pt.y = (rc.top + rc.bottom) / 2; 336 ClientToScreen(hwndTreeView, &pt); 337 TrackPopupMenu(GetSubMenu(pHotplugData->hPopupMenu, 0), 338 TPM_LEFTALIGN | TPM_TOPALIGN, 339 pt.x, 340 pt.y, 341 0, 342 hwndDlg, 343 NULL); 344 } 345 346 347 static 348 DEVINST 349 GetSelectedDeviceInst( 350 _In_ HWND hwndDeviceTree) 351 { 352 HTREEITEM hTreeItem; 353 TVITEMW item; 354 355 hTreeItem = TreeView_GetSelection(hwndDeviceTree); 356 if (hTreeItem == NULL) 357 return 0; 358 359 ZeroMemory(&item, sizeof(item)); 360 item.mask = TVIF_PARAM; 361 item.hItem = hTreeItem; 362 363 TreeView_GetItem(hwndDeviceTree, &item); 364 365 return item.lParam; 366 } 367 368 static 369 VOID 370 ShowDeviceProperties( 371 _In_ HWND hwndParent, 372 _In_ DEVINST DevInst) 373 { 374 ULONG ulSize; 375 CONFIGRET cr; 376 LPWSTR pszDevId; 377 378 cr = CM_Get_Device_ID_Size(&ulSize, DevInst, 0); 379 if (cr != CR_SUCCESS || ulSize == 0) 380 return; 381 382 /* Take the terminating NULL into account */ 383 ulSize++; 384 385 pszDevId = HeapAlloc(GetProcessHeap(), 0, ulSize * sizeof(WCHAR)); 386 if (pszDevId == NULL) 387 return; 388 389 cr = CM_Get_Device_IDW(DevInst, pszDevId, ulSize, 0); 390 if (cr == CR_SUCCESS) 391 { 392 typedef int (WINAPI *PFDEVICEPROPERTIESW)(HWND, LPCWSTR, LPCWSTR, BOOL); 393 HMODULE hDevMgrDll; 394 PFDEVICEPROPERTIESW pDevicePropertiesW; 395 396 hDevMgrDll = LoadLibraryW(L"devmgr.dll"); 397 if (hDevMgrDll != NULL) 398 { 399 pDevicePropertiesW = (PFDEVICEPROPERTIESW)GetProcAddress(hDevMgrDll, "DevicePropertiesW"); 400 if (pDevicePropertiesW != NULL) 401 pDevicePropertiesW(hwndParent, NULL, pszDevId, FALSE); 402 403 FreeLibrary(hDevMgrDll); 404 } 405 } 406 407 HeapFree(GetProcessHeap(), 0, pszDevId); 408 } 409 410 INT_PTR 411 CALLBACK 412 SafeRemovalDlgProc( 413 HWND hwndDlg, 414 UINT uMsg, 415 WPARAM wParam, 416 LPARAM lParam) 417 { 418 PHOTPLUG_DATA pHotplugData; 419 420 pHotplugData = (PHOTPLUG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER); 421 422 switch (uMsg) 423 { 424 case WM_INITDIALOG: 425 pHotplugData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HOTPLUG_DATA)); 426 if (pHotplugData != NULL) 427 { 428 WCHAR szWindowTitle[MAX_PATH]; 429 430 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pHotplugData); 431 432 if (LoadStringW(hApplet, 433 IDS_CPLNAME, 434 szWindowTitle, 435 ARRAYSIZE(szWindowTitle))) 436 { 437 SetWindowTextW(hwndDlg, szWindowTitle); 438 } 439 440 pHotplugData->hIcon = (HICON)LoadImageW(hApplet, 441 MAKEINTRESOURCEW(IDI_HOTPLUG), 442 IMAGE_ICON, 443 GetSystemMetrics(SM_CXICON), 444 GetSystemMetrics(SM_CYICON), 445 LR_DEFAULTCOLOR); 446 pHotplugData->hIconSm = (HICON)LoadImageW(hApplet, 447 MAKEINTRESOURCEW(IDI_HOTPLUG), 448 IMAGE_ICON, 449 GetSystemMetrics(SM_CXSMICON), 450 GetSystemMetrics(SM_CYSMICON), 451 LR_DEFAULTCOLOR); 452 SendMessageW(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)pHotplugData->hIcon); 453 SendMessageW(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)pHotplugData->hIconSm); 454 455 pHotplugData->ImageListData.cbSize = sizeof(pHotplugData->ImageListData); 456 SetupDiGetClassImageList(&pHotplugData->ImageListData); 457 458 pHotplugData->hPopupMenu = LoadMenu(hApplet, MAKEINTRESOURCE(IDM_POPUP_DEVICE_TREE)); 459 460 pHotplugData->dwFlags = GetHotPlugFlags(); 461 462 if (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS) 463 Button_SetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS), BST_CHECKED); 464 465 TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 466 pHotplugData->ImageListData.ImageList, 467 TVSIL_NORMAL); 468 469 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 470 pHotplugData); 471 UpdateButtons(hwndDlg); 472 } 473 return TRUE; 474 475 case WM_COMMAND: 476 switch (LOWORD(wParam)) 477 { 478 case IDCLOSE: 479 KillTimer(hwndDlg, 1); 480 EndDialog(hwndDlg, TRUE); 481 break; 482 483 case IDC_SAFE_REMOVE_DISPLAY_COMPONENTS: 484 if (HIWORD(wParam) == BN_CLICKED) 485 { 486 if (pHotplugData != NULL) 487 { 488 if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS)) == BST_CHECKED) 489 pHotplugData->dwFlags |= HOTPLUG_DISPLAY_DEVICE_COMPONENTS; 490 else 491 pHotplugData->dwFlags &= ~HOTPLUG_DISPLAY_DEVICE_COMPONENTS; 492 493 SetHotPlugFlags(pHotplugData->dwFlags); 494 495 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 496 pHotplugData); 497 } 498 } 499 break; 500 501 case IDC_SAFE_REMOVE_PROPERTIES: 502 case IDM_PROPERTIES: 503 { 504 HWND hwndDevTree = GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE); 505 ShowDeviceProperties(hwndDlg, GetSelectedDeviceInst(hwndDevTree)); 506 break; 507 } 508 } 509 break; 510 511 case WM_DEVICECHANGE: 512 switch (wParam) 513 { 514 case DBT_DEVNODES_CHANGED: 515 SetTimer(hwndDlg, 1, 500, NULL); 516 break; 517 } 518 break; 519 520 case WM_TIMER: 521 if (wParam == 1) 522 { 523 KillTimer(hwndDlg, 1); 524 525 if (pHotplugData != NULL) 526 { 527 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 528 pHotplugData); 529 UpdateButtons(hwndDlg); 530 } 531 } 532 break; 533 534 case WM_NOTIFY: 535 if (((LPNMHDR)lParam)->idFrom == IDC_SAFE_REMOVE_DEVICE_TREE) 536 { 537 if (((LPNMHDR)lParam)->code == NM_RCLICK) 538 { 539 if (pHotplugData != NULL) 540 { 541 ShowContextMenu(hwndDlg, 542 ((LPNMHDR)lParam)->hwndFrom, 543 pHotplugData); 544 return TRUE; 545 } 546 } 547 } 548 break; 549 550 case WM_CLOSE: 551 KillTimer(hwndDlg, 1); 552 EndDialog(hwndDlg, TRUE); 553 break; 554 555 case WM_DESTROY: 556 if (pHotplugData != NULL) 557 { 558 if (pHotplugData->hPopupMenu != NULL) 559 DestroyMenu(pHotplugData->hPopupMenu); 560 561 SetupDiDestroyClassImageList(&pHotplugData->ImageListData); 562 563 if (pHotplugData->hIconSm) 564 { 565 DestroyIcon(pHotplugData->hIconSm); 566 } 567 568 if (pHotplugData->hIcon) 569 { 570 DestroyIcon(pHotplugData->hIcon); 571 } 572 573 HeapFree(GetProcessHeap(), 0, pHotplugData); 574 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL); 575 } 576 break; 577 } 578 579 return FALSE; 580 } 581 582 583 LONG 584 APIENTRY 585 InitApplet( 586 HWND hwnd, 587 UINT uMsg, 588 LPARAM wParam, 589 LPARAM lParam) 590 { 591 DPRINT("InitApplet()\n"); 592 593 DialogBox(hApplet, 594 MAKEINTRESOURCE(IDD_SAFE_REMOVE_HARDWARE_DIALOG), 595 hwnd, 596 SafeRemovalDlgProc); 597 598 // TODO 599 return TRUE; 600 } 601 602 603 LONG 604 CALLBACK 605 CPlApplet( 606 HWND hwndCPl, 607 UINT uMsg, 608 LPARAM lParam1, 609 LPARAM lParam2) 610 { 611 UINT i = (UINT)lParam1; 612 613 switch(uMsg) 614 { 615 case CPL_INIT: 616 return TRUE; 617 618 case CPL_GETCOUNT: 619 return NUM_APPLETS; 620 621 case CPL_INQUIRE: 622 if (i < NUM_APPLETS) 623 { 624 CPLINFO *CPlInfo = (CPLINFO*)lParam2; 625 CPlInfo->lData = 0; 626 CPlInfo->idIcon = Applets[i].idIcon; 627 CPlInfo->idName = Applets[i].idName; 628 CPlInfo->idInfo = Applets[i].idDescription; 629 } 630 else 631 { 632 return TRUE; 633 } 634 break; 635 636 case CPL_DBLCLK: 637 if (i < NUM_APPLETS) 638 Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2); 639 else 640 return TRUE; 641 break; 642 643 case CPL_STARTWPARMSW: 644 if (i < NUM_APPLETS) 645 return Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2); 646 break; 647 } 648 return FALSE; 649 } 650 651 652 INT 653 WINAPI 654 DllMain( 655 HINSTANCE hinstDLL, 656 DWORD dwReason, 657 LPVOID lpvReserved) 658 { 659 UNREFERENCED_PARAMETER(lpvReserved); 660 661 switch (dwReason) 662 { 663 case DLL_PROCESS_ATTACH: 664 case DLL_THREAD_ATTACH: 665 hApplet = hinstDLL; 666 break; 667 } 668 return TRUE; 669 } 670