1 /* Control Panel management
3 * Copyright 2001 Eric Pouech
4 * Copyright 2008 Owen Rudge
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/winbase16.h"
34 #include "wine/debug.h"
36 #include "wine/unicode.h"
38 #define NO_SHLWAPI_REG
43 #include "shell32_main.h"
45 #define MAX_STRING_LEN 1024
47 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl
);
49 CPlApplet
* Control_UnloadApplet(CPlApplet
* applet
)
54 for (i
= 0; i
< applet
->count
; i
++) {
55 if (!applet
->info
[i
].dwSize
) continue;
56 applet
->proc(applet
->hWnd
, CPL_STOP
, i
, applet
->info
[i
].lData
);
58 if (applet
->proc
) applet
->proc(applet
->hWnd
, CPL_EXIT
, 0L, 0L);
59 FreeLibrary(applet
->hModule
);
61 HeapFree(GetProcessHeap(), 0, applet
);
65 CPlApplet
* Control_LoadApplet(HWND hWnd
, LPCWSTR cmd
, CPanel
* panel
)
72 if (!(applet
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*applet
))))
77 if (!(applet
->hModule
= LoadLibraryW(cmd
))) {
78 WARN("Cannot load control panel applet %s\n", debugstr_w(cmd
));
81 if (!(applet
->proc
= (APPLET_PROC
)GetProcAddress(applet
->hModule
, "CPlApplet"))) {
82 WARN("Not a valid control panel applet %s\n", debugstr_w(cmd
));
85 if (!applet
->proc(hWnd
, CPL_INIT
, 0L, 0L)) {
86 WARN("Init of applet has failed\n");
89 if ((applet
->count
= applet
->proc(hWnd
, CPL_GETCOUNT
, 0L, 0L)) == 0) {
90 WARN("No subprogram in applet\n");
94 applet
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, applet
,
95 sizeof(*applet
) + (applet
->count
- 1) * sizeof(NEWCPLINFOW
));
97 for (i
= 0; i
< applet
->count
; i
++) {
98 ZeroMemory(&newinfo
, sizeof(newinfo
));
99 newinfo
.dwSize
= sizeof(NEWCPLINFOA
);
100 applet
->info
[i
].dwSize
= sizeof(NEWCPLINFOW
);
101 applet
->info
[i
].dwFlags
= 0;
102 applet
->info
[i
].dwHelpContext
= 0;
103 applet
->info
[i
].szHelpFile
[0] = '\0';
104 /* proc is supposed to return a null value upon success for
105 * CPL_INQUIRE and CPL_NEWINQUIRE
106 * However, real drivers don't seem to behave like this
107 * So, use introspection rather than return value
109 applet
->proc(hWnd
, CPL_INQUIRE
, i
, (LPARAM
)&info
);
110 applet
->info
[i
].lData
= info
.lData
;
111 if (info
.idIcon
!= CPL_DYNAMIC_RES
)
112 applet
->info
[i
].hIcon
= LoadIconW(applet
->hModule
,
113 MAKEINTRESOURCEW(info
.idIcon
));
114 if (info
.idName
!= CPL_DYNAMIC_RES
)
115 LoadStringW(applet
->hModule
, info
.idName
,
116 applet
->info
[i
].szName
, sizeof(applet
->info
[i
].szName
) / sizeof(WCHAR
));
117 if (info
.idInfo
!= CPL_DYNAMIC_RES
)
118 LoadStringW(applet
->hModule
, info
.idInfo
,
119 applet
->info
[i
].szInfo
, sizeof(applet
->info
[i
].szInfo
) / sizeof(WCHAR
));
121 if ((info
.idIcon
== CPL_DYNAMIC_RES
) || (info
.idName
== CPL_DYNAMIC_RES
) ||
122 (info
.idInfo
== CPL_DYNAMIC_RES
)) {
123 applet
->proc(hWnd
, CPL_NEWINQUIRE
, i
, (LPARAM
)&newinfo
);
125 applet
->info
[i
].dwFlags
= newinfo
.dwFlags
;
126 applet
->info
[i
].dwHelpContext
= newinfo
.dwHelpContext
;
127 applet
->info
[i
].lData
= newinfo
.lData
;
128 if (info
.idIcon
== CPL_DYNAMIC_RES
) {
129 if (!newinfo
.hIcon
) WARN("couldn't get icon for applet %u\n", i
);
130 applet
->info
[i
].hIcon
= newinfo
.hIcon
;
132 if (newinfo
.dwSize
== sizeof(NEWCPLINFOW
)) {
133 if (info
.idName
== CPL_DYNAMIC_RES
)
134 memcpy(applet
->info
[i
].szName
, newinfo
.szName
, sizeof(newinfo
.szName
));
135 if (info
.idInfo
== CPL_DYNAMIC_RES
)
136 memcpy(applet
->info
[i
].szInfo
, newinfo
.szInfo
, sizeof(newinfo
.szInfo
));
137 memcpy(applet
->info
[i
].szHelpFile
, newinfo
.szHelpFile
, sizeof(newinfo
.szHelpFile
));
139 if (info
.idName
== CPL_DYNAMIC_RES
)
140 MultiByteToWideChar(CP_ACP
, 0, ((LPNEWCPLINFOA
)&newinfo
)->szName
,
141 sizeof(((LPNEWCPLINFOA
)&newinfo
)->szName
) / sizeof(CHAR
),
142 applet
->info
[i
].szName
,
143 sizeof(applet
->info
[i
].szName
) / sizeof(WCHAR
));
144 if (info
.idInfo
== CPL_DYNAMIC_RES
)
145 MultiByteToWideChar(CP_ACP
, 0, ((LPNEWCPLINFOA
)&newinfo
)->szInfo
,
146 sizeof(((LPNEWCPLINFOA
)&newinfo
)->szInfo
) / sizeof(CHAR
),
147 applet
->info
[i
].szInfo
,
148 sizeof(applet
->info
[i
].szInfo
) / sizeof(WCHAR
));
149 MultiByteToWideChar(CP_ACP
, 0, ((LPNEWCPLINFOA
)&newinfo
)->szHelpFile
,
150 sizeof(((LPNEWCPLINFOA
)&newinfo
)->szHelpFile
) / sizeof(CHAR
),
151 applet
->info
[i
].szHelpFile
,
152 sizeof(applet
->info
[i
].szHelpFile
) / sizeof(WCHAR
));
157 applet
->next
= panel
->first
;
158 panel
->first
= applet
;
163 Control_UnloadApplet(applet
);
167 static void Control_WndProc_Create(HWND hWnd
, const CREATESTRUCTW
* cs
)
169 CPanel
* panel
= (CPanel
*)cs
->lpCreateParams
;
170 HMENU hMenu
, hSubMenu
;
176 SetWindowLongPtrW(hWnd
, 0, (LONG_PTR
)panel
);
179 hMenu
= LoadMenuW(shell32_hInstance
, MAKEINTRESOURCEW(MENU_CPANEL
));
181 /* insert menu items for applets */
182 hSubMenu
= GetSubMenu(hMenu
, 0);
185 for (applet
= panel
->first
; applet
; applet
= applet
->next
) {
186 for (i
= 0; i
< applet
->count
; i
++) {
187 if (!applet
->info
[i
].dwSize
)
190 /* set up a CPlItem for this particular subprogram */
191 item
= HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem
));
196 item
->applet
= (CPlApplet
*) applet
;
199 mii
.cbSize
= sizeof(MENUITEMINFOW
);
200 mii
.fMask
= MIIM_ID
| MIIM_STRING
| MIIM_DATA
;
201 mii
.dwTypeData
= applet
->info
[i
].szName
;
202 mii
.cch
= sizeof(applet
->info
[i
].szName
) / sizeof(applet
->info
[i
].szName
[0]);
203 mii
.wID
= IDM_CPANEL_APPLET_BASE
+ menucount
;
204 mii
.dwItemData
= (DWORD
) item
;
206 if (InsertMenuItemW(hSubMenu
, menucount
, TRUE
, &mii
)) {
213 panel
->total_subprogs
= menucount
;
215 /* check the "large items" icon in the View menu */
216 hSubMenu
= GetSubMenu(hMenu
, 1);
217 CheckMenuRadioItem(hSubMenu
, FCIDM_SHVIEW_BIGICON
, FCIDM_SHVIEW_REPORTVIEW
,
218 FCIDM_SHVIEW_BIGICON
, MF_BYCOMMAND
);
220 SetMenu(hWnd
, hMenu
);
223 static void Control_FreeCPlItems(HWND hWnd
, CPanel
*panel
)
225 HMENU hMenu
, hSubMenu
;
229 /* get the File menu */
230 hMenu
= GetMenu(hWnd
);
235 hSubMenu
= GetSubMenu(hMenu
, 0);
240 /* loop and free the item data */
241 for (i
= IDM_CPANEL_APPLET_BASE
; i
<= IDM_CPANEL_APPLET_BASE
+ panel
->total_subprogs
; i
++)
243 mii
.cbSize
= sizeof(MENUITEMINFOW
);
244 mii
.fMask
= MIIM_DATA
;
246 if (!GetMenuItemInfoW(hSubMenu
, i
, FALSE
, &mii
))
249 HeapFree(GetProcessHeap(), 0, (LPVOID
) mii
.dwItemData
);
253 static LRESULT WINAPI
Control_WndProc(HWND hWnd
, UINT wMsg
,
254 WPARAM lParam1
, LPARAM lParam2
)
256 CPanel
* panel
= (CPanel
*)GetWindowLongPtrW(hWnd
, 0);
258 if (panel
|| wMsg
== WM_CREATE
) {
261 Control_WndProc_Create(hWnd
, (CREATESTRUCTW
*)lParam2
);
265 CPlApplet
* applet
= panel
->first
;
267 applet
= Control_UnloadApplet(applet
);
269 Control_FreeCPlItems(hWnd
, panel
);
273 switch (LOWORD(lParam1
))
275 case IDM_CPANEL_EXIT
:
276 SendMessageW(hWnd
, WM_CLOSE
, 0, 0);
279 case IDM_CPANEL_ABOUT
:
281 WCHAR appName
[MAX_STRING_LEN
];
283 LoadStringW(shell32_hInstance
, IDS_CPANEL_TITLE
, appName
,
284 sizeof(appName
) / sizeof(appName
[0]));
285 ShellAboutW(hWnd
, appName
, NULL
, NULL
);
290 case FCIDM_SHVIEW_BIGICON
:
291 case FCIDM_SHVIEW_SMALLICON
:
292 case FCIDM_SHVIEW_LISTVIEW
:
293 case FCIDM_SHVIEW_REPORTVIEW
:
297 /* check if this is an applet */
298 if ((LOWORD(lParam1
) >= IDM_CPANEL_APPLET_BASE
) &&
299 (LOWORD(lParam1
) <= IDM_CPANEL_APPLET_BASE
+ panel
->total_subprogs
))
302 HMENU hMenu
, hSubMenu
;
305 /* retrieve the CPlItem structure from the menu item data */
306 hMenu
= GetMenu(hWnd
);
311 hSubMenu
= GetSubMenu(hMenu
, 0);
316 mii
.cbSize
= sizeof(MENUITEMINFOW
);
317 mii
.fMask
= MIIM_DATA
;
319 if (!GetMenuItemInfoW(hSubMenu
, LOWORD(lParam1
), FALSE
, &mii
))
322 item
= (CPlItem
*) mii
.dwItemData
;
324 /* execute the applet if item is valid */
326 item
->applet
->proc(item
->applet
->hWnd
, CPL_DBLCLK
, item
->id
,
327 item
->applet
->info
[item
->id
].lData
);
339 return DefWindowProcW(hWnd
, wMsg
, lParam1
, lParam2
);
342 static void Control_DoInterface(CPanel
* panel
, HWND hWnd
, HINSTANCE hInst
)
346 WCHAR appName
[MAX_STRING_LEN
];
347 const WCHAR className
[] = {'S','h','e','l','l','_','C','o','n','t','r','o',
348 'l','_','W','n','d','C','l','a','s','s',0};
350 LoadStringW(shell32_hInstance
, IDS_CPANEL_TITLE
, appName
, sizeof(appName
) / sizeof(appName
[0]));
352 wc
.style
= CS_HREDRAW
|CS_VREDRAW
;
353 wc
.lpfnWndProc
= Control_WndProc
;
355 wc
.cbWndExtra
= sizeof(CPlApplet
*);
356 wc
.hInstance
= hInst
;
358 wc
.hCursor
= LoadCursorW( 0, (LPWSTR
)IDC_ARROW
);
359 wc
.hbrBackground
= GetStockObject(WHITE_BRUSH
);
360 wc
.lpszMenuName
= NULL
;
361 wc
.lpszClassName
= className
;
363 if (!RegisterClassW(&wc
)) return;
365 CreateWindowExW(0, wc
.lpszClassName
, appName
,
366 WS_OVERLAPPEDWINDOW
| WS_VISIBLE
,
367 CW_USEDEFAULT
, CW_USEDEFAULT
,
368 CW_USEDEFAULT
, CW_USEDEFAULT
,
369 hWnd
, NULL
, hInst
, panel
);
370 if (!panel
->hWnd
) return;
372 while (GetMessageW(&msg
, panel
->hWnd
, 0, 0)) {
373 TranslateMessage(&msg
);
374 DispatchMessageW(&msg
);
378 static void Control_RegisterRegistryApplets(HWND hWnd
, CPanel
*panel
, HKEY hkey_root
, LPCWSTR szRepPath
)
380 WCHAR name
[MAX_PATH
];
381 WCHAR value
[MAX_PATH
];
384 if (RegOpenKeyW(hkey_root
, szRepPath
, &hkey
) == ERROR_SUCCESS
)
390 DWORD nameLen
= MAX_PATH
;
391 DWORD valueLen
= MAX_PATH
;
393 if (RegEnumValueW(hkey
, idx
, name
, &nameLen
, NULL
, NULL
, (LPBYTE
)value
, &valueLen
) != ERROR_SUCCESS
)
396 Control_LoadApplet(hWnd
, value
, panel
);
402 static void Control_DoWindow(CPanel
* panel
, HWND hWnd
, HINSTANCE hInst
)
406 WCHAR buffer
[MAX_PATH
];
407 static const WCHAR wszAllCpl
[] = {'*','.','c','p','l',0};
408 static const WCHAR wszRegPath
[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t',
409 '\\','W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
410 '\\','C','o','n','t','r','o','l',' ','P','a','n','e','l','\\','C','p','l','s',0};
413 /* first add .cpl files in the system directory */
414 GetSystemDirectoryW( buffer
, MAX_PATH
);
415 p
= buffer
+ strlenW(buffer
);
417 lstrcpyW(p
, wszAllCpl
);
419 if ((h
= FindFirstFileW(buffer
, &fd
)) != INVALID_HANDLE_VALUE
) {
421 lstrcpyW(p
, fd
.cFileName
);
422 Control_LoadApplet(hWnd
, buffer
, panel
);
423 } while (FindNextFileW(h
, &fd
));
427 /* now check for cpls in the registry */
428 Control_RegisterRegistryApplets(hWnd
, panel
, HKEY_LOCAL_MACHINE
, wszRegPath
);
429 Control_RegisterRegistryApplets(hWnd
, panel
, HKEY_CURRENT_USER
, wszRegPath
);
431 Control_DoInterface(panel
, hWnd
, hInst
);
434 static void Control_DoLaunch(CPanel
* panel
, HWND hWnd
, LPCWSTR wszCmd
)
450 LPWSTR extraPmtsBuf
= NULL
;
451 LPWSTR extraPmts
= NULL
;
454 buffer
= HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd
) + 1) * sizeof(*wszCmd
));
457 end
= lstrcpyW(buffer
, wszCmd
);
461 if (ch
== '"') quoted
= !quoted
;
462 if (!quoted
&& (ch
== ' ' || ch
== ',' || ch
== '\0')) {
467 } else if (*beg
== '\0') {
473 if (ch
== '\0') break;
475 if (ch
== ' ') while (end
[1] == ' ') end
++;
479 while ((ptr
= StrChrW(buffer
, '"')))
480 memmove(ptr
, ptr
+1, lstrlenW(ptr
)*sizeof(WCHAR
));
482 /* now check for any quotes in extraPmtsBuf and remove */
483 if (extraPmtsBuf
!= NULL
)
485 beg
= end
= extraPmtsBuf
;
490 if (ch
== '"') quoted
= !quoted
;
491 if (!quoted
&& (ch
== ' ' || ch
== ',' || ch
== '\0')) {
498 if (ch
== '\0') break;
500 if (ch
== ' ') while (end
[1] == ' ') end
++;
505 while ((ptr
= StrChrW(extraPmts
, '"')))
506 memmove(ptr
, ptr
+1, lstrlenW(ptr
)*sizeof(WCHAR
));
508 if (extraPmts
== NULL
)
509 extraPmts
= extraPmtsBuf
;
512 TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer
), debugstr_w(extraPmts
), sp
);
514 Control_LoadApplet(hWnd
, buffer
, panel
);
517 CPlApplet
* applet
= panel
->first
;
519 assert(applet
&& applet
->next
== NULL
);
521 /* we've been given a textual parameter (or none at all) */
523 while ((++sp
) != applet
->count
) {
524 if (applet
->info
[sp
].dwSize
) {
525 TRACE("sp %d, name %s\n", sp
, debugstr_w(applet
->info
[sp
].szName
));
527 if (StrCmpIW(extraPmts
, applet
->info
[sp
].szName
) == 0)
533 if (sp
>= applet
->count
) {
534 WARN("Out of bounds (%u >= %u), setting to 0\n", sp
, applet
->count
);
538 if (applet
->info
[sp
].dwSize
) {
539 if (!applet
->proc(applet
->hWnd
, CPL_STARTWPARMSA
, sp
, (LPARAM
)extraPmts
))
540 applet
->proc(applet
->hWnd
, CPL_DBLCLK
, sp
, applet
->info
[sp
].lData
);
543 Control_UnloadApplet(applet
);
546 HeapFree(GetProcessHeap(), 0, buffer
);
549 /*************************************************************************
550 * Control_RunDLLW [SHELL32.@]
553 void WINAPI
Control_RunDLLW(HWND hWnd
, HINSTANCE hInst
, LPCWSTR cmd
, DWORD nCmdShow
)
557 TRACE("(%p, %p, %s, 0x%08x)\n",
558 hWnd
, hInst
, debugstr_w(cmd
), nCmdShow
);
560 memset(&panel
, 0, sizeof(panel
));
563 Control_DoWindow(&panel
, hWnd
, hInst
);
565 Control_DoLaunch(&panel
, hWnd
, cmd
);
569 /*************************************************************************
570 * Control_RunDLLA [SHELL32.@]
573 void WINAPI
Control_RunDLLA(HWND hWnd
, HINSTANCE hInst
, LPCSTR cmd
, DWORD nCmdShow
)
575 DWORD len
= MultiByteToWideChar(CP_ACP
, 0, cmd
, -1, NULL
, 0 );
576 LPWSTR wszCmd
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
577 if (wszCmd
&& MultiByteToWideChar(CP_ACP
, 0, cmd
, -1, wszCmd
, len
))
579 Control_RunDLLW(hWnd
, hInst
, wszCmd
, nCmdShow
);
581 HeapFree(GetProcessHeap(), 0, wszCmd
);
584 /*************************************************************************
585 * Control_FillCache_RunDLLW [SHELL32.@]
588 HRESULT WINAPI
Control_FillCache_RunDLLW(HWND hWnd
, HANDLE hModule
, DWORD w
, DWORD x
)
590 FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd
, hModule
, w
, x
);
594 /*************************************************************************
595 * Control_FillCache_RunDLLA [SHELL32.@]
598 HRESULT WINAPI
Control_FillCache_RunDLLA(HWND hWnd
, HANDLE hModule
, DWORD w
, DWORD x
)
600 return Control_FillCache_RunDLLW(hWnd
, hModule
, w
, x
);
604 /*************************************************************************
605 * RunDLL_CallEntry16 [SHELL32.122]
606 * the name is probably wrong
608 void WINAPI
RunDLL_CallEntry16( DWORD proc
, HWND hwnd
, HINSTANCE inst
,
609 LPCSTR cmdline
, INT cmdshow
)
614 TRACE( "proc %x hwnd %p inst %p cmdline %s cmdshow %d\n",
615 proc
, hwnd
, inst
, debugstr_a(cmdline
), cmdshow
);
617 cmdline_seg
= MapLS( cmdline
);
618 args
[4] = HWND_16(hwnd
);
619 args
[3] = MapHModuleLS(inst
);
620 args
[2] = SELECTOROF(cmdline_seg
);
621 args
[1] = OFFSETOF(cmdline_seg
);
623 WOWCallback16Ex( proc
, WCB16_PASCAL
, sizeof(args
), args
, NULL
);
624 UnMapLS( cmdline_seg
);
627 /*************************************************************************
628 * CallCPLEntry16 [SHELL32.166]
630 * called by desk.cpl on "Advanced" with:
631 * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
634 DWORD WINAPI
CallCPLEntry16(HMODULE hMod
, FARPROC pFunc
, DWORD dw3
, DWORD dw4
, DWORD dw5
, DWORD dw6
)
636 FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod
, pFunc
, dw3
, dw4
, dw5
, dw6
);