Reactos
1/*
2 * PROJECT: ReactOS System Control Panel
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: ReactOS System Control Panel
5 * COPYRIGHT: Copyright 2004 Gero Kuehn (reactos.filter@gkware.com)
6 * Copyright 2008 Colin Finck (colin@reactos.org)
7 * Copyright 2014 Hermès Bélusca-Maïto (hermes.belusca-maito@reactos.org)
8 */
9
10#include <stdio.h>
11
12#define WIN32_NO_STATUS
13#define COBJMACROS
14
15
16#include <windef.h>
17#include <winbase.h>
18#include <winuser.h>
19#include <winreg.h>
20#include <shellapi.h>
21#include <strsafe.h>
22#include <objbase.h>
23#include <shobjidl.h>
24#include <shlguid.h>
25
26#include "resource.h"
27
28#define MAX_VALUE_NAME 16383
29
30/*
31 * Macro for calling "rundll32.exe"
32 * According to MSDN, ShellExecute returns a value greater than 32
33 * if the operation was successful.
34 */
35#define RUNDLL(param) \
36 ((INT_PTR)ShellExecuteW(NULL, L"open", L"rundll32.exe", (param), NULL, SW_SHOWDEFAULT) > 32)
37
38VOID
39WINAPI
40Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
41
42static BOOL
43IsSwitch(LPCWSTR Switch, LPCWSTR Arg)
44{
45 if (*Arg == '/' || *Arg == '-')
46 {
47 return !lstrcmpiW(Arg+1, Switch);
48 }
49 return FALSE;
50}
51
52static HRESULT
53OpenControlPanelItem(LPCWSTR Name, LPCWSTR Page)
54{
55 HRESULT hr = CoInitialize(0);
56 if (SUCCEEDED(hr))
57 {
58 IOpenControlPanel *pOCP;
59 hr = CoCreateInstance(&CLSID_OpenControlPanel, NULL, CLSCTX_INPROC_SERVER,
60 &IID_IOpenControlPanel, (void**)&pOCP);
61 if (SUCCEEDED(hr))
62 {
63 hr = IOpenControlPanel_Open(pOCP, Name, Page, NULL);
64 IOpenControlPanel_Release(pOCP);
65 }
66 CoUninitialize();
67 }
68 return hr;
69}
70
71static INT
72OpenShellFolder(LPWSTR lpFolderCLSID)
73{
74 WCHAR szParameters[MAX_PATH];
75
76 /*
77 * Open a shell folder using "explorer.exe". If Explorer shell is not
78 * available, use ReactOS's alternative file browser instead.
79 * The passed CLSIDs are all subfolders of the "Control Panel" shell folder.
80 */
81 StringCbCopyW(szParameters, sizeof(szParameters), L"/n,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}");
82 StringCbCatW(szParameters, sizeof(szParameters), lpFolderCLSID);
83
84 return (INT_PTR)ShellExecuteW(NULL,
85 L"open",
86 GetShellWindow() ? L"explorer.exe" : L"filebrowser.exe",
87 szParameters,
88 NULL,
89 SW_SHOWDEFAULT) > 32;
90}
91
92static INT
93RunControlPanel(LPCWSTR lpCmd)
94{
95 WCHAR szParameters[MAX_PATH];
96 StringCchCopyW(szParameters, ARRAYSIZE(szParameters), L"shell32.dll,Control_RunDLL ");
97 if (FAILED(StringCchCatW(szParameters, ARRAYSIZE(szParameters), lpCmd)))
98 return 0;
99
100 return RUNDLL(szParameters);
101}
102
103INT
104WINAPI
105wWinMain(HINSTANCE hInstance,
106 HINSTANCE hPrevInstance,
107 LPWSTR lpCmdLine,
108 INT nCmdShow)
109{
110 HKEY hKey;
111 LPWSTR *argv;
112 int argc;
113
114 /* Show the control panel window if no argument or "panel" was passed */
115 if (*lpCmdLine == 0 || !_wcsicmp(lpCmdLine, L"panel"))
116 return OpenShellFolder(L"");
117
118 /* Map legacy control panels */
119 if (!_wcsicmp(lpCmdLine, L"sticpl.cpl")) lpCmdLine = (LPWSTR) L"scannercamera";
120
121 /* Check one of the built-in control panel handlers */
122 if (!_wcsicmp(lpCmdLine, L"admintools")) return OpenShellFolder(L"\\::{D20EA4E1-3957-11d2-A40B-0C5020524153}");
123 else if (!_wcsicmp(lpCmdLine, L"color")) return RunControlPanel(L"desk.cpl,,2");
124 else if (!_wcsicmp(lpCmdLine, L"date/time")) return RunControlPanel(L"timedate.cpl");
125 else if (!_wcsicmp(lpCmdLine, L"desktop")) return RunControlPanel(L"desk.cpl");
126 else if (!_wcsicmp(lpCmdLine, L"folders")) return RUNDLL(L"shell32.dll,Options_RunDLL");
127 else if (!_wcsicmp(lpCmdLine, L"fonts")) return OpenShellFolder(L"\\::{D20EA4E1-3957-11d2-A40B-0C5020524152}");
128 else if (!_wcsicmp(lpCmdLine, L"infrared")) return RunControlPanel(L"irprops.cpl");
129 else if (!_wcsicmp(lpCmdLine, L"international")) return RunControlPanel(L"intl.cpl");
130 else if (!_wcsicmp(lpCmdLine, L"keyboard")) return RunControlPanel(L"main.cpl @1");
131 else if (!_wcsicmp(lpCmdLine, L"mouse")) return RunControlPanel(L"main.cpl @0");
132 else if (!_wcsicmp(lpCmdLine, L"netconnections")) return OpenShellFolder(L"\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}");
133 else if (!_wcsicmp(lpCmdLine, L"netware")) return RunControlPanel(L"nwc.cpl");
134 else if (!_wcsicmp(lpCmdLine, L"ports")) return RunControlPanel(L"sysdm.cpl,,1");
135 else if (!_wcsicmp(lpCmdLine, L"printers")) return OpenShellFolder(L"\\::{2227A280-3AEA-1069-A2DE-08002B30309D}");
136 else if (!_wcsicmp(lpCmdLine, L"scannercamera")) return OpenShellFolder(L"\\::{E211B736-43FD-11D1-9EFB-0000F8757FCD}");
137 else if (!_wcsicmp(lpCmdLine, L"schedtasks")) return OpenShellFolder(L"\\::{D6277990-4C6A-11CF-8D87-00AA0060F5BF}");
138 else if (!_wcsicmp(lpCmdLine, L"telephony")) return RunControlPanel(L"telephon.cpl");
139 else if (!_wcsicmp(lpCmdLine, L"userpasswords")) return RunControlPanel(L"nusrmgr.cpl"); /* Graphical User Account Manager */
140 else if (!_wcsicmp(lpCmdLine, L"userpasswords2")) return RUNDLL(L"netplwiz.dll,UsersRunDll"); /* Dialog based advanced User Account Manager */
141
142 /* https://learn.microsoft.com/en-us/windows/win32/shell/executing-control-panel-items#windows-vista-canonical-names */
143 argv = CommandLineToArgvW(lpCmdLine, &argc);
144 if (argv)
145 {
146 UINT argi = 0;
147 HRESULT hr = -1;
148 if (argc >= 2 && IsSwitch(L"name", argv[argi + 0]))
149 {
150 LPCWSTR pszPage = NULL;
151 if (argc >= 4 && IsSwitch(L"page", argv[argi + 2]))
152 {
153 pszPage = argv[argi + 3];
154 }
155 hr = OpenControlPanelItem(argv[argi + 1], pszPage);
156 }
157 LocalFree(argv);
158 if (hr != -1)
159 {
160 return SUCCEEDED(hr);
161 }
162 }
163
164 /* It is none of them, so look for a handler in the registry */
165 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
166 L"Software\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls",
167 0,
168 KEY_QUERY_VALUE,
169 &hKey) == ERROR_SUCCESS)
170 {
171 DWORD dwIndex;
172
173 for (dwIndex = 0; ; ++dwIndex)
174 {
175 DWORD dwDataSize;
176 DWORD dwValueSize = MAX_VALUE_NAME;
177 WCHAR szValueName[MAX_VALUE_NAME];
178
179 /* Get the value name and data size */
180 if (RegEnumValueW(hKey,
181 dwIndex,
182 szValueName,
183 &dwValueSize,
184 0,
185 NULL,
186 NULL,
187 &dwDataSize) != ERROR_SUCCESS)
188 {
189 break;
190 }
191
192 /* Check if the parameter is the value name */
193 if (!_wcsicmp(lpCmdLine, szValueName))
194 {
195 /*
196 * Allocate memory for the data plus two more characters,
197 * so we can quote the file name if required.
198 */
199 LPWSTR pszData;
200 pszData = HeapAlloc(GetProcessHeap(),
201 0,
202 dwDataSize + 2 * sizeof(WCHAR));
203 ++pszData;
204
205 /*
206 * This value is the one we are looking for, so get the data.
207 * It is the path to a .cpl file.
208 */
209 if (RegQueryValueExW(hKey,
210 szValueName,
211 0,
212 NULL,
213 (LPBYTE)pszData,
214 &dwDataSize) == ERROR_SUCCESS)
215 {
216 INT nReturnValue;
217
218 /* Quote the file name if required */
219 if (*pszData != L'\"')
220 {
221 *(--pszData) = L'\"';
222 pszData[dwDataSize / sizeof(WCHAR)] = L'\"';
223 pszData[(dwDataSize / sizeof(WCHAR)) + 1] = 0;
224 }
225
226 nReturnValue = RunControlPanel(pszData);
227 HeapFree(GetProcessHeap(), 0, pszData);
228 RegCloseKey(hKey);
229
230 return nReturnValue;
231 }
232
233 HeapFree(GetProcessHeap(), 0, pszData);
234 }
235 }
236
237 RegCloseKey(hKey);
238 }
239
240 /*
241 * It's none of the known parameters, so interpret the parameter
242 * as the file name of a control panel applet.
243 */
244 return RunControlPanel(lpCmdLine);
245}