shell32: Define a valid cursor for the control panel window.
[wine/multimedia.git] / dlls / shell32 / control.c
blob58516bb0d0d7870ee4082b1c1603abd72524135b
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
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winreg.h"
32 #include "wine/winbase16.h"
33 #include "wownt32.h"
34 #include "wine/debug.h"
35 #include "cpl.h"
36 #include "wine/unicode.h"
38 #define NO_SHLWAPI_REG
39 #include "shlwapi.h"
41 #include "cpanel.h"
42 #include "shresdef.h"
43 #include "shell32_main.h"
45 #define MAX_STRING_LEN 1024
47 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
49 CPlApplet* Control_UnloadApplet(CPlApplet* applet)
51 unsigned i;
52 CPlApplet* next;
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);
60 next = applet->next;
61 HeapFree(GetProcessHeap(), 0, applet);
62 return next;
65 CPlApplet* Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
67 CPlApplet* applet;
68 unsigned i;
69 CPLINFO info;
70 NEWCPLINFOW newinfo;
72 if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
73 return applet;
75 applet->hWnd = hWnd;
77 if (!(applet->hModule = LoadLibraryW(cmd))) {
78 WARN("Cannot load control panel applet %s\n", debugstr_w(cmd));
79 goto theError;
81 if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
82 WARN("Not a valid control panel applet %s\n", debugstr_w(cmd));
83 goto theError;
85 if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) {
86 WARN("Init of applet has failed\n");
87 goto theError;
89 if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) {
90 WARN("No subprogram in applet\n");
91 goto theError;
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));
138 } else {
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;
160 return applet;
162 theError:
163 Control_UnloadApplet(applet);
164 return NULL;
167 static void Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
169 CPanel* panel = (CPanel*)cs->lpCreateParams;
170 HMENU hMenu, hSubMenu;
171 CPlApplet* applet;
172 MENUITEMINFOW mii;
173 int menucount, i;
174 CPlItem *item;
176 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
177 panel->hWnd = hWnd;
179 hMenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(MENU_CPANEL));
181 /* insert menu items for applets */
182 hSubMenu = GetSubMenu(hMenu, 0);
183 menucount = 0;
185 for (applet = panel->first; applet; applet = applet->next) {
186 for (i = 0; i < applet->count; i++) {
187 if (!applet->info[i].dwSize)
188 continue;
190 /* set up a CPlItem for this particular subprogram */
191 item = HeapAlloc(GetProcessHeap(), 0, sizeof(CPlItem));
193 if (!item)
194 continue;
196 item->applet = (CPlApplet *) applet;
197 item->id = i;
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)) {
207 DrawMenuBar(hWnd);
208 menucount++;
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;
226 MENUITEMINFOW mii;
227 int i;
229 /* get the File menu */
230 hMenu = GetMenu(hWnd);
232 if (!hMenu)
233 return;
235 hSubMenu = GetSubMenu(hMenu, 0);
237 if (!hSubMenu)
238 return;
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))
247 continue;
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) {
259 switch (wMsg) {
260 case WM_CREATE:
261 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
262 return 0;
263 case WM_DESTROY:
265 CPlApplet* applet = panel->first;
266 while (applet)
267 applet = Control_UnloadApplet(applet);
269 Control_FreeCPlItems(hWnd, panel);
270 PostQuitMessage(0);
271 break;
272 case WM_COMMAND:
273 switch (LOWORD(lParam1))
275 case IDM_CPANEL_EXIT:
276 SendMessageW(hWnd, WM_CLOSE, 0, 0);
277 return 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);
287 return 0;
290 case FCIDM_SHVIEW_BIGICON:
291 case FCIDM_SHVIEW_SMALLICON:
292 case FCIDM_SHVIEW_LISTVIEW:
293 case FCIDM_SHVIEW_REPORTVIEW:
294 return 0;
296 default:
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))
301 CPlItem *item;
302 HMENU hMenu, hSubMenu;
303 MENUITEMINFOW mii;
305 /* retrieve the CPlItem structure from the menu item data */
306 hMenu = GetMenu(hWnd);
308 if (!hMenu)
309 break;
311 hSubMenu = GetSubMenu(hMenu, 0);
313 if (!hSubMenu)
314 break;
316 mii.cbSize = sizeof(MENUITEMINFOW);
317 mii.fMask = MIIM_DATA;
319 if (!GetMenuItemInfoW(hSubMenu, LOWORD(lParam1), FALSE, &mii))
320 break;
322 item = (CPlItem *) mii.dwItemData;
324 /* execute the applet if item is valid */
325 if (item)
326 item->applet->proc(item->applet->hWnd, CPL_DBLCLK, item->id,
327 item->applet->info[item->id].lData);
329 return 0;
332 break;
335 break;
339 return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
342 static void Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
344 WNDCLASSW wc;
345 MSG msg;
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;
354 wc.cbClsExtra = 0;
355 wc.cbWndExtra = sizeof(CPlApplet*);
356 wc.hInstance = hInst;
357 wc.hIcon = 0;
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];
382 HKEY hkey;
384 if (RegOpenKeyW(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
386 int idx = 0;
388 for(;; ++idx)
390 DWORD nameLen = MAX_PATH;
391 DWORD valueLen = MAX_PATH;
393 if (RegEnumValueW(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)value, &valueLen) != ERROR_SUCCESS)
394 break;
396 Control_LoadApplet(hWnd, value, panel);
398 RegCloseKey(hkey);
402 static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
404 HANDLE h;
405 WIN32_FIND_DATAW fd;
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};
411 WCHAR *p;
413 /* first add .cpl files in the system directory */
414 GetSystemDirectoryW( buffer, MAX_PATH );
415 p = buffer + strlenW(buffer);
416 *p++ = '\\';
417 lstrcpyW(p, wszAllCpl);
419 if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) {
420 do {
421 lstrcpyW(p, fd.cFileName);
422 Control_LoadApplet(hWnd, buffer, panel);
423 } while (FindNextFileW(h, &fd));
424 FindClose(h);
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)
435 /* forms to parse:
436 * foo.cpl,@sp,str
437 * foo.cpl,@sp
438 * foo.cpl,,str
439 * foo.cpl @sp
440 * foo.cpl str
441 * "a path\foo.cpl"
444 LPWSTR buffer;
445 LPWSTR beg = NULL;
446 LPWSTR end;
447 WCHAR ch;
448 LPWSTR ptr;
449 signed sp = -1;
450 LPWSTR extraPmtsBuf = NULL;
451 LPWSTR extraPmts = NULL;
452 int quoted = 0;
454 buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd));
455 if (!buffer) return;
457 end = lstrcpyW(buffer, wszCmd);
459 for (;;) {
460 ch = *end;
461 if (ch == '"') quoted = !quoted;
462 if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
463 *end = '\0';
464 if (beg) {
465 if (*beg == '@') {
466 sp = atoiW(beg + 1);
467 } else if (*beg == '\0') {
468 sp = -1;
469 } else {
470 extraPmtsBuf = beg;
473 if (ch == '\0') break;
474 beg = end + 1;
475 if (ch == ' ') while (end[1] == ' ') end++;
477 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;
486 quoted = 0;
488 for (;;) {
489 ch = *end;
490 if (ch == '"') quoted = !quoted;
491 if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
492 *end = '\0';
493 if (beg) {
494 if (*beg != '\0') {
495 extraPmts = beg;
498 if (ch == '\0') break;
499 beg = end + 1;
500 if (ch == ' ') while (end[1] == ' ') end++;
502 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);
516 if (panel->first) {
517 CPlApplet* applet = panel->first;
519 assert(applet && applet->next == NULL);
521 /* we've been given a textual parameter (or none at all) */
522 if (sp == -1) {
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)
528 break;
533 if (sp >= applet->count) {
534 WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count);
535 sp = 0;
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)
555 CPanel panel;
557 TRACE("(%p, %p, %s, 0x%08x)\n",
558 hWnd, hInst, debugstr_w(cmd), nCmdShow);
560 memset(&panel, 0, sizeof(panel));
562 if (!cmd || !*cmd) {
563 Control_DoWindow(&panel, hWnd, hInst);
564 } else {
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);
591 return 0;
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 )
611 WORD args[5];
612 SEGPTR cmdline_seg;
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);
622 args[0] = cmdshow;
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);
637 return 0x0deadbee;