xref: /reactos/base/system/userinit/userinit.c (revision b819608e)
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 */
570