* Sync up to trunk HEAD (r62286).
[reactos.git] / base / system / userinit / userinit.c
1 /*
2 * ReactOS applications
3 * Copyright (C) 2001, 2002 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS Userinit Logon Application
22 * FILE: subsys/system/userinit/userinit.c
23 * PROGRAMMERS: Thomas Weidenmueller (w3seek@users.sourceforge.net)
24 * Hervé Poussineau (hpoussin@reactos.org)
25 */
26
27 #define WIN32_NO_STATUS
28 #define _INC_WINDOWS
29 #define COM_NO_WINDOWS_H
30 #include <stdarg.h>
31 #include <windef.h>
32 #include <winbase.h>
33 #include <winreg.h>
34 #include <wingdi.h>
35 #include <wincon.h>
36 #include <shellapi.h>
37 #include <regstr.h>
38 #include <shlobj.h>
39 #include <shlwapi.h>
40 #include <undocuser.h>
41 #include <wine/debug.h>
42
43 #include "resource.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(userinit);
46
47 #define CMP_MAGIC 0x01234567
48
49 /* GLOBALS ******************************************************************/
50
51 /* FUNCTIONS ****************************************************************/
52
53 static LONG
54 ReadRegSzKey(
55 IN HKEY hKey,
56 IN LPCWSTR pszKey,
57 OUT LPWSTR* pValue)
58 {
59 LONG rc;
60 DWORD dwType;
61 DWORD cbData = 0;
62 LPWSTR Value;
63
64 TRACE("(%p, %s, %p)\n", hKey, debugstr_w(pszKey), pValue);
65
66 rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
67 if (rc != ERROR_SUCCESS)
68 {
69 WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
70 return rc;
71 }
72 if (dwType != REG_SZ)
73 {
74 WARN("Wrong registry data type (%u vs %u)\n", dwType, REG_SZ);
75 return ERROR_FILE_NOT_FOUND;
76 }
77 Value = (WCHAR*) HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
78 if (!Value)
79 {
80 WARN("No memory\n");
81 return ERROR_NOT_ENOUGH_MEMORY;
82 }
83 rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)Value, &cbData);
84 if (rc != ERROR_SUCCESS)
85 {
86 WARN("RegQueryValueEx(%s) failed with error %lu\n", debugstr_w(pszKey), rc);
87 HeapFree(GetProcessHeap(), 0, Value);
88 return rc;
89 }
90 /* NULL-terminate the string */
91 Value[cbData / sizeof(WCHAR)] = '\0';
92
93 *pValue = Value;
94 return ERROR_SUCCESS;
95 }
96
97 static
98 BOOL IsConsoleShell(VOID)
99 {
100 HKEY ControlKey = NULL;
101 LPWSTR SystemStartOptions = NULL;
102 LPWSTR CurrentOption, NextOption; /* Pointers into SystemStartOptions */
103 LONG rc;
104 BOOL ret = FALSE;
105
106 TRACE("()\n");
107
108 rc = RegOpenKeyEx(
109 HKEY_LOCAL_MACHINE,
110 REGSTR_PATH_CURRENT_CONTROL_SET,
111 0,
112 KEY_QUERY_VALUE,
113 &ControlKey);
114 if (rc != ERROR_SUCCESS)
115 {
116 WARN("RegOpenKeyEx() failed with error %lu\n", rc);
117 goto cleanup;
118 }
119
120 rc = ReadRegSzKey(ControlKey, L"SystemStartOptions", &SystemStartOptions);
121 if (rc != ERROR_SUCCESS)
122 {
123 WARN("ReadRegSzKey() failed with error %lu\n", rc);
124 goto cleanup;
125 }
126
127 /* Check for CONSOLE switch in SystemStartOptions */
128 CurrentOption = SystemStartOptions;
129 while (CurrentOption)
130 {
131 NextOption = wcschr(CurrentOption, L' ');
132 if (NextOption)
133 *NextOption = L'\0';
134 if (_wcsicmp(CurrentOption, L"CONSOLE") == 0)
135 {
136 TRACE("Found 'CONSOLE' boot option\n");
137 ret = TRUE;
138 goto cleanup;
139 }
140 CurrentOption = NextOption ? NextOption + 1 : NULL;
141 }
142
143 cleanup:
144 if (ControlKey != NULL)
145 RegCloseKey(ControlKey);
146 HeapFree(GetProcessHeap(), 0, SystemStartOptions);
147 TRACE("IsConsoleShell() returning %d\n", ret);
148 return ret;
149 }
150
151 static
152 BOOL GetShell(
153 OUT WCHAR *CommandLine, /* must be at least MAX_PATH long */
154 IN HKEY hRootKey)
155 {
156 HKEY hKey;
157 DWORD Type, Size;
158 WCHAR Shell[MAX_PATH];
159 BOOL Ret = FALSE;
160 BOOL ConsoleShell = IsConsoleShell();
161 LONG rc;
162
163 TRACE("(%p, %p)\n", CommandLine, hRootKey);
164
165 rc = RegOpenKeyExW(hRootKey, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
166 0, KEY_QUERY_VALUE, &hKey);
167 if (rc == ERROR_SUCCESS)
168 {
169 Size = MAX_PATH * sizeof(WCHAR);
170 rc = RegQueryValueExW(hKey,
171 ConsoleShell ? L"ConsoleShell" : L"Shell",
172 NULL,
173 &Type,
174 (LPBYTE)Shell,
175 &Size);
176 if (rc == ERROR_SUCCESS)
177 {
178 if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
179 {
180 TRACE("Found command line %s\n", debugstr_w(Shell));
181 wcscpy(CommandLine, Shell);
182 Ret = TRUE;
183 }
184 else
185 WARN("Wrong type %lu (expected %u or %u)\n", Type, REG_SZ, REG_EXPAND_SZ);
186 }
187 else
188 WARN("RegQueryValueEx() failed with error %lu\n", rc);
189 RegCloseKey(hKey);
190 }
191 else
192 WARN("RegOpenKeyEx() failed with error %lu\n", rc);
193
194 return Ret;
195 }
196
197 static VOID
198 StartAutoApplications(
199 IN INT clsid)
200 {
201 WCHAR szPath[MAX_PATH] = {0};
202 HRESULT hResult;
203 HANDLE hFind;
204 WIN32_FIND_DATAW findData;
205 SHELLEXECUTEINFOW ExecInfo;
206 size_t len;
207
208 TRACE("(%d)\n", clsid);
209
210 hResult = SHGetFolderPathW(NULL, clsid, NULL, SHGFP_TYPE_CURRENT, szPath);
211 len = wcslen(szPath);
212 if (!SUCCEEDED(hResult) || len == 0)
213 {
214 WARN("SHGetFolderPath() failed with error %lu\n", GetLastError());
215 return;
216 }
217
218 wcscat(szPath, L"\\*");
219 hFind = FindFirstFileW(szPath, &findData);
220 if (hFind == INVALID_HANDLE_VALUE)
221 {
222 WARN("FindFirstFile(%s) failed with error %lu\n", debugstr_w(szPath), GetLastError());
223 return;
224 }
225
226 do
227 {
228 if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (findData.nFileSizeHigh || findData.nFileSizeLow))
229 {
230 memset(&ExecInfo, 0x0, sizeof(SHELLEXECUTEINFOW));
231 ExecInfo.cbSize = sizeof(ExecInfo);
232 wcscpy(&szPath[len+1], findData.cFileName);
233 ExecInfo.lpVerb = L"open";
234 ExecInfo.lpFile = szPath;
235 ExecInfo.lpDirectory = NULL;
236 TRACE("Executing %s in directory %s\n",
237 debugstr_w(findData.cFileName), debugstr_w(szPath));
238 ShellExecuteExW(&ExecInfo);
239 }
240 } while (FindNextFileW(hFind, &findData));
241 FindClose(hFind);
242 }
243
244 static BOOL
245 TryToStartShell(
246 IN LPCWSTR Shell)
247 {
248 STARTUPINFO si;
249 PROCESS_INFORMATION pi;
250 WCHAR ExpandedShell[MAX_PATH];
251
252 TRACE("(%s)\n", debugstr_w(Shell));
253
254 ZeroMemory(&si, sizeof(si));
255 si.cb = sizeof(si);
256 si.dwFlags = STARTF_USESHOWWINDOW;
257 si.wShowWindow = SW_SHOWNORMAL;
258 ZeroMemory(&pi, sizeof(pi));
259
260 ExpandEnvironmentStrings(Shell, ExpandedShell, MAX_PATH);
261
262 if (!CreateProcess(NULL,
263 ExpandedShell,
264 NULL,
265 NULL,
266 FALSE,
267 NORMAL_PRIORITY_CLASS,
268 NULL,
269 NULL,
270 &si,
271 &pi))
272 {
273 WARN("CreateProcess() failed with error %lu\n", GetLastError());
274 return FALSE;
275 }
276
277 StartAutoApplications(CSIDL_STARTUP);
278 StartAutoApplications(CSIDL_COMMON_STARTUP);
279 CloseHandle(pi.hProcess);
280 CloseHandle(pi.hThread);
281 return TRUE;
282 }
283
284 static
285 VOID StartShell(VOID)
286 {
287 WCHAR Shell[MAX_PATH];
288 TCHAR szMsg[RC_STRING_MAX_SIZE];
289 DWORD Type, Size;
290 DWORD Value = 0;
291 LONG rc;
292 HKEY hKey;
293
294 TRACE("()\n");
295
296 /* Safe Mode shell run */
297 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
298 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
299 0, KEY_QUERY_VALUE, &hKey);
300 if(rc == ERROR_SUCCESS)
301 {
302 Size = sizeof(Value);
303 rc = RegQueryValueExW(hKey, L"UseAlternateShell", NULL,
304 &Type, (LPBYTE)&Value, &Size);
305 if(rc == ERROR_SUCCESS)
306 {
307 RegCloseKey(hKey);
308 if(Type == REG_DWORD)
309 {
310 if(Value)
311 {
312 /* Safe Mode Alternate Shell required */
313 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
314 L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot",
315 0, KEY_READ, &hKey);
316 if(rc == ERROR_SUCCESS)
317 {
318 Size = MAX_PATH * sizeof(WCHAR);
319 rc = RegQueryValueExW(hKey, L"AlternateShell", NULL,
320 &Type, (LPBYTE)Shell, &Size);
321 if(rc == ERROR_SUCCESS)
322 {
323 RegCloseKey(hKey);
324 if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
325 {
326 TRACE("Key located - %s\n", debugstr_w(Shell));
327 /* Try to run alternate shell */
328 if (TryToStartShell(Shell))
329 {
330 TRACE("Alternate shell started (Safe Mode)\n");
331 return;
332 }
333 }
334 else
335 {
336 WARN("Wrong type %lu (expected %u or %u)\n",
337 Type, REG_SZ, REG_EXPAND_SZ);
338 }
339 }
340 else
341 {
342 WARN("Alternate shell in Safe Mode required but not specified.");
343 }
344 }
345 }
346 }
347 else
348 {
349 WARN("Wrong type %lu (expected %u)\n", Type, REG_DWORD);
350 }
351 }
352 }
353 /* Try to run shell in user key */
354 if (GetShell(Shell, HKEY_CURRENT_USER) && TryToStartShell(Shell))
355 {
356 TRACE("Started shell from HKEY_CURRENT_USER\n");
357 return;
358 }
359
360 /* Try to run shell in local machine key */
361 if (GetShell(Shell, HKEY_LOCAL_MACHINE) && TryToStartShell(Shell))
362 {
363 TRACE("Started shell from HKEY_LOCAL_MACHINE\n");
364 return;
365 }
366
367 /* Try default shell */
368 if (IsConsoleShell())
369 {
370 if (GetSystemDirectory(Shell, MAX_PATH - 8))
371 wcscat(Shell, L"\\cmd.exe");
372 else
373 wcscpy(Shell, L"cmd.exe");
374 }
375 else
376 {
377 if (GetWindowsDirectory(Shell, MAX_PATH - 13))
378 wcscat(Shell, L"\\explorer.exe");
379 else
380 wcscpy(Shell, L"explorer.exe");
381 }
382 if (!TryToStartShell(Shell))
383 {
384 WARN("Failed to start default shell %s\n", debugstr_w(Shell));
385 LoadString( GetModuleHandle(NULL), STRING_USERINIT_FAIL, szMsg, sizeof(szMsg) / sizeof(szMsg[0]));
386 MessageBox(0, szMsg, NULL, 0);
387 }
388 }
389
390 const WCHAR g_RegColorNames[][32] = {
391 L"Scrollbar", /* 00 = COLOR_SCROLLBAR */
392 L"Background", /* 01 = COLOR_DESKTOP */
393 L"ActiveTitle", /* 02 = COLOR_ACTIVECAPTION */
394 L"InactiveTitle", /* 03 = COLOR_INACTIVECAPTION */
395 L"Menu", /* 04 = COLOR_MENU */
396 L"Window", /* 05 = COLOR_WINDOW */
397 L"WindowFrame", /* 06 = COLOR_WINDOWFRAME */
398 L"MenuText", /* 07 = COLOR_MENUTEXT */
399 L"WindowText", /* 08 = COLOR_WINDOWTEXT */
400 L"TitleText", /* 09 = COLOR_CAPTIONTEXT */
401 L"ActiveBorder", /* 10 = COLOR_ACTIVEBORDER */
402 L"InactiveBorder", /* 11 = COLOR_INACTIVEBORDER */
403 L"AppWorkSpace", /* 12 = COLOR_APPWORKSPACE */
404 L"Hilight", /* 13 = COLOR_HIGHLIGHT */
405 L"HilightText", /* 14 = COLOR_HIGHLIGHTTEXT */
406 L"ButtonFace", /* 15 = COLOR_BTNFACE */
407 L"ButtonShadow", /* 16 = COLOR_BTNSHADOW */
408 L"GrayText", /* 17 = COLOR_GRAYTEXT */
409 L"ButtonText", /* 18 = COLOR_BTNTEXT */
410 L"InactiveTitleText", /* 19 = COLOR_INACTIVECAPTIONTEXT */
411 L"ButtonHilight", /* 20 = COLOR_BTNHIGHLIGHT */
412 L"ButtonDkShadow", /* 21 = COLOR_3DDKSHADOW */
413 L"ButtonLight", /* 22 = COLOR_3DLIGHT */
414 L"InfoText", /* 23 = COLOR_INFOTEXT */
415 L"InfoWindow", /* 24 = COLOR_INFOBK */
416 L"ButtonAlternateFace", /* 25 = COLOR_ALTERNATEBTNFACE */
417 L"HotTrackingColor", /* 26 = COLOR_HOTLIGHT */
418 L"GradientActiveTitle", /* 27 = COLOR_GRADIENTACTIVECAPTION */
419 L"GradientInactiveTitle", /* 28 = COLOR_GRADIENTINACTIVECAPTION */
420 L"MenuHilight", /* 29 = COLOR_MENUHILIGHT */
421 L"MenuBar" /* 30 = COLOR_MENUBAR */
422 };
423 #define NUM_SYSCOLORS (sizeof(g_RegColorNames) / sizeof(g_RegColorNames[0]))
424
425 static
426 COLORREF StrToColorref(
427 IN LPWSTR lpszCol)
428 {
429 BYTE rgb[3];
430
431 TRACE("(%s)\n", debugstr_w(lpszCol));
432
433 rgb[0] = StrToIntW(lpszCol);
434 lpszCol = StrChrW(lpszCol, L' ') + 1;
435 rgb[1] = StrToIntW(lpszCol);
436 lpszCol = StrChrW(lpszCol, L' ') + 1;
437 rgb[2] = StrToIntW(lpszCol);
438 return RGB(rgb[0], rgb[1], rgb[2]);
439 }
440
441 static
442 VOID SetUserSysColors(VOID)
443 {
444 HKEY hKey;
445 INT i;
446 WCHAR szColor[20];
447 DWORD Type, Size;
448 COLORREF crColor;
449 LONG rc;
450
451 TRACE("()\n");
452
453 rc = RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_COLORS,
454 0, KEY_QUERY_VALUE, &hKey);
455 if (rc != ERROR_SUCCESS)
456 {
457 WARN("RegOpenKeyEx() failed with error %lu\n", rc);
458 return;
459 }
460 for(i = 0; i < NUM_SYSCOLORS; i++)
461 {
462 Size = sizeof(szColor);
463 rc = RegQueryValueEx(hKey, g_RegColorNames[i], NULL, &Type,
464 (LPBYTE)szColor, &Size);
465 if (rc == ERROR_SUCCESS && Type == REG_SZ)
466 {
467 crColor = StrToColorref(szColor);
468 SetSysColors(1, &i, &crColor);
469 }
470 else
471 WARN("RegQueryValueEx(%s) failed with error %lu\n",
472 debugstr_w(g_RegColorNames[i]), rc);
473 }
474 RegCloseKey(hKey);
475 }
476
477 static
478 VOID SetUserWallpaper(VOID)
479 {
480 HKEY hKey;
481 DWORD Type, Size;
482 WCHAR szWallpaper[MAX_PATH + 1];
483 LONG rc;
484
485 TRACE("()\n");
486
487 rc = RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP,
488 0, KEY_QUERY_VALUE, &hKey);
489 if (rc == ERROR_SUCCESS)
490 {
491 Size = sizeof(szWallpaper);
492 rc = RegQueryValueEx(hKey,
493 L"Wallpaper",
494 NULL,
495 &Type,
496 (LPBYTE)szWallpaper,
497 &Size);
498 if (rc == ERROR_SUCCESS && Type == REG_SZ)
499 {
500 ExpandEnvironmentStrings(szWallpaper, szWallpaper, MAX_PATH);
501 TRACE("Using wallpaper %s\n", debugstr_w(szWallpaper));
502
503 /* Load and change the wallpaper */
504 SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, szWallpaper, SPIF_SENDCHANGE);
505 }
506 else
507 {
508 /* remove the wallpaper */
509 TRACE("No wallpaper set in registry (error %lu)\n", rc);
510 SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, NULL, SPIF_SENDCHANGE);
511 }
512 RegCloseKey(hKey);
513 }
514 else
515 WARN("RegOpenKeyEx() failed with error %lu\n", rc);
516 }
517
518 static
519 VOID SetUserSettings(VOID)
520 {
521 TRACE("()\n");
522
523 UpdatePerUserSystemParameters(1, TRUE);
524 SetUserSysColors();
525 SetUserWallpaper();
526 }
527
528 typedef DWORD (WINAPI *PCMP_REPORT_LOGON)(DWORD, DWORD);
529
530 static VOID
531 NotifyLogon(VOID)
532 {
533 HINSTANCE hModule;
534 PCMP_REPORT_LOGON CMP_Report_LogOn;
535
536 TRACE("()\n");
537
538 hModule = LoadLibrary(L"setupapi.dll");
539 if (hModule)
540 {
541 CMP_Report_LogOn = (PCMP_REPORT_LOGON)GetProcAddress(hModule, "CMP_Report_LogOn");
542 if (CMP_Report_LogOn)
543 CMP_Report_LogOn(CMP_MAGIC, GetCurrentProcessId());
544 else
545 WARN("GetProcAddress() failed\n");
546
547 FreeLibrary(hModule);
548 }
549 else
550 WARN("LoadLibrary() failed with error %lu\n", GetLastError());
551 }
552
553 #ifdef _MSC_VER
554 #pragma warning(disable : 4100)
555 #endif /* _MSC_VER */
556
557 int WINAPI
558 wWinMain(IN HINSTANCE hInst,
559 IN HINSTANCE hPrevInstance,
560 IN LPWSTR lpszCmdLine,
561 IN int nCmdShow)
562 {
563 SetUserSettings();
564 StartShell();
565 NotifyLogon();
566 return 0;
567 }
568
569 /* EOF */