xref: /reactos/base/applications/control/control.c (revision 6c74e69d)
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 
38 VOID
39 WINAPI
40 Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
41 
42 static BOOL
43 IsSwitch(LPCWSTR Switch, LPCWSTR Arg)
44 {
45     if (*Arg == '/' || *Arg == '-')
46     {
47         return !lstrcmpiW(Arg+1, Switch);
48     }
49     return FALSE;
50 }
51 
52 static HRESULT
53 OpenControlPanelItem(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 
71 static INT
72 OpenShellFolder(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 
92 static INT
93 RunControlPanel(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 
103 INT
104 WINAPI
105 wWinMain(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 }
246