1 /* Control Panel management
3 * Copyright 2001 Eric Pouech
4 * Copyright 2008 Owen Rudge
5 * Copyright 2022 Raymond Czerny
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.
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.
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
24 #define WIN32_NO_STATUS
29 #define NO_SHLWAPI_REG
34 #include <wine/debug.h>
39 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl
);
43 void Control_UnloadApplet(CPlApplet
* applet
)
47 for (i
= 0; i
< applet
->count
; i
++)
48 applet
->proc(applet
->hWnd
, CPL_STOP
, i
, applet
->info
[i
].data
);
50 if (applet
->proc
) applet
->proc(applet
->hWnd
, CPL_EXIT
, 0L, 0L);
51 FreeLibrary(applet
->hModule
);
53 list_remove( &applet
->entry
);
55 HeapFree(GetProcessHeap(), 0, applet
->cmd
);
56 HeapFree(GetProcessHeap(), 0, applet
);
59 CPlApplet
* Control_LoadApplet(HWND hWnd
, LPCWSTR cmd
, CPanel
* panel
)
62 ACTCTXW ActCtx
= {sizeof(ACTCTX
), ACTCTX_FLAG_RESOURCE_NAME_VALID
};
65 WCHAR fileBuffer
[MAX_PATH
];
73 if (!(applet
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*applet
))))
76 len
= ExpandEnvironmentStringsW(cmd
, NULL
, 0);
79 if (!(applet
->cmd
= HeapAlloc(GetProcessHeap(), 0, (len
+1) * sizeof(WCHAR
))))
81 WARN("Cannot allocate memory for applet path\n");
84 ExpandEnvironmentStringsW(cmd
, applet
->cmd
, len
+1);
88 WARN("Cannot expand applet path\n");
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
);
104 if (!(applet
->hModule
= LoadLibraryW(applet
->cmd
))) {
105 WARN("Cannot load control panel applet %s\n", debugstr_w(applet
->cmd
));
108 if (!(applet
->proc
= (APPLET_PROC
)GetProcAddress(applet
->hModule
, "CPlApplet"))) {
109 WARN("Not a valid control panel applet %s\n", debugstr_w(applet
->cmd
));
112 if (!applet
->proc(hWnd
, CPL_INIT
, 0L, 0L)) {
113 WARN("Init of applet has failed\n");
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);
122 applet
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, applet
,
123 FIELD_OFFSET( CPlApplet
, info
[applet
->count
] ));
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
134 applet
->proc(hWnd
, CPL_INQUIRE
, i
, (LPARAM
)&info
);
135 applet
->info
[i
].data
= info
.lData
;
137 applet
->info
[i
].idIcon
= info
.idIcon
;
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
));
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 */
153 if (!applet
->info
[i
].name
[0]) info
.idName
= CPL_DYNAMIC_RES
;
155 /* zero-length szInfo may not be a buggy applet, but it doesn't hurt for us
158 if (!applet
->info
[i
].info
[0]) info
.idInfo
= CPL_DYNAMIC_RES
;
160 if (applet
->info
[i
].icon
== NULL
)
161 info
.idIcon
= CPL_DYNAMIC_RES
;
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
);
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
;
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
));
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
));
197 DeactivateActCtx(0, cookie
);
201 list_add_head( &panel
->applets
, &applet
->entry
);
207 FreeLibrary(applet
->hModule
);
208 HeapFree(GetProcessHeap(), 0, applet
->cmd
);
209 HeapFree(GetProcessHeap(), 0, applet
);
215 #define IDC_LISTVIEW 1000
216 #define IDC_STATUSBAR 1001
218 #define NUM_COLUMNS 2
219 #define LISTVIEW_DEFSTYLE (WS_CHILD | WS_VISIBLE | WS_TABSTOP |\
220 LVS_SORTASCENDING | LVS_AUTOARRANGE | LVS_SINGLESEL)
222 static BOOL
Control_CreateListView (CPanel
*panel
)
225 WCHAR empty_string
[] = {0};
226 WCHAR buf
[MAX_STRING_LEN
];
229 /* Create list view */
230 GetClientRect(panel
->hWndStatusBar
, &sb
);
231 GetClientRect(panel
->hWnd
, &ws
);
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
);
239 if (!panel
->hWndListView
)
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);
248 SendMessageW(panel
->hWndListView
, LVM_SETIMAGELIST
, LVSIL_SMALL
, (LPARAM
)panel
->hImageListSmall
);
249 SendMessageW(panel
->hWndListView
, LVM_SETIMAGELIST
, LVSIL_NORMAL
, (LPARAM
)panel
->hImageListLarge
);
251 /* Create columns for list view */
252 lvc
.mask
= LVCF_FMT
| LVCF_TEXT
| LVCF_SUBITEM
| LVCF_WIDTH
;
254 lvc
.fmt
= LVCFMT_LEFT
;
258 lvc
.cx
= (ws
.right
- ws
.left
) / 3;
259 LoadStringW(shell32_hInstance
, IDS_CPANEL_NAME
, buf
, sizeof(buf
) / sizeof(buf
[0]));
261 if (ListView_InsertColumnW(panel
->hWndListView
, 0, &lvc
) == -1)
264 /* Description column */
266 lvc
.cx
= ((ws
.right
- ws
.left
) / 3) * 2;
267 LoadStringW(shell32_hInstance
, IDS_CPANEL_DESCRIPTION
, buf
, sizeof(buf
) /
270 if (ListView_InsertColumnW(panel
->hWndListView
, 1, &lvc
) == -1)
276 static void Control_WndProc_Create(HWND hWnd
, const CREATESTRUCTW
* cs
)
278 CPanel
* panel
= cs
->lpCreateParams
;
279 HMENU hMenu
, hSubMenu
;
283 int menucount
, index
;
286 INITCOMMONCONTROLSEX icex
;
290 SetWindowLongPtrW(hWnd
, 0, (LONG_PTR
)panel
);
293 /* Initialise common control DLL */
294 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
295 icex
.dwICC
= ICC_LISTVIEW_CLASSES
| ICC_BAR_CLASSES
;
296 InitCommonControlsEx(&icex
);
298 /* create the status bar */
299 if (!(panel
->hWndStatusBar
= CreateStatusWindowW(WS_CHILD
| WS_VISIBLE
| CCS_BOTTOM
| SBARS_SIZEGRIP
, NULL
, hWnd
, IDC_STATUSBAR
)))
303 SendMessageW(panel
->hWndStatusBar
, SB_SETPARTS
, 1, (LPARAM
) &sb_parts
);
305 /* create the list view */
306 if (!Control_CreateListView(panel
))
309 hMenu
= LoadMenuW(shell32_hInstance
, MAKEINTRESOURCEW(MENU_CPANEL
));
311 /* insert menu items for applets */
312 hSubMenu
= GetSubMenu(hMenu
, 0);
315 LIST_FOR_EACH_ENTRY( applet
, &panel
->applets
, CPlApplet
, entry
)
317 for (i
= 0; i
< applet
->count
; i
++) {
318 /* set up a CPlItem for this particular subprogram */
319 item
= HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem
));
324 item
->applet
= applet
;
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
;
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
);
341 lvItem
.mask
= LVIF_IMAGE
| LVIF_TEXT
| LVIF_PARAM
;
342 lvItem
.iItem
= menucount
;
344 lvItem
.pszText
= applet
->info
[i
].name
;
345 lvItem
.iImage
= index
;
346 lvItem
.lParam
= (LPARAM
) item
;
348 itemidx
= ListView_InsertItemW(panel
->hWndListView
, &lvItem
);
350 /* add the description */
351 ListView_SetItemTextW(panel
->hWndListView
, itemidx
, 1, applet
->info
[i
].info
);
353 /* update menu bar, increment count */
360 panel
->total_subprogs
= menucount
;
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
);
367 SetMenu(hWnd
, hMenu
);
370 static void Control_FreeCPlItems(HWND hWnd
, CPanel
*panel
)
372 HMENU hMenu
, hSubMenu
;
376 /* get the File menu */
377 hMenu
= GetMenu(hWnd
);
382 hSubMenu
= GetSubMenu(hMenu
, 0);
387 /* loop and free the item data */
388 for (i
= IDM_CPANEL_APPLET_BASE
; i
<= IDM_CPANEL_APPLET_BASE
+ panel
->total_subprogs
; i
++)
390 mii
.cbSize
= sizeof(MENUITEMINFOW
);
391 mii
.fMask
= MIIM_DATA
;
393 if (!GetMenuItemInfoW(hSubMenu
, i
, FALSE
, &mii
))
396 HeapFree(GetProcessHeap(), 0, (LPVOID
) mii
.dwItemData
);
400 static void Control_UpdateListViewStyle(CPanel
*panel
, UINT style
, UINT id
)
402 HMENU hMenu
, hSubMenu
;
404 SetWindowLongW(panel
->hWndListView
, GWL_STYLE
, LISTVIEW_DEFSTYLE
| style
);
406 /* update the menu */
407 hMenu
= GetMenu(panel
->hWnd
);
408 hSubMenu
= GetSubMenu(hMenu
, 1);
410 CheckMenuRadioItem(hSubMenu
, FCIDM_SHVIEW_BIGICON
, FCIDM_SHVIEW_REPORTVIEW
,
414 static CPlItem
* Control_GetCPlItem_From_MenuID(HWND hWnd
, UINT id
)
416 HMENU hMenu
, hSubMenu
;
419 /* retrieve the CPlItem structure from the menu item data */
420 hMenu
= GetMenu(hWnd
);
425 hSubMenu
= GetSubMenu(hMenu
, 0);
430 mii
.cbSize
= sizeof(MENUITEMINFOW
);
431 mii
.fMask
= MIIM_DATA
;
433 if (!GetMenuItemInfoW(hSubMenu
, id
, FALSE
, &mii
))
436 return (CPlItem
*) mii
.dwItemData
;
439 static CPlItem
* Control_GetCPlItem_From_ListView(CPanel
*panel
)
444 selitem
= SendMessageW(panel
->hWndListView
, LVM_GETNEXTITEM
, -1, LVNI_FOCUSED
449 lvItem
.iItem
= selitem
;
450 lvItem
.mask
= LVIF_PARAM
;
452 if (SendMessageW(panel
->hWndListView
, LVM_GETITEMW
, 0, (LPARAM
) &lvItem
))
453 return (CPlItem
*) lvItem
.lParam
;
459 static void Control_StartApplet(HWND hWnd
, CPlItem
*item
)
461 WCHAR verbOpen
[] = {'c','p','l','o','p','e','n',0};
462 WCHAR format
[] = {'@','%','d',0};
463 WCHAR param
[MAX_PATH
];
465 /* execute the applet if item is valid */
468 wsprintfW(param
, format
, item
->id
);
469 ShellExecuteW(hWnd
, verbOpen
, item
->applet
->cmd
, param
, NULL
, SW_SHOW
);
473 static LRESULT WINAPI
Control_WndProc(HWND hWnd
, UINT wMsg
,
474 WPARAM lParam1
, LPARAM lParam2
)
476 CPanel
* panel
= (CPanel
*)GetWindowLongPtrW(hWnd
, 0);
478 if (panel
|| wMsg
== WM_CREATE
) {
481 Control_WndProc_Create(hWnd
, (CREATESTRUCTW
*)lParam2
);
485 CPlApplet
*applet
, *next
;
486 LIST_FOR_EACH_ENTRY_SAFE( applet
, next
, &panel
->applets
, CPlApplet
, entry
)
487 Control_UnloadApplet(applet
);
489 Control_FreeCPlItems(hWnd
, panel
);
493 switch (LOWORD(lParam1
))
495 case IDM_CPANEL_EXIT
:
496 SendMessageW(hWnd
, WM_CLOSE
, 0, 0);
499 case IDM_CPANEL_ABOUT
:
501 WCHAR appName
[MAX_STRING_LEN
];
502 HICON icon
= LoadImageW(shell32_hInstance
, MAKEINTRESOURCEW(IDI_SHELL_CONTROL_PANEL
),
503 IMAGE_ICON
, 48, 48, LR_SHARED
);
505 LoadStringW(shell32_hInstance
, IDS_CPANEL_TITLE
, appName
,
506 sizeof(appName
) / sizeof(appName
[0]));
507 ShellAboutW(hWnd
, appName
, NULL
, icon
);
512 case FCIDM_SHVIEW_BIGICON
:
513 Control_UpdateListViewStyle(panel
, LVS_ICON
, FCIDM_SHVIEW_BIGICON
);
516 case FCIDM_SHVIEW_SMALLICON
:
517 Control_UpdateListViewStyle(panel
, LVS_SMALLICON
, FCIDM_SHVIEW_SMALLICON
);
520 case FCIDM_SHVIEW_LISTVIEW
:
521 Control_UpdateListViewStyle(panel
, LVS_LIST
, FCIDM_SHVIEW_LISTVIEW
);
524 case FCIDM_SHVIEW_REPORTVIEW
:
525 Control_UpdateListViewStyle(panel
, LVS_REPORT
, FCIDM_SHVIEW_REPORTVIEW
);
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
))
533 Control_StartApplet(hWnd
, Control_GetCPlItem_From_MenuID(hWnd
, LOWORD(lParam1
)));
544 LPNMHDR nmh
= (LPNMHDR
) lParam2
;
554 Control_StartApplet(hWnd
, Control_GetCPlItem_From_ListView(panel
));
557 case LVN_ITEMCHANGED
:
559 CPlItem
*item
= Control_GetCPlItem_From_ListView(panel
);
561 /* update the status bar if item is valid */
563 SetWindowTextW(panel
->hWndStatusBar
, item
->applet
->info
[item
->id
].info
);
565 SetWindowTextW(panel
->hWndStatusBar
, NULL
);
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
))
582 CPlItem
*item
= Control_GetCPlItem_From_MenuID(hWnd
, LOWORD(lParam1
));
584 /* update the status bar if item is valid */
586 SetWindowTextW(panel
->hWndStatusBar
, item
->applet
->info
[item
->id
].info
);
588 else if ((HIWORD(lParam1
) == 0xFFFF) && (lParam2
== 0))
590 /* reset status bar description to that of the selected icon */
591 CPlItem
*item
= Control_GetCPlItem_From_ListView(panel
);
594 SetWindowTextW(panel
->hWndStatusBar
, item
->applet
->info
[item
->id
].info
);
596 SetWindowTextW(panel
->hWndStatusBar
, NULL
);
601 SetWindowTextW(panel
->hWndStatusBar
, NULL
);
610 hdwp
= BeginDeferWindowPos(2);
615 GetClientRect(panel
->hWndStatusBar
, &sb
);
617 hdwp
= DeferWindowPos(hdwp
, panel
->hWndListView
, NULL
, 0, 0,
618 LOWORD(lParam2
), HIWORD(lParam2
) - (sb
.bottom
- sb
.top
),
619 SWP_NOZORDER
| SWP_NOMOVE
);
624 hdwp
= DeferWindowPos(hdwp
, panel
->hWndStatusBar
, NULL
, 0, 0,
625 LOWORD(lParam2
), LOWORD(lParam1
), SWP_NOZORDER
| SWP_NOMOVE
);
628 EndDeferWindowPos(hdwp
);
635 return DefWindowProcW(hWnd
, wMsg
, lParam1
, lParam2
);
638 static void Control_DoInterface(CPanel
* panel
, HWND hWnd
, HINSTANCE hInst
)
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};
646 LoadStringW(shell32_hInstance
, IDS_CPANEL_TITLE
, appName
, sizeof(appName
) / sizeof(appName
[0]));
648 wc
.cbSize
= sizeof(wc
);
649 wc
.style
= CS_HREDRAW
|CS_VREDRAW
;
650 wc
.lpfnWndProc
= Control_WndProc
;
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
);
662 if (!RegisterClassExW(&wc
)) return;
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;
671 while (GetMessageW(&msg
, panel
->hWnd
, 0, 0)) {
672 TranslateMessage(&msg
);
673 DispatchMessageW(&msg
);
677 static void Control_RegisterRegistryApplets(HWND hWnd
, CPanel
*panel
, HKEY hkey_root
, LPCWSTR szRepPath
)
679 WCHAR name
[MAX_PATH
];
680 WCHAR value
[MAX_PATH
];
683 if (RegOpenKeyW(hkey_root
, szRepPath
, &hkey
) == ERROR_SUCCESS
)
689 DWORD nameLen
= MAX_PATH
;
690 DWORD valueLen
= MAX_PATH
;
692 if (RegEnumValueW(hkey
, idx
, name
, &nameLen
, NULL
, NULL
, (LPBYTE
)value
, &valueLen
) != ERROR_SUCCESS
)
695 Control_LoadApplet(hWnd
, value
, panel
);
701 static void Control_DoWindow(CPanel
* panel
, HWND hWnd
, HINSTANCE hInst
)
703 static const WCHAR wszRegPath
[] = L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls";
706 WCHAR buffer
[MAX_PATH
];
709 /* first add .cpl files in the system directory */
710 GetSystemDirectoryW( buffer
, MAX_PATH
);
711 p
= buffer
+ strlenW(buffer
);
713 lstrcpyW(p
, L
"*.cpl");
715 if ((h
= FindFirstFileW(buffer
, &fd
)) != INVALID_HANDLE_VALUE
) {
717 lstrcpyW(p
, fd
.cFileName
);
718 Control_LoadApplet(hWnd
, buffer
, panel
);
719 } while (FindNextFileW(h
, &fd
));
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
);
727 Control_DoInterface(panel
, hWnd
, hInst
);
731 static void Control_DoWindow(CPanel
* panel
, HWND hWnd
, HINSTANCE hInst
)
736 L
"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}",
744 /** Structure for in and out data when
745 * search for the cpl dialog of first instance
747 typedef struct tagAppDlgFindData
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 */
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
768 AppDlgFindData
* pData
= (AppDlgFindData
*)lParam
;
769 WCHAR szClassName
[256] = L
"";
771 if (pData
->hRunDLL
== hwnd
)
773 // Skip self instance
777 if (GetClassNameW(hwnd
, szClassName
, _countof(szClassName
)))
779 // Note: A comparison on identical is not possible, the class names are different.
780 // ReactOS: 'rundll32_window'
782 // other OS: not checked
783 if (StrStrIW(szClassName
, L
"rundll32") != NULL
)
787 sAppletNo
= (UINT_PTR
)GetPropW(hwnd
, (LPTSTR
)MAKEINTATOM(pData
->aCPLFlags
));
788 if (sAppletNo
== pData
->sAppletNo
)
791 WCHAR szAppFile
[MAX_PATH
];
793 hRes
= GetPropW(hwnd
, (LPTSTR
)MAKEINTATOM(pData
->aCPLName
));
794 GlobalGetAtomNameW((ATOM
)HandleToUlong(hRes
), szAppFile
, _countof(szAppFile
));
795 if (wcscmp(szAppFile
, pData
->szAppFile
) == 0)
797 HWND hDialog
= GetLastActivePopup(hwnd
);
798 if (IsWindow(hDialog
))
800 pData
->hDlgResult
= hDialog
;
801 return FALSE
; // stop enumeration
807 return TRUE
; // continue enumeration
811 * This function makes the system control applet accessible via the taskbar.
814 * Pointer of system control applet.
817 * Number of applet in a system control library.
820 Control_ShowAppletInTaskbar(CPlApplet
* applet
, UINT index
)
822 ITaskbarList
* pTaskbar
= NULL
;
824 SetWindowTextW(applet
->hWnd
, applet
->info
[index
].name
);
825 SendMessageW(applet
->hWnd
, WM_SETICON
, ICON_SMALL
, (LPARAM
)applet
->info
[index
].icon
);
827 /* Add button to the taskbar */
828 ShowWindow(applet
->hWnd
, SW_SHOWMINNOACTIVE
);
830 /* Activate the corresponding button in the taskbar */
832 if (CoCreateInstance(&CLSID_TaskbarList
,
833 NULL
, CLSCTX_INPROC_SERVER
,
835 (LPVOID
*)&pTaskbar
) == S_OK
)
837 if (ITaskbarList_HrInit(pTaskbar
) == S_OK
)
839 ITaskbarList_ActivateTab(pTaskbar
, applet
->hWnd
);
841 ITaskbarList_Release(pTaskbar
);
846 #endif /* __REACTOS__ */
848 static void Control_DoLaunch(CPanel
* panel
, HWND hWnd
, LPCWSTR wszCmd
)
864 LPWSTR extraPmtsBuf
= NULL
;
865 LPWSTR extraPmts
= NULL
;
869 buffer
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd
) + 1) * sizeof(*wszCmd
));
872 end
= lstrcpyW(buffer
, wszCmd
);
876 if (ch
== '"') quoted
= !quoted
;
877 if (!quoted
&& (ch
== ' ' || ch
== ',' || ch
== '\0')) {
882 } else if (*beg
== '\0') {
888 if (ch
== '\0') break;
890 if (ch
== ' ') while (end
[1] == ' ') end
++;
894 while ((ptr
= StrChrW(buffer
, '"')))
895 memmove(ptr
, ptr
+1, lstrlenW(ptr
)*sizeof(WCHAR
));
897 /* now check for any quotes in extraPmtsBuf and remove */
898 if (extraPmtsBuf
!= NULL
)
900 beg
= end
= extraPmtsBuf
;
905 if (ch
== '"') quoted
= !quoted
;
906 if (!quoted
&& (ch
== ' ' || ch
== ',' || ch
== '\0')) {
913 if (ch
== '\0') break;
915 if (ch
== ' ') while (end
[1] == ' ') end
++;
920 while ((ptr
= StrChrW(extraPmts
, '"')))
921 memmove(ptr
, ptr
+1, lstrlenW(ptr
)*sizeof(WCHAR
));
923 if (extraPmts
== NULL
)
924 extraPmts
= extraPmtsBuf
;
927 /* Now check if there had been a numerical value in the extra params */
928 if ((extraPmts
) && (*extraPmts
== '@') && (sp
== -1)) {
929 sp
= atoiW(extraPmts
+ 1);
932 TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer
), debugstr_w(extraPmts
), sp
);
934 applet
= Control_LoadApplet(hWnd
, buffer
, panel
);
943 AppDlgFindData findData
;
945 /* we've been given a textual parameter (or none at all) */
947 while ((++sp
) != applet
->count
) {
948 TRACE("sp %d, name %s\n", sp
, debugstr_w(applet
->info
[sp
].name
));
950 if (StrCmpIW(extraPmts
, applet
->info
[sp
].name
) == 0)
955 if (sp
>= applet
->count
) {
956 WARN("Out of bounds (%u >= %u), setting to 0\n", sp
, applet
->count
);
961 bActivated
= (applet
->hActCtx
!= INVALID_HANDLE_VALUE
? ActivateActCtx(applet
->hActCtx
, &cookie
) : FALSE
);
963 aCPLPath
= GlobalFindAtomW(applet
->cmd
);
966 aCPLPath
= GlobalAddAtomW(applet
->cmd
);
969 aCPLName
= GlobalFindAtomW(L
"CPLName");
972 aCPLName
= GlobalAddAtomW(L
"CPLName");
975 aCPLFlags
= GlobalFindAtomW(L
"CPLFlags");
978 aCPLFlags
= GlobalAddAtomW(L
"CPLFlags");
981 findData
.szAppFile
= applet
->cmd
;
982 findData
.sAppletNo
= (UINT_PTR
)(sp
+ 1);
983 findData
.aCPLName
= aCPLName
;
984 findData
.aCPLFlags
= aCPLFlags
;
985 findData
.hRunDLL
= applet
->hWnd
;
986 findData
.hDlgResult
= NULL
;
987 // Find the dialog of this applet in the first instance.
988 // Note: The simpler functions "FindWindow" or "FindWindowEx" does not find this type of dialogs.
989 EnumWindows(Control_EnumWinProc
, (LPARAM
)&findData
);
990 if (findData
.hDlgResult
)
992 BringWindowToTop(findData
.hDlgResult
);
996 SetPropW(applet
->hWnd
, (LPTSTR
)MAKEINTATOM(aCPLName
), (HANDLE
)MAKEINTATOM(aCPLPath
));
997 SetPropW(applet
->hWnd
, (LPTSTR
)MAKEINTATOM(aCPLFlags
), UlongToHandle(sp
+ 1));
998 Control_ShowAppletInTaskbar(applet
, sp
);
1001 if (!applet
->proc(applet
->hWnd
, CPL_STARTWPARMSW
, sp
, (LPARAM
)extraPmts
))
1002 applet
->proc(applet
->hWnd
, CPL_DBLCLK
, sp
, applet
->info
[sp
].data
);
1004 RemovePropW(applet
->hWnd
, applet
->cmd
);
1005 GlobalDeleteAtom(aCPLPath
);
1009 Control_UnloadApplet(applet
);
1013 DeactivateActCtx(0, cookie
);
1018 HeapFree(GetProcessHeap(), 0, buffer
);
1021 /*************************************************************************
1022 * Control_RunDLLW [SHELL32.@]
1025 void WINAPI
Control_RunDLLW(HWND hWnd
, HINSTANCE hInst
, LPCWSTR cmd
, DWORD nCmdShow
)
1029 TRACE("(%p, %p, %s, 0x%08x)\n",
1030 hWnd
, hInst
, debugstr_w(cmd
), nCmdShow
);
1033 memset(&panel
, 0, sizeof(panel
));
1034 list_init( &panel
.applets
);
1037 if (!cmd
|| !*cmd
) {
1038 Control_DoWindow(&panel
, hWnd
, hInst
);
1040 Control_DoLaunch(&panel
, hWnd
, cmd
);
1044 /*************************************************************************
1045 * Control_RunDLLA [SHELL32.@]
1048 void WINAPI
Control_RunDLLA(HWND hWnd
, HINSTANCE hInst
, LPCSTR cmd
, DWORD nCmdShow
)
1050 DWORD len
= MultiByteToWideChar(CP_ACP
, 0, cmd
, -1, NULL
, 0 );
1051 LPWSTR wszCmd
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
1052 if (wszCmd
&& MultiByteToWideChar(CP_ACP
, 0, cmd
, -1, wszCmd
, len
))
1054 Control_RunDLLW(hWnd
, hInst
, wszCmd
, nCmdShow
);
1056 HeapFree(GetProcessHeap(), 0, wszCmd
);
1059 /*************************************************************************
1060 * Control_FillCache_RunDLLW [SHELL32.@]
1063 HRESULT WINAPI
Control_FillCache_RunDLLW(HWND hWnd
, HANDLE hModule
, DWORD w
, DWORD x
)
1065 FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd
, hModule
, w
, x
);
1069 /*************************************************************************
1070 * Control_FillCache_RunDLLA [SHELL32.@]
1073 HRESULT WINAPI
Control_FillCache_RunDLLA(HWND hWnd
, HANDLE hModule
, DWORD w
, DWORD x
)
1075 return Control_FillCache_RunDLLW(hWnd
, hModule
, w
, x
);
1080 /*************************************************************************
1081 * RunDll_CallEntry16 [SHELL32.122]
1082 * the name is OK (when written with Dll, and not DLL as in Wine!)
1084 void WINAPI
RunDll_CallEntry16( DWORD proc
, HWND hwnd
, HINSTANCE inst
,
1085 LPCSTR cmdline
, INT cmdshow
)
1087 FIXME( "proc %lx hwnd %p inst %p cmdline %s cmdshow %d\n",
1088 proc
, hwnd
, inst
, debugstr_a(cmdline
), cmdshow
);
1092 /*************************************************************************
1093 * CallCPLEntry16 [SHELL32.166]
1095 * called by desk.cpl on "Advanced" with:
1096 * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
1100 DWORD WINAPI
CallCPLEntry16(HMODULE hMod
, FARPROC pFunc
, DWORD dw3
, DWORD dw4
, DWORD dw5
, DWORD dw6
)
1102 DECLARE_HANDLE(FARPROC16
);
1103 LRESULT WINAPI
CallCPLEntry16(HINSTANCE hMod
, FARPROC16 pFunc
, HWND dw3
, UINT dw4
, LPARAM dw5
, LPARAM dw6
)
1106 FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod
, pFunc
, dw3
, dw4
, dw5
, dw6
);