Completely revamped "control.exe":
[reactos.git] / reactos / base / applications / control / control.c
1 /*
2 * PROJECT: ReactOS System Control Panel
3 * FILE: base/applications/control/control.c
4 * PURPOSE: ReactOS System Control Panel
5 * PROGRAMMERS: Gero Kuehn (reactos.filter@gkware.com)
6 * Colin Finck (mail@colinfinck.de)
7 */
8
9 #include "control.h"
10
11 static const TCHAR szWindowClass[] = _T("DummyControlClass");
12
13 HANDLE hProcessHeap;
14 HINSTANCE hInst;
15
16 static INT
17 OpenShellFolder(LPTSTR lpFolderCLSID)
18 {
19 TCHAR szParameters[MAX_PATH];
20
21 /* Open a shell folder using "explorer.exe".
22 The passed CLSID's are all subfolders of the "Control Panel" shell folder. */
23 _tcscpy(szParameters, _T("/n,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"));
24 _tcscat(szParameters, lpFolderCLSID);
25
26 return (int)ShellExecute(NULL, _T("open"), _T("explorer.exe"), szParameters, NULL, SW_SHOWDEFAULT) > 32;
27 }
28
29 static INT
30 RunControlPanel(LPTSTR lpCmd)
31 {
32 TCHAR szParameters[MAX_PATH];
33
34 _tcscpy(szParameters, _T("shell32.dll,Control_RunDLL "));
35 _tcscat(szParameters, lpCmd);
36
37 return RUNDLL(szParameters);
38 }
39
40 static VOID
41 PopulateCPLList(HWND hLisCtrl)
42 {
43 WIN32_FIND_DATA fd;
44 HANDLE hFind;
45 TCHAR pszSearchPath[MAX_PATH];
46 HIMAGELIST hImgListSmall;
47 HIMAGELIST hImgListLarge;
48 HMODULE hDll;
49 CPLAPPLETFUNC pFunc;
50 TCHAR pszPath[MAX_PATH];
51 TCHAR szPanelNum[CCH_UINT_MAX + 1];
52 DEVMODE pDevMode;
53
54 /* Icon drawing mode */
55 pDevMode.dmSize = sizeof(DEVMODE);
56 pDevMode.dmDriverExtra = 0;
57
58 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &pDevMode);
59 hImgListSmall = ImageList_Create(16, 16, pDevMode.dmBitsPerPel | ILC_MASK, 5, 5);
60 hImgListLarge = ImageList_Create(32, 32, pDevMode.dmBitsPerPel | ILC_MASK, 5, 5);
61
62 GetSystemDirectory(pszSearchPath, MAX_PATH);
63 _tcscat(pszSearchPath, _T("\\*.cpl"));
64
65 hFind = FindFirstFile(pszSearchPath, &fd);
66
67 while (hFind != INVALID_HANDLE_VALUE)
68 {
69 _tcscpy(pszPath, pszSearchPath);
70 *_tcsrchr(pszPath, '\\') = 0;
71 _tcscat(pszPath, _T("\\"));
72 _tcscat(pszPath, fd.cFileName);
73
74 hDll = LoadLibrary(pszPath);
75 pFunc = (CPLAPPLETFUNC)GetProcAddress(hDll, "CPlApplet");
76
77 if (pFunc && pFunc(hLisCtrl, CPL_INIT, 0, 0))
78 {
79 UINT i, uPanelCount;
80
81 uPanelCount = (UINT)pFunc(hLisCtrl, CPL_GETCOUNT, 0, 0);
82
83 for (i = 0; i < uPanelCount; i++)
84 {
85 CPLINFO CplInfo;
86 HICON hIcon;
87 TCHAR Name[MAX_PATH];
88 int index;
89 LPTSTR pszCmd;
90
91 pszCmd = (LPTSTR) HeapAlloc(hProcessHeap, 0, MAX_PATH * sizeof(TCHAR));
92 if(!pszCmd)
93 return;
94
95 /* Build the command, which is later passed to RunControlPanel */
96 _tcscpy(pszCmd, fd.cFileName);
97 _tcscat(pszCmd, _T(" @"));
98 _itot(i, szPanelNum, 10);
99 _tcscat(pszCmd, szPanelNum);
100
101 pFunc(hLisCtrl, CPL_INQUIRE, (LPARAM)i, (LPARAM)&CplInfo);
102
103 hIcon = LoadImage(hDll, MAKEINTRESOURCE(CplInfo.idIcon), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
104 index = ImageList_AddIcon(hImgListSmall, hIcon);
105 DestroyIcon(hIcon);
106
107 hIcon = LoadImage(hDll, MAKEINTRESOURCE(CplInfo.idIcon), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
108 ImageList_AddIcon(hImgListLarge, hIcon);
109 DestroyIcon(hIcon);
110
111 if (LoadString(hDll, CplInfo.idName, Name, MAX_PATH))
112 {
113 INT nIndex;
114 LV_ITEM lvi = {0};
115
116 lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
117 lvi.pszText = Name;
118 lvi.state = 0;
119 lvi.iImage = index;
120 lvi.lParam = (LPARAM)pszCmd;
121 nIndex = ListView_InsertItem(hLisCtrl, &lvi);
122
123 if (LoadString(hDll, CplInfo.idInfo, Name, MAX_PATH))
124 ListView_SetItemText(hLisCtrl, nIndex, 1, Name);
125 }
126 }
127 }
128
129 if (!FindNextFile(hFind, &fd))
130 hFind = INVALID_HANDLE_VALUE;
131 }
132
133 (void)ListView_SetImageList(hLisCtrl, hImgListSmall, LVSIL_SMALL);
134 (void)ListView_SetImageList(hLisCtrl, hImgListLarge, LVSIL_NORMAL);
135 }
136
137 LRESULT CALLBACK
138 MyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
139 {
140 static HWND hListView;
141 TCHAR szBuf[1024];
142
143 switch (uMsg)
144 {
145 case WM_CREATE:
146 {
147 RECT rect;
148 LV_COLUMN column = {0};
149
150 GetClientRect(hWnd, &rect);
151 hListView = CreateWindow(WC_LISTVIEW, NULL, LVS_REPORT | LVS_ALIGNLEFT | LVS_SORTASCENDING | LVS_AUTOARRANGE | LVS_SINGLESEL | WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, rect.right, rect.bottom, hWnd, NULL, hInst, 0);
152
153 column.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
154 column.fmt = LVCFMT_LEFT;
155 column.cx = (rect.right - rect.left) / 3;
156 column.iSubItem = 0;
157 LoadString(hInst, IDS_NAME, szBuf, sizeof(szBuf) / sizeof(TCHAR));
158 column.pszText = szBuf;
159 (void)ListView_InsertColumn(hListView, 0, &column);
160
161 column.cx = (rect.right - rect.left) - ((rect.right - rect.left) / 3) - 1;
162 column.iSubItem = 1;
163 LoadString(hInst, IDS_COMMENT, szBuf, sizeof(szBuf) / sizeof(TCHAR));
164 column.pszText = szBuf;
165 (void)ListView_InsertColumn(hListView, 1, &column);
166
167 PopulateCPLList(hListView);
168
169 (void)ListView_SetColumnWidth(hListView, 2, LVSCW_AUTOSIZE_USEHEADER);
170 (void)ListView_Update(hListView, 0);
171
172 SetFocus(hListView);
173
174 return 0;
175 }
176
177 case WM_DESTROY:
178 {
179 LV_ITEM lvi;
180 INT nItems;
181
182 lvi.mask = LVIF_PARAM;
183
184 /* Free the memory used for the command strings */
185 for(nItems = ListView_GetItemCount(hListView); --nItems >= 0;)
186 {
187 lvi.iItem = nItems;
188 (void)ListView_GetItem(hListView, &lvi);
189 HeapFree(hProcessHeap, 0, (LPVOID)lvi.lParam);
190 }
191
192 PostQuitMessage(0);
193 return 0;
194 }
195
196 case WM_SIZE:
197 {
198 RECT rect;
199
200 GetClientRect(hWnd, &rect);
201 MoveWindow(hListView, 0, 0, rect.right, rect.bottom, TRUE);
202
203 return 0;
204 }
205
206 case WM_NOTIFY:
207 {
208 NMHDR *phdr;
209 phdr = (NMHDR*)lParam;
210
211 switch(phdr->code)
212 {
213 case NM_RETURN:
214 case NM_DBLCLK:
215 {
216 int nSelect;
217 LV_ITEM lvi = {0};
218 LPTSTR pszCmd;
219
220 nSelect = SendMessage(hListView, LVM_GETNEXTITEM, (WPARAM)-1, LVNI_FOCUSED);
221
222 if (nSelect == -1)
223 {
224 /* no items */
225 LoadString(hInst, IDS_NO_ITEMS, szBuf, sizeof(szBuf) / sizeof(TCHAR));
226 MessageBox(hWnd, (LPCTSTR)szBuf, NULL, MB_OK | MB_ICONINFORMATION);
227 break;
228 }
229
230 lvi.iItem = nSelect;
231 lvi.mask = LVIF_PARAM;
232 (void)ListView_GetItem(hListView, &lvi);
233
234 pszCmd = (LPTSTR)lvi.lParam;
235
236 if (pszCmd)
237 RunControlPanel(pszCmd);
238
239 return 0;
240 }
241 }
242 }
243
244 case WM_COMMAND:
245 switch (LOWORD(wParam))
246 {
247 case IDM_LARGEICONS:
248 SetWindowLong(hListView,GWL_STYLE,LVS_ICON | LVS_ALIGNLEFT | LVS_AUTOARRANGE | LVS_SINGLESEL | WS_VISIBLE | WS_CHILD|WS_BORDER|WS_TABSTOP);
249 return 0;
250
251 case IDM_SMALLICONS:
252 SetWindowLong(hListView,GWL_STYLE,LVS_SMALLICON | LVS_ALIGNLEFT | LVS_AUTOARRANGE | LVS_SINGLESEL | WS_VISIBLE | WS_CHILD|WS_BORDER|WS_TABSTOP);
253 return 0;
254
255 case IDM_LIST:
256 SetWindowLong(hListView,GWL_STYLE,LVS_LIST | LVS_ALIGNLEFT | LVS_AUTOARRANGE | LVS_SINGLESEL | WS_VISIBLE | WS_CHILD|WS_BORDER|WS_TABSTOP);
257 return 0;
258
259 case IDM_DETAILS:
260 SetWindowLong(hListView,GWL_STYLE,LVS_REPORT | LVS_ALIGNLEFT | LVS_AUTOARRANGE | LVS_SINGLESEL | WS_VISIBLE | WS_CHILD|WS_BORDER|WS_TABSTOP);
261 return 0;
262
263 case IDM_CLOSE:
264 DestroyWindow(hWnd);
265 return 0;
266
267 case IDM_ABOUT:
268 {
269 TCHAR Title[256];
270
271 LoadString(hInst, IDS_ABOUT, szBuf, sizeof(szBuf) / sizeof(TCHAR));
272 LoadString(hInst, IDS_ABOUT_TITLE, Title, sizeof(Title) / sizeof(TCHAR));
273
274 MessageBox(hWnd, (LPCTSTR)szBuf, (LPCTSTR)Title, MB_OK | MB_ICONINFORMATION);
275
276 return 0;
277 }
278 }
279 }
280
281 return DefWindowProc(hWnd, uMsg, wParam, lParam);
282 }
283
284
285 static INT
286 RunControlPanelWindow(int nCmdShow)
287 {
288 MSG msg;
289 HWND hMainWnd;
290 INITCOMMONCONTROLSEX icex;
291 WNDCLASSEX wcex = {0};
292 TCHAR szBuf[256];
293
294 wcex.cbSize = sizeof(wcex);
295 wcex.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_MAINICON));
296 wcex.lpszClassName = MYWNDCLASS;
297 wcex.lpfnWndProc = MyWindowProc;
298 RegisterClassEx(&wcex);
299
300 icex.dwSize = sizeof(icex);
301 icex.dwICC = ICC_LISTVIEW_CLASSES;
302 InitCommonControlsEx(&icex);
303
304 LoadString(hInst, IDS_WINDOW_TITLE, szBuf, sizeof(szBuf) / sizeof(TCHAR));
305
306 hMainWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
307 MYWNDCLASS,
308 (LPCTSTR)szBuf,
309 WS_OVERLAPPEDWINDOW,
310 CW_USEDEFAULT,
311 CW_USEDEFAULT,
312 CW_USEDEFAULT,
313 CW_USEDEFAULT,
314 NULL,
315 LoadMenu(hInst, MAKEINTRESOURCE(IDM_MAINMENU)),
316 hInst,
317 0);
318 if (!hMainWnd)
319 return 1;
320
321 ShowWindow(hMainWnd, nCmdShow);
322
323 while (GetMessage(&msg, 0, 0, 0))
324 {
325 TranslateMessage(&msg);
326 DispatchMessage(&msg);
327 }
328
329 return 0;
330 }
331
332 int WINAPI
333 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
334 {
335 HKEY hKey;
336
337 hInst = hInstance;
338 hProcessHeap = GetProcessHeap();
339
340 /* Show the control panel window if no argument or "panel" was passed */
341 if(lpCmdLine[0] == 0 || !_tcsicmp(lpCmdLine, _T("panel")))
342 return RunControlPanelWindow(nCmdShow);
343
344 /* Check one of the built-in control panel handlers */
345 if (!_tcsicmp(lpCmdLine, _T("admintools"))) return OpenShellFolder(_T("\\::{D20EA4E1-3957-11d2-A40B-0C5020524153}"));
346 else if (!_tcsicmp(lpCmdLine, _T("color"))) return RunControlPanel(_T("desk.cpl")); /* TODO: Switch to the "Apperance" tab */
347 else if (!_tcsicmp(lpCmdLine, _T("date/time"))) return RunControlPanel(_T("timedate.cpl"));
348 else if (!_tcsicmp(lpCmdLine, _T("desktop"))) return RunControlPanel(_T("desk.cpl"));
349 else if (!_tcsicmp(lpCmdLine, _T("folders"))) return RUNDLL(_T("shell32.dll,Options_RunDLL"));
350 else if (!_tcsicmp(lpCmdLine, _T("fonts"))) return OpenShellFolder(_T("\\::{D20EA4E1-3957-11d2-A40B-0C5020524152}"));
351 else if (!_tcsicmp(lpCmdLine, _T("infrared"))) return RunControlPanel(_T("irprops.cpl"));
352 else if (!_tcsicmp(lpCmdLine, _T("international"))) return RunControlPanel(_T("intl.cpl"));
353 else if (!_tcsicmp(lpCmdLine, _T("keyboard"))) return RunControlPanel(_T("main.cpl @1"));
354 else if (!_tcsicmp(lpCmdLine, _T("mouse"))) return RunControlPanel(_T("main.cpl @0"));
355 else if (!_tcsicmp(lpCmdLine, _T("netconnections"))) return OpenShellFolder(_T("\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}"));
356 else if (!_tcsicmp(lpCmdLine, _T("netware"))) return RunControlPanel(_T("nwc.cpl"));
357 else if (!_tcsicmp(lpCmdLine, _T("ports"))) return RunControlPanel(_T("sysdm.cpl")); /* TODO: Switch to the "Computer Name" tab */
358 else if (!_tcsicmp(lpCmdLine, _T("printers"))) return OpenShellFolder(_T("\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"));
359 else if (!_tcsicmp(lpCmdLine, _T("scannercamera"))) return OpenShellFolder(_T("\\::{E211B736-43FD-11D1-9EFB-0000F8757FCD}"));
360 else if (!_tcsicmp(lpCmdLine, _T("schedtasks"))) return OpenShellFolder(_T("\\::{D6277990-4C6A-11CF-8D87-00AA0060F5BF}"));
361 else if (!_tcsicmp(lpCmdLine, _T("telephony"))) return RunControlPanel(_T("telephon.cpl"));
362 else if (!_tcsicmp(lpCmdLine, _T("userpasswords"))) return RunControlPanel(_T("nusrmgr.cpl")); /* Graphical User Account Manager */
363 else if (!_tcsicmp(lpCmdLine, _T("userpasswords2"))) return RUNDLL(_T("netplwiz.dll,UsersRunDll")); /* Dialog based advanced User Account Manager */
364
365 /* It is none of them, so look for a handler in the registry */
366 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
367 {
368 DWORD dwIndex;
369
370 for(dwIndex = 0; ; ++dwIndex)
371 {
372 DWORD dwDataSize;
373 DWORD dwValueSize = MAX_VALUE_NAME;
374 TCHAR szValueName[MAX_VALUE_NAME];
375
376 /* Get the value name and data size */
377 if(RegEnumValue(hKey, dwIndex, szValueName, &dwValueSize, 0, NULL, NULL, &dwDataSize) != ERROR_SUCCESS)
378 break;
379
380 /* Check if the parameter is the value name */
381 if(!_tcsicmp(lpCmdLine, szValueName))
382 {
383 LPTSTR pszData;
384
385 /* Allocate memory for the data plus two more characters, so we can quote the file name if required */
386 pszData = (LPTSTR) HeapAlloc(hProcessHeap, 0, dwDataSize + 2 * sizeof(TCHAR));
387 ++pszData;
388
389 /* This value is the one we are looking for, so get the data. It is the path to a .cpl file */
390 if(RegQueryValueEx(hKey, szValueName, 0, NULL, (LPBYTE)pszData, &dwDataSize) == ERROR_SUCCESS)
391 {
392 INT nReturnValue;
393
394 /* Quote the file name if required */
395 if(*pszData != '\"')
396 {
397 *(--pszData) = '\"';
398 pszData[dwDataSize / sizeof(TCHAR)] = '\"';
399 pszData[(dwDataSize / sizeof(TCHAR)) + 1] = 0;
400 }
401
402 nReturnValue = RunControlPanel(pszData);
403 HeapFree(hProcessHeap, 0, pszData);
404 RegCloseKey(hKey);
405
406 return nReturnValue;
407 }
408
409 HeapFree(hProcessHeap, 0, pszData);
410 }
411 }
412
413 RegCloseKey(hKey);
414 }
415
416 /* It's none of the known parameters, so interpret the parameter as the file name of a control panel applet */
417 return RunControlPanel(lpCmdLine);
418 }