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 SP_CLASSIMAGELIST_DATA ImageListData; 21 HMENU hPopupMenu; 22 DWORD dwFlags; 23 } HOTPLUG_DATA, *PHOTPLUG_DATA; 24 25 26 // globals 27 HINSTANCE hApplet = 0; 28 29 /* Applets */ 30 APPLET Applets[NUM_APPLETS] = 31 { 32 {IDI_HOTPLUG, IDS_CPLNAME, IDS_CPLDESCRIPTION, InitApplet} 33 }; 34 35 static 36 DWORD 37 GetHotPlugFlags(VOID) 38 { 39 HKEY hKey = NULL; 40 DWORD dwFlags = 0; 41 DWORD dwSize, dwError; 42 43 dwError = RegOpenKeyExW(HKEY_CURRENT_USER, 44 REGSTR_PATH_SYSTRAY, 45 0, 46 KEY_READ, 47 &hKey); 48 if (dwError != ERROR_SUCCESS) 49 goto done; 50 51 dwSize = sizeof(dwFlags); 52 dwError = RegQueryValueExW(hKey, 53 L"HotPlugFlags", 54 NULL, 55 NULL, 56 (LPBYTE)&dwFlags, 57 &dwSize); 58 if (dwError != ERROR_SUCCESS) 59 dwFlags = 0; 60 61 done: 62 if (hKey != NULL) 63 RegCloseKey(hKey); 64 65 return dwFlags; 66 } 67 68 69 static 70 DWORD 71 SetHotPlugFlags( 72 _In_ DWORD dwFlags) 73 { 74 HKEY hKey = NULL; 75 DWORD dwError; 76 77 dwError = RegCreateKeyExW(HKEY_CURRENT_USER, 78 REGSTR_PATH_SYSTRAY, 79 0, 80 NULL, 81 REG_OPTION_NON_VOLATILE, 82 KEY_WRITE, 83 NULL, 84 &hKey, 85 NULL); 86 if (dwError != ERROR_SUCCESS) 87 goto done; 88 89 dwError = RegSetValueExW(hKey, 90 L"HotPlugFlags", 91 0, 92 REG_DWORD, 93 (LPBYTE)&dwFlags, 94 sizeof(dwFlags)); 95 96 done: 97 if (hKey != NULL) 98 RegCloseKey(hKey); 99 100 return dwError; 101 } 102 103 104 static 105 HTREEITEM 106 InsertDeviceTreeItem( 107 _In_ HWND hwndDeviceTree, 108 _In_ HTREEITEM hParent, 109 _In_ DWORD DevInst, 110 _In_ PHOTPLUG_DATA pHotplugData) 111 { 112 WCHAR szDisplayName[40]; 113 WCHAR szGuidString[MAX_GUID_STRING_LEN]; 114 TVINSERTSTRUCTW tvItem; 115 GUID ClassGuid; 116 INT nClassImage; 117 DWORD dwSize; 118 CONFIGRET cr; 119 120 /* Get the device description */ 121 dwSize = sizeof(szDisplayName); 122 cr = CM_Get_DevNode_Registry_Property(DevInst, 123 CM_DRP_DEVICEDESC, 124 NULL, 125 szDisplayName, 126 &dwSize, 127 0); 128 if (cr != CR_SUCCESS) 129 wcscpy(szDisplayName, L"Unknown Device"); 130 131 /* Get the class GUID */ 132 dwSize = sizeof(szGuidString); 133 cr = CM_Get_DevNode_Registry_Property(DevInst, 134 CM_DRP_CLASSGUID, 135 NULL, 136 szGuidString, 137 &dwSize, 138 0); 139 if (cr == CR_SUCCESS) 140 { 141 pSetupGuidFromString(szGuidString, &ClassGuid); 142 } 143 else 144 { 145 memcpy(&ClassGuid, &GUID_DEVCLASS_UNKNOWN, sizeof(GUID)); 146 } 147 148 /* Get the image for the class this device is in */ 149 SetupDiGetClassImageIndex(&pHotplugData->ImageListData, 150 &ClassGuid, 151 &nClassImage); 152 153 /* Add it to the device tree */ 154 ZeroMemory(&tvItem, sizeof(tvItem)); 155 tvItem.hParent = hParent; 156 tvItem.hInsertAfter = TVI_LAST; 157 158 tvItem.item.mask = TVIF_STATE | TVIF_TEXT /*| TVIF_PARAM*/ | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 159 tvItem.item.state = TVIS_EXPANDED; 160 tvItem.item.stateMask = TVIS_EXPANDED; 161 tvItem.item.pszText = szDisplayName; 162 tvItem.item.iImage = nClassImage; 163 tvItem.item.iSelectedImage = nClassImage; 164 tvItem.item.lParam = (LPARAM)NULL; 165 166 return TreeView_InsertItem(hwndDeviceTree, &tvItem); 167 } 168 169 170 static 171 VOID 172 RecursiveInsertSubDevices( 173 _In_ HWND hwndDeviceTree, 174 _In_ HTREEITEM hParentItem, 175 _In_ DWORD ParentDevInst, 176 _In_ PHOTPLUG_DATA pHotplugData) 177 { 178 HTREEITEM hTreeItem; 179 DEVINST ChildDevInst; 180 CONFIGRET cr; 181 182 DPRINT("RecursiveInsertSubDevices()\n"); 183 184 cr = CM_Get_Child(&ChildDevInst, ParentDevInst, 0); 185 if (cr != CR_SUCCESS) 186 { 187 DPRINT("No child! %lu\n", cr); 188 return; 189 } 190 191 hTreeItem = InsertDeviceTreeItem(hwndDeviceTree, 192 hParentItem, 193 ChildDevInst, 194 pHotplugData); 195 if (hTreeItem != NULL) 196 { 197 RecursiveInsertSubDevices(hwndDeviceTree, 198 hTreeItem, 199 ChildDevInst, 200 pHotplugData); 201 } 202 203 for (;;) 204 { 205 cr = CM_Get_Sibling(&ChildDevInst, ChildDevInst, 0); 206 if (cr != CR_SUCCESS) 207 { 208 DPRINT("No sibling! %lu\n", cr); 209 return; 210 } 211 212 hTreeItem = InsertDeviceTreeItem(hwndDeviceTree, 213 hParentItem, 214 ChildDevInst, 215 pHotplugData); 216 if (hTreeItem != NULL) 217 { 218 RecursiveInsertSubDevices(hwndDeviceTree, 219 hTreeItem, 220 ChildDevInst, 221 pHotplugData); 222 } 223 } 224 } 225 226 227 static 228 VOID 229 EnumHotpluggedDevices( 230 HWND hwndDeviceTree, 231 PHOTPLUG_DATA pHotplugData) 232 { 233 SP_DEVINFO_DATA did = { 0 }; 234 HDEVINFO hdev; 235 int idev; 236 DWORD dwCapabilities, dwSize; 237 ULONG ulStatus, ulProblem; 238 HTREEITEM hTreeItem; 239 CONFIGRET cr; 240 241 DPRINT1("EnumHotpluggedDevices()\n"); 242 243 TreeView_DeleteAllItems(hwndDeviceTree); 244 245 hdev = SetupDiGetClassDevs(NULL, NULL, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT); 246 if (hdev == INVALID_HANDLE_VALUE) 247 return; 248 249 did.cbSize = sizeof(did); 250 251 /* Enumerate all the attached devices */ 252 for (idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); idev++) 253 { 254 ulStatus = 0; 255 ulProblem = 0; 256 257 cr = CM_Get_DevNode_Status(&ulStatus, 258 &ulProblem, 259 did.DevInst, 260 0); 261 if (cr != CR_SUCCESS) 262 continue; 263 264 dwCapabilities = 0, 265 dwSize = sizeof(dwCapabilities); 266 cr = CM_Get_DevNode_Registry_Property(did.DevInst, 267 CM_DRP_CAPABILITIES, 268 NULL, 269 &dwCapabilities, 270 &dwSize, 271 0); 272 if (cr != CR_SUCCESS) 273 continue; 274 275 /* Add devices that require safe removal to the device tree */ 276 if ( (dwCapabilities & CM_DEVCAP_REMOVABLE) && 277 !(dwCapabilities & CM_DEVCAP_DOCKDEVICE) && 278 !(dwCapabilities & CM_DEVCAP_SURPRISEREMOVALOK) && 279 ((dwCapabilities & CM_DEVCAP_EJECTSUPPORTED) || (ulStatus & DN_DISABLEABLE)) && 280 ulProblem == 0) 281 { 282 hTreeItem = InsertDeviceTreeItem(hwndDeviceTree, 283 TVI_ROOT, 284 did.DevInst, 285 pHotplugData); 286 287 if ((hTreeItem != NULL) && (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS)) 288 { 289 RecursiveInsertSubDevices(hwndDeviceTree, 290 hTreeItem, 291 did.DevInst, 292 pHotplugData); 293 } 294 } 295 } 296 297 SetupDiDestroyDeviceInfoList(hdev); 298 } 299 300 301 static 302 VOID 303 UpdateButtons( 304 HWND hwndDlg) 305 { 306 BOOL bEnabled; 307 308 bEnabled = (TreeView_GetCount(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE)) != 0); 309 310 EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_PROPERTIES), bEnabled); 311 EnableWindow(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_STOP), bEnabled); 312 } 313 314 315 static 316 VOID 317 ShowContextMenu( 318 HWND hwndDlg, 319 HWND hwndTreeView, 320 PHOTPLUG_DATA pHotplugData) 321 { 322 HTREEITEM hTreeItem; 323 RECT rc; 324 POINT pt; 325 326 hTreeItem = TreeView_GetSelection(hwndTreeView); 327 if (hTreeItem == NULL) 328 return; 329 330 TreeView_GetItemRect(hwndTreeView, hTreeItem, &rc, TRUE); 331 332 pt.x = (rc.left + rc.right) / 2; 333 pt.y = (rc.top + rc.bottom) / 2; 334 ClientToScreen(hwndTreeView, &pt); 335 TrackPopupMenu(GetSubMenu(pHotplugData->hPopupMenu, 0), 336 TPM_LEFTALIGN | TPM_TOPALIGN, 337 pt.x, 338 pt.y, 339 0, 340 hwndDlg, 341 NULL); 342 } 343 344 345 INT_PTR 346 CALLBACK 347 SafeRemovalDlgProc( 348 HWND hwndDlg, 349 UINT uMsg, 350 WPARAM wParam, 351 LPARAM lParam) 352 { 353 PHOTPLUG_DATA pHotplugData; 354 355 pHotplugData = (PHOTPLUG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER); 356 357 switch (uMsg) 358 { 359 case WM_INITDIALOG: 360 pHotplugData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HOTPLUG_DATA)); 361 if (pHotplugData != NULL) 362 { 363 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pHotplugData); 364 365 pHotplugData->ImageListData.cbSize = sizeof(pHotplugData->ImageListData); 366 SetupDiGetClassImageList(&pHotplugData->ImageListData); 367 368 pHotplugData->hPopupMenu = LoadMenu(hApplet, MAKEINTRESOURCE(IDM_POPUP_DEVICE_TREE)); 369 370 pHotplugData->dwFlags = GetHotPlugFlags(); 371 372 if (pHotplugData->dwFlags & HOTPLUG_DISPLAY_DEVICE_COMPONENTS) 373 Button_SetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS), BST_CHECKED); 374 375 TreeView_SetImageList(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 376 pHotplugData->ImageListData.ImageList, 377 TVSIL_NORMAL); 378 379 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 380 pHotplugData); 381 UpdateButtons(hwndDlg); 382 } 383 return TRUE; 384 385 case WM_COMMAND: 386 switch (LOWORD(wParam)) 387 { 388 case IDCLOSE: 389 KillTimer(hwndDlg, 1); 390 EndDialog(hwndDlg, TRUE); 391 break; 392 393 case IDC_SAFE_REMOVE_DISPLAY_COMPONENTS: 394 if (HIWORD(wParam) == BN_CLICKED) 395 { 396 if (pHotplugData != NULL) 397 { 398 if (Button_GetCheck(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DISPLAY_COMPONENTS)) == BST_CHECKED) 399 pHotplugData->dwFlags |= HOTPLUG_DISPLAY_DEVICE_COMPONENTS; 400 else 401 pHotplugData->dwFlags &= ~HOTPLUG_DISPLAY_DEVICE_COMPONENTS; 402 403 SetHotPlugFlags(pHotplugData->dwFlags); 404 405 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 406 pHotplugData); 407 } 408 } 409 break; 410 } 411 break; 412 413 case WM_DEVICECHANGE: 414 switch (wParam) 415 { 416 case DBT_DEVNODES_CHANGED: 417 SetTimer(hwndDlg, 1, 500, NULL); 418 break; 419 } 420 break; 421 422 case WM_TIMER: 423 if (wParam == 1) 424 { 425 KillTimer(hwndDlg, 1); 426 427 if (pHotplugData != NULL) 428 { 429 EnumHotpluggedDevices(GetDlgItem(hwndDlg, IDC_SAFE_REMOVE_DEVICE_TREE), 430 pHotplugData); 431 UpdateButtons(hwndDlg); 432 } 433 } 434 break; 435 436 case WM_NOTIFY: 437 if (((LPNMHDR)lParam)->idFrom == IDC_SAFE_REMOVE_DEVICE_TREE) 438 { 439 if (((LPNMHDR)lParam)->code == NM_RCLICK) 440 { 441 if (pHotplugData != NULL) 442 { 443 ShowContextMenu(hwndDlg, 444 ((LPNMHDR)lParam)->hwndFrom, 445 pHotplugData); 446 return TRUE; 447 } 448 } 449 } 450 break; 451 452 case WM_CLOSE: 453 KillTimer(hwndDlg, 1); 454 EndDialog(hwndDlg, TRUE); 455 break; 456 457 case WM_DESTROY: 458 if (pHotplugData != NULL) 459 { 460 if (pHotplugData->hPopupMenu != NULL) 461 DestroyMenu(pHotplugData->hPopupMenu); 462 463 SetupDiDestroyClassImageList(&pHotplugData->ImageListData); 464 465 HeapFree(GetProcessHeap(), 0, pHotplugData); 466 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL); 467 } 468 break; 469 } 470 471 return FALSE; 472 } 473 474 475 LONG 476 APIENTRY 477 InitApplet( 478 HWND hwnd, 479 UINT uMsg, 480 LPARAM wParam, 481 LPARAM lParam) 482 { 483 DPRINT("InitApplet()\n"); 484 485 DialogBox(hApplet, 486 MAKEINTRESOURCE(IDD_SAFE_REMOVE_HARDWARE_DIALOG), 487 hwnd, 488 SafeRemovalDlgProc); 489 490 // TODO 491 return TRUE; 492 } 493 494 495 LONG 496 CALLBACK 497 CPlApplet( 498 HWND hwndCPl, 499 UINT uMsg, 500 LPARAM lParam1, 501 LPARAM lParam2) 502 { 503 UINT i = (UINT)lParam1; 504 505 switch(uMsg) 506 { 507 case CPL_INIT: 508 return TRUE; 509 510 case CPL_GETCOUNT: 511 return NUM_APPLETS; 512 513 case CPL_INQUIRE: 514 { 515 CPLINFO *CPlInfo = (CPLINFO*)lParam2; 516 CPlInfo->lData = 0; 517 CPlInfo->idIcon = Applets[i].idIcon; 518 CPlInfo->idName = Applets[i].idName; 519 CPlInfo->idInfo = Applets[i].idDescription; 520 } 521 break; 522 523 case CPL_DBLCLK: 524 Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2); 525 break; 526 527 case CPL_STARTWPARMSW: 528 return Applets[i].AppletProc(hwndCPl, uMsg, lParam1, lParam2); 529 } 530 return FALSE; 531 } 532 533 534 INT 535 WINAPI 536 DllMain( 537 HINSTANCE hinstDLL, 538 DWORD dwReason, 539 LPVOID lpvReserved) 540 { 541 UNREFERENCED_PARAMETER(lpvReserved); 542 543 switch (dwReason) 544 { 545 case DLL_PROCESS_ATTACH: 546 case DLL_THREAD_ATTACH: 547 hApplet = hinstDLL; 548 break; 549 } 550 return TRUE; 551 } 552