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)NULL; 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 INT_PTR 348 CALLBACK 349 SafeRemovalDlgProc( 350 HWND hwndDlg, 351 UINT uMsg, 352 WPARAM wParam, 353 LPARAM lParam) 354 { 355 PHOTPLUG_DATA pHotplugData; 356 357 pHotplugData = (PHOTPLUG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER); 358 359 switch (uMsg) 360 { 361 case WM_INITDIALOG: 362 pHotplugData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HOTPLUG_DATA)); 363 if (pHotplugData != NULL) 364 { 365 WCHAR szWindowTitle[MAX_PATH]; 366 367 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pHotplugData); 368 369 if (LoadStringW(hApplet, 370 IDS_CPLNAME, 371 szWindowTitle, 372 ARRAYSIZE(szWindowTitle))) 373 { 374 SetWindowTextW(hwndDlg, szWindowTitle); 375 } 376 377 pHotplugData->hIcon = (HICON)LoadImageW(hApplet, 378 MAKEINTRESOURCEW(IDI_HOTPLUG), 379 IMAGE_ICON, 380 GetSystemMetrics(SM_CXICON), 381 GetSystemMetrics(SM_CYICON), 382 LR_DEFAULTCOLOR); 383 pHotplugData->hIconSm = (HICON)LoadImageW(hApplet, 384 MAKEINTRESOURCEW(IDI_HOTPLUG), 385 IMAGE_ICON, 386 GetSystemMetrics(SM_CXSMICON), 387 GetSystemMetrics(SM_CYSMICON), 388 LR_DEFAULTCOLOR); 389 SendMessageW(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)pHotplugData->hIcon); 390 SendMessageW(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)pHotplugData->hIconSm); 391 392 pHotplugData->ImageListData.cbSize = sizeof(pHotplugData->ImageListData); 393 SetupDiGetClassImageList(&pHotplugData->ImageListData); 394 395 pHotplugData->hPopupMenu = LoadMenu(hApplet, MAKEINTRESOURCE(IDM_POPUP_DEVICE_TREE)); 396 397 pHotplugData->dwFlags = GetHotPlugFlags(); 398 399 if (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS) 400 Button_SetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS), BST_CHECKED); 401 402 TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 403 pHotplugData->ImageListData.ImageList, 404 TVSIL_NORMAL); 405 406 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 407 pHotplugData); 408 UpdateButtons(hwndDlg); 409 } 410 return TRUE; 411 412 case WM_COMMAND: 413 switch (LOWORD(wParam)) 414 { 415 case IDCLOSE: 416 KillTimer(hwndDlg, 1); 417 EndDialog(hwndDlg, TRUE); 418 break; 419 420 case IDC_SAFE_REMOVE_DISPLAY_COMPONENTS: 421 if (HIWORD(wParam) == BN_CLICKED) 422 { 423 if (pHotplugData != NULL) 424 { 425 if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS)) == BST_CHECKED) 426 pHotplugData->dwFlags |= HOTPLUG_DISPLAY_DEVICE_COMPONENTS; 427 else 428 pHotplugData->dwFlags &= ~HOTPLUG_DISPLAY_DEVICE_COMPONENTS; 429 430 SetHotPlugFlags(pHotplugData->dwFlags); 431 432 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 433 pHotplugData); 434 } 435 } 436 break; 437 } 438 break; 439 440 case WM_DEVICECHANGE: 441 switch (wParam) 442 { 443 case DBT_DEVNODES_CHANGED: 444 SetTimer(hwndDlg, 1, 500, NULL); 445 break; 446 } 447 break; 448 449 case WM_TIMER: 450 if (wParam == 1) 451 { 452 KillTimer(hwndDlg, 1); 453 454 if (pHotplugData != NULL) 455 { 456 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 457 pHotplugData); 458 UpdateButtons(hwndDlg); 459 } 460 } 461 break; 462 463 case WM_NOTIFY: 464 if (((LPNMHDR)lParam)->idFrom == IDC_SAFE_REMOVE_DEVICE_TREE) 465 { 466 if (((LPNMHDR)lParam)->code == NM_RCLICK) 467 { 468 if (pHotplugData != NULL) 469 { 470 ShowContextMenu(hwndDlg, 471 ((LPNMHDR)lParam)->hwndFrom, 472 pHotplugData); 473 return TRUE; 474 } 475 } 476 } 477 break; 478 479 case WM_CLOSE: 480 KillTimer(hwndDlg, 1); 481 EndDialog(hwndDlg, TRUE); 482 break; 483 484 case WM_DESTROY: 485 if (pHotplugData != NULL) 486 { 487 if (pHotplugData->hPopupMenu != NULL) 488 DestroyMenu(pHotplugData->hPopupMenu); 489 490 SetupDiDestroyClassImageList(&pHotplugData->ImageListData); 491 492 if (pHotplugData->hIconSm) 493 { 494 DestroyIcon(pHotplugData->hIconSm); 495 } 496 497 if (pHotplugData->hIcon) 498 { 499 DestroyIcon(pHotplugData->hIcon); 500 } 501 502 HeapFree(GetProcessHeap(), 0, pHotplugData); 503 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL); 504 } 505 break; 506 } 507 508 return FALSE; 509 } 510 511 512 LONG 513 APIENTRY 514 InitApplet( 515 HWND hwnd, 516 UINT uMsg, 517 LPARAM wParam, 518 LPARAM lParam) 519 { 520 DPRINT("InitApplet()\n"); 521 522 DialogBox(hApplet, 523 MAKEINTRESOURCE(IDD_SAFE_REMOVE_HARDWARE_DIALOG), 524 hwnd, 525 SafeRemovalDlgProc); 526 527 // TODO 528 return TRUE; 529 } 530 531 532 LONG 533 CALLBACK 534 CPlApplet( 535 HWND hwndCPl, 536 UINT uMsg, 537 LPARAM lParam1, 538 LPARAM lParam2) 539 { 540 UINT i = (UINT)lParam1; 541 542 switch(uMsg) 543 { 544 case CPL_INIT: 545 return TRUE; 546 547 case CPL_GETCOUNT: 548 return NUM_APPLETS; 549 550 case CPL_INQUIRE: 551 if (i < NUM_APPLETS) 552 { 553 CPLINFO *CPlInfo = (CPLINFO*)lParam2; 554 CPlInfo->lData = 0; 555 CPlInfo->idIcon = Applets[i].idIcon; 556 CPlInfo->idName = Applets[i].idName; 557 CPlInfo->idInfo = Applets[i].idDescription; 558 } 559 else 560 { 561 return TRUE; 562 } 563 break; 564 565 case CPL_DBLCLK: 566 if (i < NUM_APPLETS) 567 Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2); 568 else 569 return TRUE; 570 break; 571 572 case CPL_STARTWPARMSW: 573 if (i < NUM_APPLETS) 574 return Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2); 575 break; 576 } 577 return FALSE; 578 } 579 580 581 INT 582 WINAPI 583 DllMain( 584 HINSTANCE hinstDLL, 585 DWORD dwReason, 586 LPVOID lpvReserved) 587 { 588 UNREFERENCED_PARAMETER(lpvReserved); 589 590 switch (dwReason) 591 { 592 case DLL_PROCESS_ATTACH: 593 case DLL_THREAD_ATTACH: 594 hApplet = hinstDLL; 595 break; 596 } 597 return TRUE; 598 } 599