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