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
23 #define WIN32_NO_STATUS
28 #define NO_SHLWAPI_REG
30 #include <wine/debug.h>
34 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl
);
36 CPlApplet
* Control_UnloadApplet(CPlApplet
* applet
)
41 for (i
= 0; i
< applet
->count
; i
++)
43 if (!applet
->info
[i
].dwSize
)
45 applet
->proc(applet
->hWnd
, CPL_STOP
, i
, applet
->info
[i
].lData
);
48 applet
->proc(applet
->hWnd
, CPL_EXIT
, 0L, 0L);
50 FreeLibrary(applet
->hModule
);
52 HeapFree(GetProcessHeap(), 0, applet
->cmd
);
53 HeapFree(GetProcessHeap(), 0, applet
);
57 CPlApplet
* Control_LoadApplet(HWND hWnd
, LPCWSTR cmd
, CPanel
* panel
)
65 if (!(applet
= (CPlApplet
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*applet
))))
68 len
= ExpandEnvironmentStringsW(cmd
, NULL
, 0);
71 if (!(applet
->cmd
= HeapAlloc(GetProcessHeap(), 0, (len
+1) * sizeof(WCHAR
))))
73 WARN("Cannot allocate memory for applet path\n");
76 ExpandEnvironmentStringsW(cmd
, applet
->cmd
, len
+1);
80 WARN("Cannot expand applet path\n");
86 if (!(applet
->hModule
= LoadLibraryW(applet
->cmd
)))
88 WARN("Cannot load control panel applet %s\n", debugstr_w(applet
->cmd
));
91 if (!(applet
->proc
= (APPLET_PROC
)GetProcAddress(applet
->hModule
, "CPlApplet")))
93 WARN("Not a valid control panel applet %s\n", debugstr_w(applet
->cmd
));
96 if (!applet
->proc(hWnd
, CPL_INIT
, 0L, 0L))
98 WARN("Init of applet has failed\n");
101 if ((applet
->count
= applet
->proc(hWnd
, CPL_GETCOUNT
, 0L, 0L)) == 0)
103 WARN("No subprogram in applet\n");
107 applet
= (CPlApplet
*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, applet
,
108 sizeof(*applet
) + (applet
->count
- 1) * sizeof(NEWCPLINFOW
));
110 for (i
= 0; i
< applet
->count
; i
++)
112 ZeroMemory(&newinfo
, sizeof(newinfo
));
113 newinfo
.dwSize
= sizeof(NEWCPLINFOW
);
114 applet
->info
[i
].dwSize
= sizeof(NEWCPLINFOW
);
115 /* proc is supposed to return a null value upon success for
116 * CPL_INQUIRE and CPL_NEWINQUIRE
117 * However, real drivers don't seem to behave like this
118 * So, use introspection rather than return value
120 applet
->proc(hWnd
, CPL_NEWINQUIRE
, i
, (LPARAM
)&newinfo
);
121 if (newinfo
.hIcon
== 0)
123 applet
->proc(hWnd
, CPL_INQUIRE
, i
, (LPARAM
)&info
);
124 if (info
.idIcon
== 0 || info
.idName
== 0)
126 WARN("Couldn't get info from sp %u\n", i
);
127 applet
->info
[i
].dwSize
= 0;
131 /* convert the old data into the new structure */
132 applet
->info
[i
].dwFlags
= 0;
133 applet
->info
[i
].dwHelpContext
= 0;
134 applet
->info
[i
].lData
= info
.lData
;
135 applet
->info
[i
].hIcon
= LoadIconW(applet
->hModule
,
136 MAKEINTRESOURCEW(info
.idIcon
));
137 LoadStringW(applet
->hModule
, info
.idName
,
138 applet
->info
[i
].szName
, sizeof(applet
->info
[i
].szName
) / sizeof(WCHAR
));
139 LoadStringW(applet
->hModule
, info
.idInfo
,
140 applet
->info
[i
].szInfo
, sizeof(applet
->info
[i
].szInfo
) / sizeof(WCHAR
));
141 applet
->info
[i
].szHelpFile
[0] = '\0';
146 CopyMemory(&applet
->info
[i
], &newinfo
, newinfo
.dwSize
);
147 if (newinfo
.dwSize
!= sizeof(NEWCPLINFOW
))
149 applet
->info
[i
].dwSize
= sizeof(NEWCPLINFOW
);
150 lstrcpyW(applet
->info
[i
].szName
, newinfo
.szName
);
151 lstrcpyW(applet
->info
[i
].szInfo
, newinfo
.szInfo
);
152 lstrcpyW(applet
->info
[i
].szHelpFile
, newinfo
.szHelpFile
);
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
;
171 SetWindowLongPtrW(hWnd
, 0, (LONG_PTR
)panel
);
181 static BOOL
Control_Localize(const CPanel
* panel
, int cx
, int cy
,
182 CPlApplet
** papplet
, unsigned* psp
)
185 int x
= (XSTEP
-XICON
)/2, y
= 0;
189 GetClientRect(panel
->hWnd
, &rc
);
190 for (applet
= panel
->first
; applet
; applet
= applet
->next
)
192 for (i
= 0; i
< applet
->count
; i
++)
194 if (!applet
->info
[i
].dwSize
)
196 if (x
+ XSTEP
>= rc
.right
- rc
.left
)
201 if (cx
>= x
&& cx
< x
+ XICON
&& cy
>= y
&& cy
< y
+ YSTEP
)
213 static LRESULT
Control_WndProc_Paint(const CPanel
* panel
, WPARAM wParam
)
223 hdc
= (wParam
) ? (HDC
)wParam
: BeginPaint(panel
->hWnd
, &ps
);
224 hOldFont
= SelectObject(hdc
, GetStockObject(ANSI_VAR_FONT
));
225 GetClientRect(panel
->hWnd
, &rc
);
227 for (applet
= panel
->first
; applet
; applet
= applet
->next
)
229 for (i
= 0; i
< applet
->count
; i
++)
231 if (x
+ XSTEP
>= rc
.right
- rc
.left
)
236 if (!applet
->info
[i
].dwSize
)
238 DrawIcon(hdc
, x
+ (XSTEP
-XICON
)/2, y
, applet
->info
[i
].hIcon
);
240 txtRect
.right
= x
+ XSTEP
;
241 txtRect
.top
= y
+ YICON
;
242 txtRect
.bottom
= y
+ YSTEP
;
243 DrawTextW(hdc
, applet
->info
[i
].szName
, -1, &txtRect
,
244 DT_CENTER
| DT_VCENTER
);
249 SelectObject(hdc
, hOldFont
);
251 EndPaint(panel
->hWnd
, &ps
);
256 static LRESULT
Control_WndProc_LButton(CPanel
* panel
, LPARAM lParam
, BOOL up
)
261 if (Control_Localize(panel
, (short)LOWORD(lParam
), (short)HIWORD(lParam
), &applet
, &i
))
265 if (panel
->clkApplet
== applet
&& panel
->clkSP
== i
)
267 applet
->proc(applet
->hWnd
, CPL_DBLCLK
, i
, applet
->info
[i
].lData
);
272 panel
->clkApplet
= applet
;
279 static LRESULT WINAPI
Control_WndProc(HWND hWnd
, UINT wMsg
,
280 WPARAM lParam1
, LPARAM lParam2
)
282 CPanel
* panel
= (CPanel
*)GetWindowLongPtrW(hWnd
, 0);
284 if (panel
|| wMsg
== WM_CREATE
)
289 Control_WndProc_Create(hWnd
, (CREATESTRUCTW
*)lParam2
);
293 CPlApplet
*applet
= panel
->first
;
295 applet
= Control_UnloadApplet(applet
);
301 return Control_WndProc_Paint(panel
, lParam1
);
303 return Control_WndProc_LButton(panel
, lParam2
, TRUE
);
305 return Control_WndProc_LButton(panel
, lParam2
, FALSE
);
306 /* EPP case WM_COMMAND: */
307 /* EPP return Control_WndProc_Command(mwi, lParam1, lParam2); */
311 return DefWindowProcW(hWnd
, wMsg
, lParam1
, lParam2
);
314 static void Control_DoInterface(CPanel
* panel
, HWND hWnd
, HINSTANCE hInst
)
318 const WCHAR
* appName
= L
"ReactOS Control Panel";
319 wc
.style
= CS_HREDRAW
|CS_VREDRAW
;
320 wc
.lpfnWndProc
= Control_WndProc
;
322 wc
.cbWndExtra
= sizeof(CPlApplet
*);
323 wc
.hInstance
= hInst
;
326 wc
.hbrBackground
= (HBRUSH
)GetStockObject(WHITE_BRUSH
);
327 wc
.lpszMenuName
= NULL
;
328 wc
.lpszClassName
= L
"Shell_Control_WndClass";
330 if (!RegisterClassW(&wc
)) return;
332 CreateWindowExW(0, wc
.lpszClassName
, appName
,
333 WS_OVERLAPPEDWINDOW
| WS_VISIBLE
,
334 CW_USEDEFAULT
, CW_USEDEFAULT
,
335 CW_USEDEFAULT
, CW_USEDEFAULT
,
336 hWnd
, NULL
, hInst
, panel
);
343 /* FIXME appName & message should be localized */
344 MessageBoxW(panel
->hWnd
, L
"Cannot load any applets", appName
, MB_OK
);
348 while (GetMessageW(&msg
, panel
->hWnd
, 0, 0))
350 TranslateMessage(&msg
);
351 DispatchMessageW(&msg
);
355 static void Control_DoWindow(CPanel
*panel
, HWND hWnd
, HINSTANCE hInst
)
358 WIN32_FIND_DATAW wfd
;
359 WCHAR wszPath
[MAX_PATH
];
360 WCHAR
*Ptr
= wszPath
;
362 Ptr
+= GetSystemDirectoryW(wszPath
, MAX_PATH
);
364 wcscpy(Ptr
, L
"*.cpl");
366 hFind
= FindFirstFileW(wszPath
, &wfd
);
367 if (hFind
!= INVALID_HANDLE_VALUE
)
371 wcscpy(Ptr
, wfd
.cFileName
);
372 Control_LoadApplet(hWnd
, wszPath
, panel
);
373 } while (FindNextFileW(hFind
, &wfd
));
377 Control_DoInterface(panel
, hWnd
, hInst
);
380 static void Control_DoLaunch(CPanel
*pPanel
, HWND hWnd
, LPCWSTR pwszCmd
)
384 /* Make a pwszCmd copy so we can modify it */
385 LPWSTR pwszCmdCopy
= _wcsdup(pwszCmd
);
387 LPWSTR pwszPath
= pwszCmdCopy
, pwszArg
= NULL
, pwszArg2
= NULL
;
391 /* Path can be quoted */
392 if (pwszPath
[0] == L
'"')
395 pwszArg
= wcschr(pwszPath
, L
'"');
400 pwszArg
= pwszCmdCopy
;
402 /* First argument starts after space or ','. Note: we ignore characters between '"' and ',' or ' '. */
404 pwszArg
= wcspbrk(pwszArg
, L
" ,");
407 /* NULL terminate path and find first character of arg */
408 *(pwszArg
++) = L
'\0';
409 if (pwszArg
[0] == L
'"')
412 pwszArg2
= wcschr(pwszArg
, L
'"');
414 *(pwszArg2
++) = L
'\0';
418 /* Second argument always starts with ','. Note: we ignore characters between '"' and ','. */
420 pwszArg2
= wcschr(pwszArg2
, L
',');
423 TRACE("Launch %ls, arg %ls, arg2 %ls\n", pwszPath
, pwszArg
, pwszArg2
);
425 /* Create a mutex to disallow running multiple instances */
426 hMutex
= CreateMutexW(NULL
, TRUE
, PathFindFileNameW(pwszPath
));
427 if (!hMutex
|| GetLastError() == ERROR_ALREADY_EXISTS
)
429 TRACE("Next instance disallowed\n");
435 /* Load applet cpl */
436 TRACE("Load applet %ls\n", pwszPath
);
437 Control_LoadApplet(hWnd
, pwszPath
, pPanel
);
441 /* First pPanel applet is the new one */
442 CPlApplet
*pApplet
= pPanel
->first
;
443 assert(pApplet
&& pApplet
->next
== NULL
);
444 TRACE("pApplet->count %d\n", pApplet
->count
);
446 /* Note: if there is only one applet, first argument is ignored */
447 if (pApplet
->count
> 1 && pwszArg
&& pwszArg
[0])
449 /* If arg begins with '@', number specifies applet index */
450 if (pwszArg
[0] == L
'@')
451 i
= _wtoi(pwszArg
+ 1);
454 /* Otherwise it's applet name */
455 for (i
= 0; i
< (INT
)pApplet
->count
; ++i
)
456 if (!wcscmp(pwszArg
, pApplet
->info
[i
].szName
))
461 if (i
>= 0 && i
< (INT
)pApplet
->count
&& pApplet
->info
[i
].dwSize
)
463 /* Start the applet */
464 TRACE("Starting applet %d\n", i
);
465 if (!pApplet
->proc(pApplet
->hWnd
, CPL_STARTWPARMSW
, i
, (LPARAM
)pwszArg
))
466 pApplet
->proc(pApplet
->hWnd
, CPL_DBLCLK
, i
, pApplet
->info
[i
].lData
);
468 ERR("Applet not found: %ls\n", pwszArg
? pwszArg
: L
"NULL");
470 Control_UnloadApplet(pApplet
);
473 ERR("Failed to load applet %ls\n", pwszPath
);
475 ReleaseMutex(hMutex
);
480 /*************************************************************************
481 * Control_RunDLLW [SHELL32.@]
484 void WINAPI
Control_RunDLLW(HWND hWnd
, HINSTANCE hInst
, LPCWSTR cmd
, DWORD nCmdShow
)
488 TRACE("(%p, %p, %s, 0x%08x)\n",
489 hWnd
, hInst
, debugstr_w(cmd
), nCmdShow
);
491 memset(&Panel
, 0, sizeof(Panel
));
495 TRACE("[shell32, Control_RunDLLW] Calling Control_DoWindow\n");
496 Control_DoWindow(&Panel
, hWnd
, hInst
);
500 TRACE("[shell32, Control_RunDLLW] Calling Control_DoLaunch\n");
501 Control_DoLaunch(&Panel
, hWnd
, cmd
);
505 /*************************************************************************
506 * Control_RunDLLA [SHELL32.@]
509 void WINAPI
Control_RunDLLA(HWND hWnd
, HINSTANCE hInst
, LPCSTR cmd
, DWORD nCmdShow
)
511 DWORD len
= MultiByteToWideChar(CP_ACP
, 0, cmd
, -1, NULL
, 0 );
512 LPWSTR wszCmd
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
));
513 if (wszCmd
&& MultiByteToWideChar(CP_ACP
, 0, cmd
, -1, wszCmd
, len
))
515 Control_RunDLLW(hWnd
, hInst
, wszCmd
, nCmdShow
);
517 HeapFree(GetProcessHeap(), 0, wszCmd
);
520 /*************************************************************************
521 * Control_FillCache_RunDLLW [SHELL32.@]
524 HRESULT WINAPI
Control_FillCache_RunDLLW(HWND hWnd
, HANDLE hModule
, DWORD w
, DWORD x
)
526 FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd
, hModule
, w
, x
);
530 /*************************************************************************
531 * Control_FillCache_RunDLLA [SHELL32.@]
534 HRESULT WINAPI
Control_FillCache_RunDLLA(HWND hWnd
, HANDLE hModule
, DWORD w
, DWORD x
)
536 return Control_FillCache_RunDLLW(hWnd
, hModule
, w
, x
);
540 /*************************************************************************
541 * RunDll_CallEntry16 [SHELL32.122]
542 * the name is OK (when written with Dll, and not DLL as in Wine!)
544 void WINAPI
RunDll_CallEntry16( DWORD proc
, HWND hwnd
, HINSTANCE inst
,
545 LPCSTR cmdline
, INT cmdshow
)
547 #if !defined(__CYGWIN__) && !defined (__MINGW32__) && !defined(_MSC_VER)
551 TRACE( "proc %x hwnd %p inst %p cmdline %s cmdshow %d\n",
552 proc
, hwnd
, inst
, debugstr_a(cmdline
), cmdshow
);
554 cmdline_seg
= MapLS( cmdline
);
555 args
[4] = HWND_16(hwnd
);
556 args
[3] = MapHModuleLS(inst
);
557 args
[2] = SELECTOROF(cmdline_seg
);
558 args
[1] = OFFSETOF(cmdline_seg
);
560 WOWCallback16Ex( proc
, WCB16_PASCAL
, sizeof(args
), args
, NULL
);
561 UnMapLS( cmdline_seg
);
563 FIXME( "proc %lx hwnd %p inst %p cmdline %s cmdshow %d\n",
564 proc
, hwnd
, inst
, debugstr_a(cmdline
), cmdshow
);
568 /*************************************************************************
569 * CallCPLEntry16 [SHELL32.166]
571 * called by desk.cpl on "Advanced" with:
572 * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
575 LRESULT WINAPI
CallCPLEntry16(HINSTANCE hMod
, FARPROC pFunc
, HWND dw3
, UINT dw4
, LPARAM dw5
, LPARAM dw6
)
577 FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod
, pFunc
, dw3
, dw4
, dw5
, dw6
);