1 /* Control Panel management 2 * 3 * Copyright 2001 Eric Pouech 4 * Copyright 2008 Owen Rudge 5 * Copyright 2022 Raymond Czerny 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include <assert.h> 23 24 #define WIN32_NO_STATUS 25 #define _INC_WINDOWS 26 27 #include <windef.h> 28 #include <winbase.h> 29 #define NO_SHLWAPI_REG 30 #include <shlwapi.h> 31 #include <shellapi.h> 32 #define COBJMACROS 33 #include <shobjidl.h> 34 #include <wine/debug.h> 35 36 #include <strsafe.h> 37 38 #include "cpanel.h" 39 #include "wine/unicode.h" 40 41 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl); 42 43 void Control_UnloadApplet(CPlApplet* applet) 44 { 45 unsigned i; 46 47 for (i = 0; i < applet->count; i++) 48 applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].data); 49 50 if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L); 51 FreeLibrary(applet->hModule); 52 #ifndef __REACTOS__ 53 list_remove( &applet->entry ); 54 #endif 55 HeapFree(GetProcessHeap(), 0, applet->cmd); 56 HeapFree(GetProcessHeap(), 0, applet); 57 } 58 59 CPlApplet* Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel) 60 { 61 #ifdef __REACTOS__ 62 ACTCTXW ActCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID}; 63 ULONG_PTR cookie; 64 BOOL bActivated; 65 WCHAR fileBuffer[MAX_PATH]; 66 #endif 67 CPlApplet* applet; 68 DWORD len; 69 unsigned i; 70 CPLINFO info; 71 NEWCPLINFOW newinfo; 72 73 if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) 74 return applet; 75 76 len = ExpandEnvironmentStringsW(cmd, NULL, 0); 77 if (len > 0) 78 { 79 if (!(applet->cmd = HeapAlloc(GetProcessHeap(), 0, (len+1) * sizeof(WCHAR)))) 80 { 81 WARN("Cannot allocate memory for applet path\n"); 82 goto theError; 83 } 84 ExpandEnvironmentStringsW(cmd, applet->cmd, len+1); 85 } 86 else 87 { 88 WARN("Cannot expand applet path\n"); 89 goto theError; 90 } 91 92 applet->hWnd = hWnd; 93 94 #ifdef __REACTOS__ 95 StringCchCopy(fileBuffer, MAX_PATH, applet->cmd); 96 if (PathIsFileSpecW(fileBuffer)) 97 SearchPath(NULL, fileBuffer, NULL, MAX_PATH, fileBuffer, NULL); 98 ActCtx.lpSource = fileBuffer; 99 ActCtx.lpResourceName = (LPCWSTR)123; 100 applet->hActCtx = CreateActCtx(&ActCtx); 101 bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE); 102 #endif 103 104 if (!(applet->hModule = LoadLibraryW(applet->cmd))) { 105 WARN("Cannot load control panel applet %s\n", debugstr_w(applet->cmd)); 106 goto theError; 107 } 108 if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) { 109 WARN("Not a valid control panel applet %s\n", debugstr_w(applet->cmd)); 110 goto theError; 111 } 112 if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) { 113 WARN("Init of applet has failed\n"); 114 goto theError; 115 } 116 if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) { 117 WARN("No subprogram in applet\n"); 118 applet->proc(applet->hWnd, CPL_EXIT, 0, 0); 119 goto theError; 120 } 121 122 applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet, 123 FIELD_OFFSET( CPlApplet, info[applet->count] )); 124 125 for (i = 0; i < applet->count; i++) { 126 ZeroMemory(&newinfo, sizeof(newinfo)); 127 newinfo.dwSize = sizeof(NEWCPLINFOA); 128 applet->info[i].helpfile[0] = 0; 129 /* proc is supposed to return a null value upon success for 130 * CPL_INQUIRE and CPL_NEWINQUIRE 131 * However, real drivers don't seem to behave like this 132 * So, use introspection rather than return value 133 */ 134 applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info); 135 applet->info[i].data = info.lData; 136 #ifdef __REACTOS__ 137 applet->info[i].idIcon = info.idIcon; 138 #endif 139 140 if (info.idIcon != CPL_DYNAMIC_RES) 141 applet->info[i].icon = LoadIconW(applet->hModule, MAKEINTRESOURCEW(info.idIcon)); 142 if (info.idName != CPL_DYNAMIC_RES) 143 LoadStringW(applet->hModule, info.idName, 144 applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR)); 145 if (info.idInfo != CPL_DYNAMIC_RES) 146 LoadStringW(applet->hModule, info.idInfo, 147 applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR)); 148 149 /* some broken control panels seem to return incorrect values in CPL_INQUIRE, 150 but proper data in CPL_NEWINQUIRE. if we get an empty string or a null 151 icon, see what we can get from CPL_NEWINQUIRE */ 152 153 if (!applet->info[i].name[0]) info.idName = CPL_DYNAMIC_RES; 154 155 /* zero-length szInfo may not be a buggy applet, but it doesn't hurt for us 156 to check anyway */ 157 158 if (!applet->info[i].info[0]) info.idInfo = CPL_DYNAMIC_RES; 159 160 if (applet->info[i].icon == NULL) 161 info.idIcon = CPL_DYNAMIC_RES; 162 163 if ((info.idIcon == CPL_DYNAMIC_RES) || (info.idName == CPL_DYNAMIC_RES) || 164 (info.idInfo == CPL_DYNAMIC_RES)) { 165 applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo); 166 167 applet->info[i].data = newinfo.lData; 168 if (info.idIcon == CPL_DYNAMIC_RES) { 169 if (!newinfo.hIcon) WARN("couldn't get icon for applet %u\n", i); 170 applet->info[i].icon = newinfo.hIcon; 171 } 172 if (newinfo.dwSize == sizeof(NEWCPLINFOW)) { 173 if (info.idName == CPL_DYNAMIC_RES) 174 memcpy(applet->info[i].name, newinfo.szName, sizeof(newinfo.szName)); 175 if (info.idInfo == CPL_DYNAMIC_RES) 176 memcpy(applet->info[i].info, newinfo.szInfo, sizeof(newinfo.szInfo)); 177 memcpy(applet->info[i].helpfile, newinfo.szHelpFile, sizeof(newinfo.szHelpFile)); 178 } else { 179 if (info.idName == CPL_DYNAMIC_RES) 180 MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName, 181 sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR), 182 applet->info[i].name, sizeof(applet->info[i].name) / sizeof(WCHAR)); 183 if (info.idInfo == CPL_DYNAMIC_RES) 184 MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo, 185 sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR), 186 applet->info[i].info, sizeof(applet->info[i].info) / sizeof(WCHAR)); 187 MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile, 188 sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR), 189 applet->info[i].helpfile, 190 sizeof(applet->info[i].helpfile) / sizeof(WCHAR)); 191 } 192 } 193 } 194 195 #ifdef __REACTOS__ 196 if (bActivated) 197 DeactivateActCtx(0, cookie); 198 #endif 199 200 #ifndef __REACTOS__ 201 list_add_head( &panel->applets, &applet->entry ); 202 #endif 203 204 return applet; 205 206 theError: 207 FreeLibrary(applet->hModule); 208 HeapFree(GetProcessHeap(), 0, applet->cmd); 209 HeapFree(GetProcessHeap(), 0, applet); 210 return NULL; 211 } 212 213 #ifndef __REACTOS__ 214 215 #define IDC_LISTVIEW 1000 216 #define IDC_STATUSBAR 1001 217 218 #define NUM_COLUMNS 2 219 #define LISTVIEW_DEFSTYLE (WS_CHILD | WS_VISIBLE | WS_TABSTOP |\ 220 LVS_SORTASCENDING | LVS_AUTOARRANGE | LVS_SINGLESEL) 221 222 static BOOL Control_CreateListView (CPanel *panel) 223 { 224 RECT ws, sb; 225 WCHAR empty_string[] = {0}; 226 WCHAR buf[MAX_STRING_LEN]; 227 LVCOLUMNW lvc; 228 229 /* Create list view */ 230 GetClientRect(panel->hWndStatusBar, &sb); 231 GetClientRect(panel->hWnd, &ws); 232 233 panel->hWndListView = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, 234 empty_string, LISTVIEW_DEFSTYLE | LVS_ICON, 235 0, 0, ws.right - ws.left, ws.bottom - ws.top - 236 (sb.bottom - sb.top), panel->hWnd, 237 (HMENU) IDC_LISTVIEW, panel->hInst, NULL); 238 239 if (!panel->hWndListView) 240 return FALSE; 241 242 /* Create image lists for list view */ 243 panel->hImageListSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), 244 GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 1, 1); 245 panel->hImageListLarge = ImageList_Create(GetSystemMetrics(SM_CXICON), 246 GetSystemMetrics(SM_CYICON), ILC_COLOR32 | ILC_MASK, 1, 1); 247 248 SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)panel->hImageListSmall); 249 SendMessageW(panel->hWndListView, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)panel->hImageListLarge); 250 251 /* Create columns for list view */ 252 lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH; 253 lvc.pszText = buf; 254 lvc.fmt = LVCFMT_LEFT; 255 256 /* Name column */ 257 lvc.iSubItem = 0; 258 lvc.cx = (ws.right - ws.left) / 3; 259 LoadStringW(shell32_hInstance, IDS_CPANEL_NAME, buf, sizeof(buf) / sizeof(buf[0])); 260 261 if (ListView_InsertColumnW(panel->hWndListView, 0, &lvc) == -1) 262 return FALSE; 263 264 /* Description column */ 265 lvc.iSubItem = 1; 266 lvc.cx = ((ws.right - ws.left) / 3) * 2; 267 LoadStringW(shell32_hInstance, IDS_CPANEL_DESCRIPTION, buf, sizeof(buf) / 268 sizeof(buf[0])); 269 270 if (ListView_InsertColumnW(panel->hWndListView, 1, &lvc) == -1) 271 return FALSE; 272 273 return(TRUE); 274 } 275 276 static void Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs) 277 { 278 CPanel* panel = cs->lpCreateParams; 279 HMENU hMenu, hSubMenu; 280 CPlApplet* applet; 281 MENUITEMINFOW mii; 282 unsigned int i; 283 int menucount, index; 284 CPlItem *item; 285 LVITEMW lvItem; 286 INITCOMMONCONTROLSEX icex; 287 INT sb_parts; 288 int itemidx; 289 290 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel); 291 panel->hWnd = hWnd; 292 293 /* Initialise common control DLL */ 294 icex.dwSize = sizeof(INITCOMMONCONTROLSEX); 295 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES; 296 InitCommonControlsEx(&icex); 297 298 /* create the status bar */ 299 if (!(panel->hWndStatusBar = CreateStatusWindowW(WS_CHILD | WS_VISIBLE | CCS_BOTTOM | SBARS_SIZEGRIP, NULL, hWnd, IDC_STATUSBAR))) 300 return; 301 302 sb_parts = -1; 303 SendMessageW(panel->hWndStatusBar, SB_SETPARTS, 1, (LPARAM) &sb_parts); 304 305 /* create the list view */ 306 if (!Control_CreateListView(panel)) 307 return; 308 309 hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL)); 310 311 /* insert menu items for applets */ 312 hSubMenu = GetSubMenu(hMenu, 0); 313 menucount = 0; 314 315 LIST_FOR_EACH_ENTRY( applet, &panel->applets, CPlApplet, entry ) 316 { 317 for (i = 0; i < applet->count; i++) { 318 /* set up a CPlItem for this particular subprogram */ 319 item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem)); 320 321 if (!item) 322 continue; 323 324 item->applet = applet; 325 item->id = i; 326 327 mii.cbSize = sizeof(MENUITEMINFOW); 328 mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA; 329 mii.dwTypeData = applet->info[i].name; 330 mii.cch = sizeof(applet->info[i].name) / sizeof(WCHAR); 331 mii.wID = IDM_CPANEL_APPLET_BASE + menucount; 332 mii.dwItemData = (ULONG_PTR)item; 333 334 if (InsertMenuItemW(hSubMenu, menucount, TRUE, &mii)) { 335 /* add the list view item */ 336 HICON icon = applet->info[i].icon; 337 if (!icon) icon = LoadImageW( 0, (LPCWSTR)IDI_WINLOGO, IMAGE_ICON, 0, 0, LR_SHARED ); 338 index = ImageList_AddIcon(panel->hImageListLarge, icon); 339 ImageList_AddIcon(panel->hImageListSmall, icon); 340 341 lvItem.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM; 342 lvItem.iItem = menucount; 343 lvItem.iSubItem = 0; 344 lvItem.pszText = applet->info[i].name; 345 lvItem.iImage = index; 346 lvItem.lParam = (LPARAM) item; 347 348 itemidx = ListView_InsertItemW(panel->hWndListView, &lvItem); 349 350 /* add the description */ 351 ListView_SetItemTextW(panel->hWndListView, itemidx, 1, applet->info[i].info); 352 353 /* update menu bar, increment count */ 354 DrawMenuBar(hWnd); 355 menucount++; 356 } 357 } 358 } 359 360 panel->total_subprogs = menucount; 361 362 /* check the "large items" icon in the View menu */ 363 hSubMenu = GetSubMenu(hMenu, 1); 364 CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW, 365 FCIDM_SHVIEW_BIGICON, MF_BYCOMMAND); 366 367 SetMenu(hWnd, hMenu); 368 } 369 370 static void Control_FreeCPlItems(HWND hWnd, CPanel *panel) 371 { 372 HMENU hMenu, hSubMenu; 373 MENUITEMINFOW mii; 374 unsigned int i; 375 376 /* get the File menu */ 377 hMenu = GetMenu(hWnd); 378 379 if (!hMenu) 380 return; 381 382 hSubMenu = GetSubMenu(hMenu, 0); 383 384 if (!hSubMenu) 385 return; 386 387 /* loop and free the item data */ 388 for (i = IDM_CPANEL_APPLET_BASE; i <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs; i++) 389 { 390 mii.cbSize = sizeof(MENUITEMINFOW); 391 mii.fMask = MIIM_DATA; 392 393 if (!GetMenuItemInfoW(hSubMenu, i, FALSE, &mii)) 394 continue; 395 396 HeapFree(GetProcessHeap(), 0, (LPVOID) mii.dwItemData); 397 } 398 } 399 400 static void Control_UpdateListViewStyle(CPanel *panel, UINT style, UINT id) 401 { 402 HMENU hMenu, hSubMenu; 403 404 SetWindowLongW(panel->hWndListView, GWL_STYLE, LISTVIEW_DEFSTYLE | style); 405 406 /* update the menu */ 407 hMenu = GetMenu(panel->hWnd); 408 hSubMenu = GetSubMenu(hMenu, 1); 409 410 CheckMenuRadioItem(hSubMenu, FCIDM_SHVIEW_BIGICON, FCIDM_SHVIEW_REPORTVIEW, 411 id, MF_BYCOMMAND); 412 } 413 414 static CPlItem* Control_GetCPlItem_From_MenuID(HWND hWnd, UINT id) 415 { 416 HMENU hMenu, hSubMenu; 417 MENUITEMINFOW mii; 418 419 /* retrieve the CPlItem structure from the menu item data */ 420 hMenu = GetMenu(hWnd); 421 422 if (!hMenu) 423 return NULL; 424 425 hSubMenu = GetSubMenu(hMenu, 0); 426 427 if (!hSubMenu) 428 return NULL; 429 430 mii.cbSize = sizeof(MENUITEMINFOW); 431 mii.fMask = MIIM_DATA; 432 433 if (!GetMenuItemInfoW(hSubMenu, id, FALSE, &mii)) 434 return NULL; 435 436 return (CPlItem *) mii.dwItemData; 437 } 438 439 static CPlItem* Control_GetCPlItem_From_ListView(CPanel *panel) 440 { 441 LVITEMW lvItem; 442 int selitem; 443 444 selitem = SendMessageW(panel->hWndListView, LVM_GETNEXTITEM, -1, LVNI_FOCUSED 445 | LVNI_SELECTED); 446 447 if (selitem != -1) 448 { 449 lvItem.iItem = selitem; 450 lvItem.mask = LVIF_PARAM; 451 452 if (SendMessageW(panel->hWndListView, LVM_GETITEMW, 0, (LPARAM) &lvItem)) 453 return (CPlItem *) lvItem.lParam; 454 } 455 456 return NULL; 457 } 458 459 static void Control_StartApplet(HWND hWnd, CPlItem *item) 460 { 461 WCHAR verbOpen[] = {'c','p','l','o','p','e','n',0}; 462 WCHAR format[] = {'@','%','d',0}; 463 WCHAR param[MAX_PATH]; 464 465 /* execute the applet if item is valid */ 466 if (item) 467 { 468 wsprintfW(param, format, item->id); 469 ShellExecuteW(hWnd, verbOpen, item->applet->cmd, param, NULL, SW_SHOW); 470 } 471 } 472 473 static LRESULT WINAPI Control_WndProc(HWND hWnd, UINT wMsg, 474 WPARAM lParam1, LPARAM lParam2) 475 { 476 CPanel* panel = (CPanel*)GetWindowLongPtrW(hWnd, 0); 477 478 if (panel || wMsg == WM_CREATE) { 479 switch (wMsg) { 480 case WM_CREATE: 481 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2); 482 return 0; 483 case WM_DESTROY: 484 { 485 CPlApplet *applet, *next; 486 LIST_FOR_EACH_ENTRY_SAFE( applet, next, &panel->applets, CPlApplet, entry ) 487 Control_UnloadApplet(applet); 488 } 489 Control_FreeCPlItems(hWnd, panel); 490 PostQuitMessage(0); 491 break; 492 case WM_COMMAND: 493 switch (LOWORD(lParam1)) 494 { 495 case IDM_CPANEL_EXIT: 496 SendMessageW(hWnd, WM_CLOSE, 0, 0); 497 return 0; 498 499 case IDM_CPANEL_ABOUT: 500 { 501 WCHAR appName[MAX_STRING_LEN]; 502 HICON icon = LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), 503 IMAGE_ICON, 48, 48, LR_SHARED); 504 505 LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, 506 sizeof(appName) / sizeof(appName[0])); 507 ShellAboutW(hWnd, appName, NULL, icon); 508 509 return 0; 510 } 511 512 case FCIDM_SHVIEW_BIGICON: 513 Control_UpdateListViewStyle(panel, LVS_ICON, FCIDM_SHVIEW_BIGICON); 514 return 0; 515 516 case FCIDM_SHVIEW_SMALLICON: 517 Control_UpdateListViewStyle(panel, LVS_SMALLICON, FCIDM_SHVIEW_SMALLICON); 518 return 0; 519 520 case FCIDM_SHVIEW_LISTVIEW: 521 Control_UpdateListViewStyle(panel, LVS_LIST, FCIDM_SHVIEW_LISTVIEW); 522 return 0; 523 524 case FCIDM_SHVIEW_REPORTVIEW: 525 Control_UpdateListViewStyle(panel, LVS_REPORT, FCIDM_SHVIEW_REPORTVIEW); 526 return 0; 527 528 default: 529 /* check if this is an applet */ 530 if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) && 531 (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs)) 532 { 533 Control_StartApplet(hWnd, Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1))); 534 return 0; 535 } 536 537 break; 538 } 539 540 break; 541 542 case WM_NOTIFY: 543 { 544 LPNMHDR nmh = (LPNMHDR) lParam2; 545 546 switch (nmh->idFrom) 547 { 548 case IDC_LISTVIEW: 549 switch (nmh->code) 550 { 551 case NM_RETURN: 552 case NM_DBLCLK: 553 { 554 Control_StartApplet(hWnd, Control_GetCPlItem_From_ListView(panel)); 555 return 0; 556 } 557 case LVN_ITEMCHANGED: 558 { 559 CPlItem *item = Control_GetCPlItem_From_ListView(panel); 560 561 /* update the status bar if item is valid */ 562 if (item) 563 SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info); 564 else 565 SetWindowTextW(panel->hWndStatusBar, NULL); 566 567 return 0; 568 } 569 } 570 571 break; 572 } 573 574 break; 575 } 576 577 case WM_MENUSELECT: 578 /* check if this is an applet */ 579 if ((LOWORD(lParam1) >= IDM_CPANEL_APPLET_BASE) && 580 (LOWORD(lParam1) <= IDM_CPANEL_APPLET_BASE + panel->total_subprogs)) 581 { 582 CPlItem *item = Control_GetCPlItem_From_MenuID(hWnd, LOWORD(lParam1)); 583 584 /* update the status bar if item is valid */ 585 if (item) 586 SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info); 587 } 588 else if ((HIWORD(lParam1) == 0xFFFF) && (lParam2 == 0)) 589 { 590 /* reset status bar description to that of the selected icon */ 591 CPlItem *item = Control_GetCPlItem_From_ListView(panel); 592 593 if (item) 594 SetWindowTextW(panel->hWndStatusBar, item->applet->info[item->id].info); 595 else 596 SetWindowTextW(panel->hWndStatusBar, NULL); 597 598 return 0; 599 } 600 else 601 SetWindowTextW(panel->hWndStatusBar, NULL); 602 603 return 0; 604 605 case WM_SIZE: 606 { 607 HDWP hdwp; 608 RECT sb; 609 610 hdwp = BeginDeferWindowPos(2); 611 612 if (hdwp == NULL) 613 break; 614 615 GetClientRect(panel->hWndStatusBar, &sb); 616 617 hdwp = DeferWindowPos(hdwp, panel->hWndListView, NULL, 0, 0, 618 LOWORD(lParam2), HIWORD(lParam2) - (sb.bottom - sb.top), 619 SWP_NOZORDER | SWP_NOMOVE); 620 621 if (hdwp == NULL) 622 break; 623 624 hdwp = DeferWindowPos(hdwp, panel->hWndStatusBar, NULL, 0, 0, 625 LOWORD(lParam2), LOWORD(lParam1), SWP_NOZORDER | SWP_NOMOVE); 626 627 if (hdwp != NULL) 628 EndDeferWindowPos(hdwp); 629 630 return 0; 631 } 632 } 633 } 634 635 return DefWindowProcW(hWnd, wMsg, lParam1, lParam2); 636 } 637 638 static void Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst) 639 { 640 WNDCLASSEXW wc; 641 MSG msg; 642 WCHAR appName[MAX_STRING_LEN]; 643 const WCHAR className[] = {'S','h','e','l','l','_','C','o','n','t','r','o', 644 'l','_','W','n','d','C','l','a','s','s',0}; 645 646 LoadStringW(shell32_hInstance, IDS_CPANEL_TITLE, appName, sizeof(appName) / sizeof(appName[0])); 647 648 wc.cbSize = sizeof(wc); 649 wc.style = CS_HREDRAW|CS_VREDRAW; 650 wc.lpfnWndProc = Control_WndProc; 651 wc.cbClsExtra = 0; 652 wc.cbWndExtra = sizeof(CPlApplet*); 653 wc.hInstance = panel->hInst = hInst; 654 wc.hIcon = LoadIconW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL) ); 655 wc.hCursor = LoadCursorW( 0, (LPWSTR)IDC_ARROW ); 656 wc.hbrBackground = GetStockObject(WHITE_BRUSH); 657 wc.lpszMenuName = NULL; 658 wc.lpszClassName = className; 659 wc.hIconSm = LoadImageW( shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL), IMAGE_ICON, 660 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED); 661 662 if (!RegisterClassExW(&wc)) return; 663 664 CreateWindowExW(0, wc.lpszClassName, appName, 665 WS_OVERLAPPEDWINDOW | WS_VISIBLE, 666 CW_USEDEFAULT, CW_USEDEFAULT, 667 CW_USEDEFAULT, CW_USEDEFAULT, 668 hWnd, NULL, hInst, panel); 669 if (!panel->hWnd) return; 670 671 while (GetMessageW(&msg, panel->hWnd, 0, 0)) { 672 TranslateMessage(&msg); 673 DispatchMessageW(&msg); 674 } 675 } 676 677 static void Control_RegisterRegistryApplets(HWND hWnd, CPanel *panel, HKEY hkey_root, LPCWSTR szRepPath) 678 { 679 WCHAR name[MAX_PATH]; 680 WCHAR value[MAX_PATH]; 681 HKEY hkey; 682 683 if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS) 684 { 685 int idx = 0; 686 687 for(;; ++idx) 688 { 689 DWORD nameLen = MAX_PATH; 690 DWORD valueLen = MAX_PATH; 691 692 if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS) 693 break; 694 695 Control_LoadApplet(hWnd, value, panel); 696 } 697 RegCloseKey(hkey); 698 } 699 } 700 701 static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst) 702 { 703 static const WCHAR wszRegPath[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls"; 704 HANDLE h; 705 WIN32_FIND_DATAW fd; 706 WCHAR buffer[MAX_PATH]; 707 WCHAR *p; 708 709 /* first add .cpl files in the system directory */ 710 GetSystemDirectoryW( buffer, MAX_PATH ); 711 p = buffer + strlenW(buffer); 712 *p++ = '\\'; 713 lstrcpyW(p, L"*.cpl"); 714 715 if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) { 716 do { 717 lstrcpyW(p, fd.cFileName); 718 Control_LoadApplet(hWnd, buffer, panel); 719 } while (FindNextFileW(h, &fd)); 720 FindClose(h); 721 } 722 723 /* now check for cpls in the registry */ 724 Control_RegisterRegistryApplets(hWnd, panel, HKEY_LOCAL_MACHINE, wszRegPath); 725 Control_RegisterRegistryApplets(hWnd, panel, HKEY_CURRENT_USER, wszRegPath); 726 727 Control_DoInterface(panel, hWnd, hInst); 728 } 729 730 #else 731 static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst) 732 { 733 ShellExecuteW(NULL, 734 L"open", 735 L"explorer.exe", 736 L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}", 737 NULL, 738 SW_SHOWDEFAULT); 739 } 740 #endif 741 742 #ifdef __REACTOS__ 743 744 /** Structure for in and out data when 745 * search for the cpl dialog of first instance 746 */ 747 typedef struct tagAppDlgFindData 748 { 749 PCWSTR szAppFile; /**< Full path to applet library as search parameter */ 750 UINT_PTR sAppletNo; /**< Number of applet in a system control library as search parameter */ 751 ATOM aCPLName; /**< to read window property 'CPLName' */ 752 ATOM aCPLFlags; /**< to read window property 'CPLFlags'*/ 753 HWND hRunDLL; /**< to skip self instance */ 754 HWND hDlgResult; /**< Returned dialog handle or NULL if not found */ 755 } AppDlgFindData; 756 757 /** 758 * Callback function to search applet dialog 759 * @param hwnd A handle to a top-level window. 760 * @param lParam Pointer of AppDlgFindData 761 * @return TRUE: continue enumeration, FALSE: stop enumeration 762 */ 763 static BOOL CALLBACK 764 Control_EnumWinProc( 765 _In_ HWND hwnd, 766 _In_ LPARAM lParam) 767 { 768 AppDlgFindData* pData = (AppDlgFindData*)lParam; 769 UINT_PTR sAppletNo; 770 HANDLE hRes; 771 WCHAR szAppFile[MAX_PATH]; 772 773 if (pData->hRunDLL == hwnd) 774 return TRUE; /* Skip self instance */ 775 776 sAppletNo = (UINT_PTR)GetPropW(hwnd, (LPTSTR)MAKEINTATOM(pData->aCPLFlags)); 777 if (sAppletNo != pData->sAppletNo) 778 return TRUE; /* Continue enumeration */ 779 780 hRes = GetPropW(hwnd, (LPTSTR)MAKEINTATOM(pData->aCPLName)); 781 GlobalGetAtomNameW((ATOM)HandleToUlong(hRes), szAppFile, _countof(szAppFile)); 782 if (wcscmp(szAppFile, pData->szAppFile) == 0) 783 { 784 HWND hDialog = GetLastActivePopup(hwnd); 785 if (IsWindow(hDialog)) 786 { 787 pData->hDlgResult = hDialog; 788 return FALSE; /* Stop enumeration */ 789 } 790 } 791 792 return TRUE; /* Continue enumeration */ 793 } 794 795 /** 796 * This function makes the system control applet accessible via the taskbar. 797 * 798 * @param applet 799 * Pointer of system control applet. 800 * 801 * @param index 802 * Number of applet in a system control library. 803 */ 804 static void 805 Control_ShowAppletInTaskbar(CPlApplet* applet, UINT index) 806 { 807 HICON hSmallIcon; 808 ITaskbarList* pTaskbar = NULL; 809 810 /* Try to add a taskbar button only if the applet's parent window is the desktop */ 811 if (GetParent(applet->hWnd) != NULL) 812 { 813 return; 814 } 815 816 SetWindowTextW(applet->hWnd, applet->info[index].name); 817 818 /* Set large icon for the taskbar button */ 819 if (applet->info[index].icon) 820 { 821 SendMessageW(applet->hWnd, WM_SETICON, ICON_BIG, (LPARAM)applet->info[index].icon); 822 } 823 824 /* Try loading the small icon for the taskbar button */ 825 hSmallIcon = (HICON)LoadImageW(applet->hModule, 826 MAKEINTRESOURCEW(applet->info[index].idIcon), 827 IMAGE_ICON, 828 GetSystemMetrics(SM_CXSMICON), 829 GetSystemMetrics(SM_CYSMICON), 830 0); 831 if (hSmallIcon) 832 { 833 SendMessageW(applet->hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon); 834 } 835 else 836 { 837 if (applet->info[index].icon) 838 { 839 SendMessageW(applet->hWnd, WM_SETICON, ICON_SMALL, (LPARAM)applet->info[index].icon); 840 } 841 } 842 843 /* Add button to the taskbar */ 844 ShowWindow(applet->hWnd, SW_SHOWMINNOACTIVE); 845 846 /* Activate the corresponding button in the taskbar */ 847 CoInitialize(NULL); 848 if (CoCreateInstance(&CLSID_TaskbarList, 849 NULL, CLSCTX_INPROC_SERVER, 850 &IID_ITaskbarList, 851 (LPVOID*)&pTaskbar) == S_OK) 852 { 853 if (ITaskbarList_HrInit(pTaskbar) == S_OK) 854 { 855 ITaskbarList_ActivateTab(pTaskbar, applet->hWnd); 856 } 857 ITaskbarList_Release(pTaskbar); 858 } 859 CoUninitialize(); 860 } 861 862 #endif /* __REACTOS__ */ 863 864 static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd) 865 /* forms to parse: 866 * foo.cpl,@sp,str 867 * foo.cpl,@sp 868 * foo.cpl,,str 869 * foo.cpl @sp 870 * foo.cpl str 871 * "a path\foo.cpl" 872 */ 873 { 874 LPWSTR buffer; 875 LPWSTR beg = NULL; 876 LPWSTR end; 877 WCHAR ch; 878 LPWSTR ptr; 879 signed sp = -1; 880 LPWSTR extraPmtsBuf = NULL; 881 LPWSTR extraPmts = NULL; 882 BOOL quoted = FALSE; 883 CPlApplet *applet; 884 885 buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd)); 886 if (!buffer) return; 887 888 end = lstrcpyW(buffer, wszCmd); 889 890 for (;;) { 891 ch = *end; 892 if (ch == '"') quoted = !quoted; 893 if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) { 894 *end = '\0'; 895 if (beg) { 896 if (*beg == '@') { 897 sp = atoiW(beg + 1); 898 } else if (*beg == '\0') { 899 sp = -1; 900 } else { 901 extraPmtsBuf = beg; 902 } 903 } 904 if (ch == '\0') break; 905 beg = end + 1; 906 if (ch == ' ') while (end[1] == ' ') end++; 907 } 908 end++; 909 } 910 while ((ptr = StrChrW(buffer, '"'))) 911 memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR)); 912 913 /* now check for any quotes in extraPmtsBuf and remove */ 914 if (extraPmtsBuf != NULL) 915 { 916 beg = end = extraPmtsBuf; 917 quoted = FALSE; 918 919 for (;;) { 920 ch = *end; 921 if (ch == '"') quoted = !quoted; 922 if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) { 923 *end = '\0'; 924 if (beg) { 925 if (*beg != '\0') { 926 extraPmts = beg; 927 } 928 } 929 if (ch == '\0') break; 930 beg = end + 1; 931 if (ch == ' ') while (end[1] == ' ') end++; 932 } 933 end++; 934 } 935 936 while ((ptr = StrChrW(extraPmts, '"'))) 937 memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR)); 938 939 if (extraPmts == NULL) 940 extraPmts = extraPmtsBuf; 941 } 942 943 /* Now check if there had been a numerical value in the extra params */ 944 if ((extraPmts) && (*extraPmts == '@') && (sp == -1)) { 945 sp = atoiW(extraPmts + 1); 946 } 947 948 TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp); 949 950 applet = Control_LoadApplet(hWnd, buffer, panel); 951 if (applet) 952 { 953 #ifdef __REACTOS__ 954 ULONG_PTR cookie; 955 BOOL bActivated; 956 ATOM aCPLName; 957 ATOM aCPLFlags; 958 ATOM aCPLPath; 959 AppDlgFindData findData; 960 #endif 961 /* we've been given a textual parameter (or none at all) */ 962 if (sp == -1) { 963 while ((++sp) != applet->count) { 964 TRACE("sp %d, name %s\n", sp, debugstr_w(applet->info[sp].name)); 965 966 if (StrCmpIW(extraPmts, applet->info[sp].name) == 0) 967 break; 968 } 969 } 970 971 if (sp >= applet->count) { 972 WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count); 973 sp = 0; 974 } 975 976 #ifdef __REACTOS__ 977 bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE); 978 979 aCPLPath = GlobalFindAtomW(applet->cmd); 980 if (!aCPLPath) 981 { 982 aCPLPath = GlobalAddAtomW(applet->cmd); 983 } 984 985 aCPLName = GlobalFindAtomW(L"CPLName"); 986 if (!aCPLName) 987 { 988 aCPLName = GlobalAddAtomW(L"CPLName"); 989 } 990 991 aCPLFlags = GlobalFindAtomW(L"CPLFlags"); 992 if (!aCPLFlags) 993 { 994 aCPLFlags = GlobalAddAtomW(L"CPLFlags"); 995 } 996 997 findData.szAppFile = applet->cmd; 998 findData.sAppletNo = (UINT_PTR)(sp + 1); 999 findData.aCPLName = aCPLName; 1000 findData.aCPLFlags = aCPLFlags; 1001 findData.hRunDLL = applet->hWnd; 1002 findData.hDlgResult = NULL; 1003 // Find the dialog of this applet in the first instance. 1004 // Note: The simpler functions "FindWindow" or "FindWindowEx" does not find this type of dialogs. 1005 EnumWindows(Control_EnumWinProc, (LPARAM)&findData); 1006 if (findData.hDlgResult) 1007 { 1008 BringWindowToTop(findData.hDlgResult); 1009 } 1010 else 1011 { 1012 SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLName), (HANDLE)MAKEINTATOM(aCPLPath)); 1013 SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLFlags), UlongToHandle(sp + 1)); 1014 Control_ShowAppletInTaskbar(applet, sp); 1015 #endif 1016 1017 if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts)) 1018 applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data); 1019 #ifdef __REACTOS__ 1020 RemovePropW(applet->hWnd, applet->cmd); 1021 GlobalDeleteAtom(aCPLPath); 1022 } 1023 #endif 1024 1025 Control_UnloadApplet(applet); 1026 1027 #ifdef __REACTOS__ 1028 if (bActivated) 1029 DeactivateActCtx(0, cookie); 1030 #endif 1031 1032 } 1033 1034 HeapFree(GetProcessHeap(), 0, buffer); 1035 } 1036 1037 /************************************************************************* 1038 * Control_RunDLLW [SHELL32.@] 1039 * 1040 */ 1041 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow) 1042 { 1043 CPanel panel; 1044 1045 TRACE("(%p, %p, %s, 0x%08x)\n", 1046 hWnd, hInst, debugstr_w(cmd), nCmdShow); 1047 1048 #ifndef __REACTOS__ 1049 memset(&panel, 0, sizeof(panel)); 1050 list_init( &panel.applets ); 1051 #endif 1052 1053 if (!cmd || !*cmd) { 1054 Control_DoWindow(&panel, hWnd, hInst); 1055 } else { 1056 Control_DoLaunch(&panel, hWnd, cmd); 1057 } 1058 } 1059 1060 /************************************************************************* 1061 * Control_RunDLLA [SHELL32.@] 1062 * 1063 */ 1064 void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) 1065 { 1066 DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 ); 1067 LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 1068 if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len )) 1069 { 1070 Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow); 1071 } 1072 HeapFree(GetProcessHeap(), 0, wszCmd); 1073 } 1074 1075 /************************************************************************* 1076 * Control_FillCache_RunDLLW [SHELL32.@] 1077 * 1078 */ 1079 HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) 1080 { 1081 FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x); 1082 return S_OK; 1083 } 1084 1085 /************************************************************************* 1086 * Control_FillCache_RunDLLA [SHELL32.@] 1087 * 1088 */ 1089 HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) 1090 { 1091 return Control_FillCache_RunDLLW(hWnd, hModule, w, x); 1092 } 1093 1094 1095 #ifdef __REACTOS__ 1096 /************************************************************************* 1097 * RunDll_CallEntry16 [SHELL32.122] 1098 * the name is OK (when written with Dll, and not DLL as in Wine!) 1099 */ 1100 void WINAPI RunDll_CallEntry16( DWORD proc, HWND hwnd, HINSTANCE inst, 1101 LPCSTR cmdline, INT cmdshow ) 1102 { 1103 FIXME( "proc %lx hwnd %p inst %p cmdline %s cmdshow %d\n", 1104 proc, hwnd, inst, debugstr_a(cmdline), cmdshow ); 1105 } 1106 #endif 1107 1108 /************************************************************************* 1109 * CallCPLEntry16 [SHELL32.166] 1110 * 1111 * called by desk.cpl on "Advanced" with: 1112 * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0 1113 * 1114 */ 1115 #ifndef __REACTOS__ 1116 DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6) 1117 #else 1118 DECLARE_HANDLE(FARPROC16); 1119 LRESULT WINAPI CallCPLEntry16(HINSTANCE hMod, FARPROC16 pFunc, HWND dw3, UINT dw4, LPARAM dw5, LPARAM dw6) 1120 #endif 1121 { 1122 FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6); 1123 return 0x0deadbee; 1124 } 1125