xref: /reactos/dll/win32/syssetup/install.c (revision 62919904)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS system libraries
4  * PURPOSE:           System setup
5  * FILE:              dll/win32/syssetup/install.c
6  * PROGRAMER:         Eric Kohl
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include "precomp.h"
12 
13 #define COBJMACROS
14 
15 #include <io.h>
16 #include <wincon.h>
17 #include <winnls.h>
18 #include <winsvc.h>
19 #include <userenv.h>
20 #include <shlobj.h>
21 #include <shlwapi.h>
22 #include <shobjidl.h>
23 #include <rpcproxy.h>
24 #include <ndk/cmfuncs.h>
25 
26 #define NDEBUG
27 #include <debug.h>
28 
29 DWORD WINAPI
30 CMP_WaitNoPendingInstallEvents(DWORD dwTimeout);
31 
32 DWORD WINAPI
33 SetupStartService(LPCWSTR lpServiceName, BOOL bWait);
34 
35 /* GLOBALS ******************************************************************/
36 
37 HINF hSysSetupInf = INVALID_HANDLE_VALUE;
38 ADMIN_INFO AdminInfo;
39 
40 /* FUNCTIONS ****************************************************************/
41 
42 static VOID
43 FatalError(char *pszFmt,...)
44 {
45     char szBuffer[512];
46     va_list ap;
47 
48     va_start(ap, pszFmt);
49     vsprintf(szBuffer, pszFmt, ap);
50     va_end(ap);
51 
52     LogItem(NULL, L"Failed");
53 
54     strcat(szBuffer, "\nRebooting now!");
55     MessageBoxA(NULL,
56                 szBuffer,
57                 "ReactOS Setup",
58                 MB_OK);
59 }
60 
61 static HRESULT
62 CreateShellLink(
63     LPCWSTR pszLinkPath,
64     LPCWSTR pszCmd,
65     LPCWSTR pszArg,
66     LPCWSTR pszDir,
67     LPCWSTR pszIconPath,
68     INT iIconNr,
69     LPCWSTR pszComment)
70 {
71     IShellLinkW *psl;
72     IPersistFile *ppf;
73 
74     HRESULT hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID*)&psl);
75 
76     if (SUCCEEDED(hr))
77     {
78         hr = IShellLinkW_SetPath(psl, pszCmd);
79 
80         if (pszArg)
81             hr = IShellLinkW_SetArguments(psl, pszArg);
82 
83         if (pszDir)
84             hr = IShellLinkW_SetWorkingDirectory(psl, pszDir);
85 
86         if (pszIconPath)
87             hr = IShellLinkW_SetIconLocation(psl, pszIconPath, iIconNr);
88 
89         if (pszComment)
90             hr = IShellLinkW_SetDescription(psl, pszComment);
91 
92         hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
93 
94         if (SUCCEEDED(hr))
95         {
96             hr = IPersistFile_Save(ppf, pszLinkPath, TRUE);
97             IPersistFile_Release(ppf);
98         }
99 
100         IShellLinkW_Release(psl);
101     }
102 
103     return hr;
104 }
105 
106 
107 static BOOL
108 CreateShortcut(
109     LPCWSTR pszFolder,
110     LPCWSTR pszName,
111     LPCWSTR pszCommand,
112     LPCWSTR pszDescription,
113     INT iIconNr,
114     LPCWSTR pszWorkingDir)
115 {
116     DWORD dwLen;
117     LPWSTR Ptr;
118     LPWSTR lpFilePart;
119     WCHAR szPath[MAX_PATH];
120     WCHAR szWorkingDirBuf[MAX_PATH];
121 
122     /* If no working directory is provided, try to compute a default one */
123     if (pszWorkingDir == NULL || pszWorkingDir[0] == L'\0')
124     {
125         if (ExpandEnvironmentStringsW(pszCommand, szPath, ARRAYSIZE(szPath)) == 0)
126             wcscpy(szPath, pszCommand);
127 
128         dwLen = GetFullPathNameW(szPath,
129                                  ARRAYSIZE(szWorkingDirBuf),
130                                  szWorkingDirBuf,
131                                  &lpFilePart);
132         if (dwLen != 0 && dwLen <= ARRAYSIZE(szWorkingDirBuf))
133         {
134             /* Since those should only be called with (.exe) files,
135                lpFilePart has not to be NULL */
136             ASSERT(lpFilePart != NULL);
137 
138             /* We're only interested in the path. Cut the file name off.
139                Also remove the trailing backslash unless the working directory
140                is only going to be a drive, i.e. C:\ */
141             *(lpFilePart--) = L'\0';
142             if (!(lpFilePart - szWorkingDirBuf == 2 &&
143                   szWorkingDirBuf[1] == L':' && szWorkingDirBuf[2] == L'\\'))
144             {
145                 *lpFilePart = L'\0';
146             }
147             pszWorkingDir = szWorkingDirBuf;
148         }
149     }
150 
151     /* If we failed to compute a working directory, just do not use one */
152     if (pszWorkingDir && pszWorkingDir[0] == L'\0')
153         pszWorkingDir = NULL;
154 
155     /* Build the shortcut file name */
156     wcscpy(szPath, pszFolder);
157     Ptr = PathAddBackslash(szPath);
158     wcscpy(Ptr, pszName);
159 
160     /* Create the shortcut */
161     return SUCCEEDED(CreateShellLink(szPath,
162                                      pszCommand,
163                                      L"",
164                                      pszWorkingDir,
165                                      /* Special value to indicate no icon */
166                                      (iIconNr != -1 ? pszCommand : NULL),
167                                      iIconNr,
168                                      pszDescription));
169 }
170 
171 
172 static BOOL CreateShortcutsFromSection(HINF hinf, LPWSTR pszSection, LPCWSTR pszFolder)
173 {
174     INFCONTEXT Context;
175     DWORD dwFieldCount;
176     INT iIconNr;
177     WCHAR szCommand[MAX_PATH];
178     WCHAR szName[MAX_PATH];
179     WCHAR szDescription[MAX_PATH];
180     WCHAR szDirectory[MAX_PATH];
181 
182     if (!SetupFindFirstLine(hinf, pszSection, NULL, &Context))
183         return FALSE;
184 
185     do
186     {
187         dwFieldCount = SetupGetFieldCount(&Context);
188         if (dwFieldCount < 3)
189             continue;
190 
191         if (!SetupGetStringFieldW(&Context, 1, szCommand, ARRAYSIZE(szCommand), NULL))
192             continue;
193 
194         if (!SetupGetStringFieldW(&Context, 2, szName, ARRAYSIZE(szName), NULL))
195             continue;
196 
197         if (!SetupGetStringFieldW(&Context, 3, szDescription, ARRAYSIZE(szDescription), NULL))
198             continue;
199 
200         if (dwFieldCount < 4 || !SetupGetIntField(&Context, 4, &iIconNr))
201             iIconNr = -1; /* Special value to indicate no icon */
202 
203         if (dwFieldCount < 5 || !SetupGetStringFieldW(&Context, 5, szDirectory, ARRAYSIZE(szDirectory), NULL))
204             szDirectory[0] = L'\0';
205 
206         wcscat(szName, L".lnk");
207 
208         CreateShortcut(pszFolder, szName, szCommand, szDescription, iIconNr, szDirectory);
209 
210     } while (SetupFindNextLine(&Context, &Context));
211 
212     return TRUE;
213 }
214 
215 static BOOL CreateShortcuts(HINF hinf, LPCWSTR szSection)
216 {
217     INFCONTEXT Context;
218     WCHAR szPath[MAX_PATH];
219     WCHAR szFolder[MAX_PATH];
220     WCHAR szFolderSection[MAX_PATH];
221     INT csidl;
222 
223     CoInitialize(NULL);
224 
225     if (!SetupFindFirstLine(hinf, szSection, NULL, &Context))
226         return FALSE;
227 
228     do
229     {
230         if (SetupGetFieldCount(&Context) < 2)
231             continue;
232 
233         if (!SetupGetStringFieldW(&Context, 0, szFolderSection, ARRAYSIZE(szFolderSection), NULL))
234             continue;
235 
236         if (!SetupGetIntField(&Context, 1, &csidl))
237             continue;
238 
239         if (!SetupGetStringFieldW(&Context, 2, szFolder, ARRAYSIZE(szFolder), NULL))
240             continue;
241 
242         if (FAILED(SHGetFolderPathAndSubDirW(NULL, csidl|CSIDL_FLAG_CREATE, (HANDLE)-1, SHGFP_TYPE_DEFAULT, szFolder, szPath)))
243             continue;
244 
245         CreateShortcutsFromSection(hinf, szFolderSection, szPath);
246 
247     } while (SetupFindNextLine(&Context, &Context));
248 
249     CoUninitialize();
250 
251     return TRUE;
252 }
253 
254 static VOID
255 CreateTempDir(
256     IN LPCWSTR VarName)
257 {
258     WCHAR szTempDir[MAX_PATH];
259     WCHAR szBuffer[MAX_PATH];
260     DWORD dwLength;
261     HKEY hKey;
262 
263     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
264                       L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
265                       0,
266                       KEY_QUERY_VALUE,
267                       &hKey) != ERROR_SUCCESS)
268     {
269         FatalError("Error: %lu\n", GetLastError());
270         return;
271     }
272 
273     /* Get temp dir */
274     dwLength = sizeof(szBuffer);
275     if (RegQueryValueExW(hKey,
276                          VarName,
277                          NULL,
278                          NULL,
279                          (LPBYTE)szBuffer,
280                          &dwLength) != ERROR_SUCCESS)
281     {
282         FatalError("Error: %lu\n", GetLastError());
283         goto cleanup;
284     }
285 
286     /* Expand it */
287     if (!ExpandEnvironmentStringsW(szBuffer, szTempDir, ARRAYSIZE(szTempDir)))
288     {
289         FatalError("Error: %lu\n", GetLastError());
290         goto cleanup;
291     }
292 
293     /* Create profiles directory */
294     if (!CreateDirectoryW(szTempDir, NULL))
295     {
296         if (GetLastError() != ERROR_ALREADY_EXISTS)
297         {
298             FatalError("Error: %lu\n", GetLastError());
299             goto cleanup;
300         }
301     }
302 
303 cleanup:
304     RegCloseKey(hKey);
305 }
306 
307 static BOOL
308 InstallSysSetupInfDevices(VOID)
309 {
310     INFCONTEXT InfContext;
311     WCHAR szLineBuffer[256];
312     DWORD dwLineLength;
313 
314     if (!SetupFindFirstLineW(hSysSetupInf,
315                             L"DeviceInfsToInstall",
316                             NULL,
317                             &InfContext))
318     {
319         return FALSE;
320     }
321 
322     do
323     {
324         if (!SetupGetStringFieldW(&InfContext,
325                                   0,
326                                   szLineBuffer,
327                                   ARRAYSIZE(szLineBuffer),
328                                   &dwLineLength))
329         {
330             return FALSE;
331         }
332 
333         if (!SetupDiInstallClassW(NULL, szLineBuffer, DI_QUIETINSTALL, NULL))
334         {
335             return FALSE;
336         }
337     }
338     while (SetupFindNextLine(&InfContext, &InfContext));
339 
340     return TRUE;
341 }
342 
343 static BOOL
344 InstallSysSetupInfComponents(VOID)
345 {
346     INFCONTEXT InfContext;
347     WCHAR szNameBuffer[256];
348     WCHAR szSectionBuffer[256];
349     HINF hComponentInf = INVALID_HANDLE_VALUE;
350 
351     if (!SetupFindFirstLineW(hSysSetupInf,
352                              L"Infs.Always",
353                              NULL,
354                              &InfContext))
355     {
356         DPRINT("No Inf.Always section found\n");
357     }
358     else
359     {
360         do
361         {
362             if (!SetupGetStringFieldW(&InfContext,
363                                       1, // Get the component name
364                                       szNameBuffer,
365                                       ARRAYSIZE(szNameBuffer),
366                                       NULL))
367             {
368                 FatalError("Error while trying to get component name \n");
369                 return FALSE;
370             }
371 
372             if (!SetupGetStringFieldW(&InfContext,
373                                       2, // Get the component install section
374                                       szSectionBuffer,
375                                       ARRAYSIZE(szSectionBuffer),
376                                       NULL))
377             {
378                 FatalError("Error while trying to get component install section \n");
379                 return FALSE;
380             }
381 
382             DPRINT("Trying to execute install section '%S' from '%S' \n", szSectionBuffer, szNameBuffer);
383 
384             hComponentInf = SetupOpenInfFileW(szNameBuffer,
385                                               NULL,
386                                               INF_STYLE_WIN4,
387                                               NULL);
388 
389             if (hComponentInf == INVALID_HANDLE_VALUE)
390             {
391                 FatalError("SetupOpenInfFileW() failed to open '%S' (Error: %lu)\n", szNameBuffer, GetLastError());
392                 return FALSE;
393             }
394 
395             if (!SetupInstallFromInfSectionW(NULL,
396                                              hComponentInf,
397                                              szSectionBuffer,
398                                              SPINST_ALL,
399                                              NULL,
400                                              NULL,
401                                              SP_COPY_NEWER,
402                                              SetupDefaultQueueCallbackW,
403                                              NULL,
404                                              NULL,
405                                              NULL))
406            {
407                 FatalError("Error while trying to install : %S (Error: %lu)\n", szNameBuffer, GetLastError());
408                 SetupCloseInfFile(hComponentInf);
409                 return FALSE;
410            }
411 
412            SetupCloseInfFile(hComponentInf);
413         }
414         while (SetupFindNextLine(&InfContext, &InfContext));
415     }
416 
417     return TRUE;
418 }
419 
420 
421 
422 BOOL
423 RegisterTypeLibraries(HINF hinf, LPCWSTR szSection)
424 {
425     INFCONTEXT InfContext;
426     BOOL res;
427     WCHAR szName[MAX_PATH];
428     WCHAR szPath[MAX_PATH];
429     INT csidl;
430     LPWSTR p;
431     HMODULE hmod;
432     HRESULT hret;
433 
434     /* Begin iterating the entries in the inf section */
435     res = SetupFindFirstLine(hinf, szSection, NULL, &InfContext);
436     if (!res) return FALSE;
437 
438     do
439     {
440         /* Get the name of the current type library */
441         if (!SetupGetStringFieldW(&InfContext, 1, szName, ARRAYSIZE(szName), NULL))
442         {
443             FatalError("SetupGetStringFieldW failed\n");
444             continue;
445         }
446 
447         if (!SetupGetIntField(&InfContext, 2, &csidl))
448             csidl = CSIDL_SYSTEM;
449 
450         hret = SHGetFolderPathW(NULL, csidl, NULL, 0, szPath);
451         if (FAILED(hret))
452         {
453             FatalError("SHGetFolderPathW failed hret=0x%lx\n", hret);
454             continue;
455         }
456 
457         p = PathAddBackslash(szPath);
458         wcscpy(p, szName);
459 
460         hmod = LoadLibraryW(szName);
461         if (hmod == NULL)
462         {
463             FatalError("LoadLibraryW failed\n");
464             continue;
465         }
466 
467         __wine_register_resources(hmod);
468 
469     } while (SetupFindNextLine(&InfContext, &InfContext));
470 
471     return TRUE;
472 }
473 
474 static BOOL
475 EnableUserModePnpManager(VOID)
476 {
477     SC_HANDLE hSCManager = NULL;
478     SC_HANDLE hService = NULL;
479     BOOL bRet = FALSE;
480 
481     hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
482     if (hSCManager == NULL)
483     {
484         DPRINT1("Unable to open the service control manager.\n");
485         DPRINT1("Last Error %d\n", GetLastError());
486         goto cleanup;
487     }
488 
489     hService = OpenServiceW(hSCManager,
490                             L"PlugPlay",
491                             SERVICE_CHANGE_CONFIG | SERVICE_START);
492     if (hService == NULL)
493     {
494         DPRINT1("Unable to open PlugPlay service\n");
495         goto cleanup;
496     }
497 
498     bRet = ChangeServiceConfigW(hService,
499                                 SERVICE_NO_CHANGE,
500                                 SERVICE_AUTO_START,
501                                 SERVICE_NO_CHANGE,
502                                 NULL, NULL, NULL,
503                                 NULL, NULL, NULL, NULL);
504     if (!bRet)
505     {
506         DPRINT1("Unable to change the service configuration\n");
507         goto cleanup;
508     }
509 
510     bRet = StartServiceW(hService, 0, NULL);
511     if (!bRet && (GetLastError() != ERROR_SERVICE_ALREADY_RUNNING))
512     {
513         DPRINT1("Unable to start service\n");
514         goto cleanup;
515     }
516 
517     bRet = TRUE;
518 
519 cleanup:
520     if (hService != NULL)
521         CloseServiceHandle(hService);
522     if (hSCManager != NULL)
523         CloseServiceHandle(hSCManager);
524     return bRet;
525 }
526 
527 static INT_PTR CALLBACK
528 StatusMessageWindowProc(
529     IN HWND hwndDlg,
530     IN UINT uMsg,
531     IN WPARAM wParam,
532     IN LPARAM lParam)
533 {
534     UNREFERENCED_PARAMETER(wParam);
535 
536     switch (uMsg)
537     {
538         case WM_INITDIALOG:
539         {
540             WCHAR szMsg[256];
541 
542             if (!LoadStringW(hDllInstance, IDS_STATUS_INSTALL_DEV, szMsg, ARRAYSIZE(szMsg)))
543                 return FALSE;
544             SetDlgItemTextW(hwndDlg, IDC_STATUSLABEL, szMsg);
545             return TRUE;
546         }
547     }
548     return FALSE;
549 }
550 
551 static DWORD WINAPI
552 ShowStatusMessageThread(
553     IN LPVOID lpParameter)
554 {
555     HWND hWnd, hItem;
556     MSG Msg;
557     UNREFERENCED_PARAMETER(lpParameter);
558 
559     hWnd = CreateDialogParam(hDllInstance,
560                              MAKEINTRESOURCE(IDD_STATUSWINDOW_DLG),
561                              GetDesktopWindow(),
562                              StatusMessageWindowProc,
563                              (LPARAM)NULL);
564     if (!hWnd)
565         return 0;
566 
567     ShowWindow(hWnd, SW_SHOW);
568 
569     hItem = GetDlgItem(hWnd, IDC_STATUSPROGRESS);
570     if (hItem)
571     {
572         PostMessage(hItem, PBM_SETMARQUEE, TRUE, 40);
573     }
574 
575     /* Message loop for the Status window */
576     while (GetMessage(&Msg, NULL, 0, 0))
577     {
578         TranslateMessage(&Msg);
579         DispatchMessage(&Msg);
580     }
581 
582     EndDialog(hWnd, 0);
583 
584     return 0;
585 }
586 
587 static LONG
588 ReadRegSzKey(
589     IN HKEY hKey,
590     IN LPCWSTR pszKey,
591     OUT LPWSTR* pValue)
592 {
593     LONG rc;
594     DWORD dwType;
595     DWORD cbData = 0;
596     LPWSTR pwszValue;
597 
598     if (!pValue)
599         return ERROR_INVALID_PARAMETER;
600 
601     *pValue = NULL;
602     rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
603     if (rc != ERROR_SUCCESS)
604         return rc;
605     if (dwType != REG_SZ)
606         return ERROR_FILE_NOT_FOUND;
607     pwszValue = HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
608     if (!pwszValue)
609         return ERROR_NOT_ENOUGH_MEMORY;
610     rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)pwszValue, &cbData);
611     if (rc != ERROR_SUCCESS)
612     {
613         HeapFree(GetProcessHeap(), 0, pwszValue);
614         return rc;
615     }
616     /* NULL-terminate the string */
617     pwszValue[cbData / sizeof(WCHAR)] = '\0';
618 
619     *pValue = pwszValue;
620     return ERROR_SUCCESS;
621 }
622 
623 static BOOL
624 IsConsoleBoot(VOID)
625 {
626     HKEY hControlKey = NULL;
627     LPWSTR pwszSystemStartOptions = NULL;
628     LPWSTR pwszCurrentOption, pwszNextOption; /* Pointers into SystemStartOptions */
629     BOOL bConsoleBoot = FALSE;
630     LONG rc;
631 
632     rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
633                        L"SYSTEM\\CurrentControlSet\\Control",
634                        0,
635                        KEY_QUERY_VALUE,
636                        &hControlKey);
637     if (rc != ERROR_SUCCESS)
638         goto cleanup;
639 
640     rc = ReadRegSzKey(hControlKey, L"SystemStartOptions", &pwszSystemStartOptions);
641     if (rc != ERROR_SUCCESS)
642         goto cleanup;
643 
644     /* Check for CONSOLE switch in SystemStartOptions */
645     pwszCurrentOption = pwszSystemStartOptions;
646     while (pwszCurrentOption)
647     {
648         pwszNextOption = wcschr(pwszCurrentOption, L' ');
649         if (pwszNextOption)
650             *pwszNextOption = L'\0';
651         if (wcsicmp(pwszCurrentOption, L"CONSOLE") == 0)
652         {
653             DPRINT("Found %S. Switching to console boot\n", pwszCurrentOption);
654             bConsoleBoot = TRUE;
655             goto cleanup;
656         }
657         pwszCurrentOption = pwszNextOption ? pwszNextOption + 1 : NULL;
658     }
659 
660 cleanup:
661     if (hControlKey != NULL)
662         RegCloseKey(hControlKey);
663     if (pwszSystemStartOptions)
664         HeapFree(GetProcessHeap(), 0, pwszSystemStartOptions);
665     return bConsoleBoot;
666 }
667 
668 static BOOL
669 CommonInstall(VOID)
670 {
671     HANDLE hThread = NULL;
672     BOOL bResult = FALSE;
673 
674     hSysSetupInf = SetupOpenInfFileW(L"syssetup.inf",
675                                      NULL,
676                                      INF_STYLE_WIN4,
677                                      NULL);
678     if (hSysSetupInf == INVALID_HANDLE_VALUE)
679     {
680         FatalError("SetupOpenInfFileW() failed to open 'syssetup.inf' (Error: %lu)\n", GetLastError());
681         return FALSE;
682     }
683 
684     if (!InstallSysSetupInfDevices())
685     {
686         FatalError("InstallSysSetupInfDevices() failed!\n");
687         goto Exit;
688     }
689 
690     if(!InstallSysSetupInfComponents())
691     {
692         FatalError("InstallSysSetupInfComponents() failed!\n");
693         goto Exit;
694     }
695 
696     if (!IsConsoleBoot())
697     {
698         hThread = CreateThread(NULL,
699                                0,
700                                ShowStatusMessageThread,
701                                NULL,
702                                0,
703                                NULL);
704     }
705 
706     if (!EnableUserModePnpManager())
707     {
708         FatalError("EnableUserModePnpManager() failed!\n");
709         goto Exit;
710     }
711 
712     if (CMP_WaitNoPendingInstallEvents(INFINITE) != WAIT_OBJECT_0)
713     {
714         FatalError("CMP_WaitNoPendingInstallEvents() failed!\n");
715         goto Exit;
716     }
717 
718     bResult = TRUE;
719 
720 Exit:
721 
722     if (bResult == FALSE)
723     {
724         SetupCloseInfFile(hSysSetupInf);
725     }
726 
727     if (hThread != NULL)
728     {
729         PostThreadMessage(GetThreadId(hThread), WM_QUIT, 0, 0);
730         WaitForSingleObject(hThread, INFINITE);
731         CloseHandle(hThread);
732     }
733 
734     return bResult;
735 }
736 
737 static
738 DWORD
739 InstallLiveCD(VOID)
740 {
741     STARTUPINFOW StartupInfo;
742     PROCESS_INFORMATION ProcessInformation;
743     BOOL bRes;
744 
745     if (!CommonInstall())
746         goto error;
747 
748     /* Install the TCP/IP protocol driver */
749     bRes = InstallNetworkComponent(L"MS_TCPIP");
750     if (!bRes && GetLastError() != ERROR_FILE_NOT_FOUND)
751     {
752         DPRINT("InstallNetworkComponent() failed with error 0x%lx\n", GetLastError());
753     }
754     else
755     {
756         /* Start the TCP/IP protocol driver */
757         SetupStartService(L"Tcpip", FALSE);
758         SetupStartService(L"Dhcp", FALSE);
759     }
760 
761     /* Register components */
762     _SEH2_TRY
763     {
764         if (!SetupInstallFromInfSectionW(NULL,
765                                          hSysSetupInf, L"RegistrationPhase2",
766                                          SPINST_ALL,
767                                          0, NULL, 0, NULL, NULL, NULL, NULL))
768         {
769             DPRINT1("SetupInstallFromInfSectionW failed!\n");
770         }
771 
772         RegisterTypeLibraries(hSysSetupInf, L"TypeLibraries");
773     }
774     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
775     {
776         DPRINT1("Catching exception\n");
777     }
778     _SEH2_END;
779 
780     SetupCloseInfFile(hSysSetupInf);
781 
782     /* Run the shell */
783     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
784     StartupInfo.cb = sizeof(StartupInfo);
785     bRes = CreateProcessW(L"userinit.exe",
786                           NULL,
787                           NULL,
788                           NULL,
789                           FALSE,
790                           0,
791                           NULL,
792                           NULL,
793                           &StartupInfo,
794                           &ProcessInformation);
795     if (!bRes)
796         goto error;
797 
798     CloseHandle(ProcessInformation.hThread);
799     CloseHandle(ProcessInformation.hProcess);
800 
801     return 0;
802 
803 error:
804     MessageBoxW(
805         NULL,
806         L"Failed to load LiveCD! You can shutdown your computer, or press ENTER to reboot.",
807         L"ReactOS LiveCD",
808         MB_OK);
809     return 0;
810 }
811 
812 
813 static BOOL
814 SetSetupType(DWORD dwSetupType)
815 {
816     DWORD dwError;
817     HKEY hKey;
818 
819     dwError = RegOpenKeyExW(
820         HKEY_LOCAL_MACHINE,
821         L"SYSTEM\\Setup",
822         0,
823         KEY_SET_VALUE,
824         &hKey);
825     if (dwError != ERROR_SUCCESS)
826         return FALSE;
827 
828     dwError = RegSetValueExW(
829         hKey,
830         L"SetupType",
831         0,
832         REG_DWORD,
833         (LPBYTE)&dwSetupType,
834         sizeof(DWORD));
835     RegCloseKey(hKey);
836     if (dwError != ERROR_SUCCESS)
837         return FALSE;
838 
839     return TRUE;
840 }
841 
842 static DWORD CALLBACK
843 HotkeyThread(LPVOID Parameter)
844 {
845     ATOM hotkey;
846     MSG msg;
847 
848     DPRINT("HotkeyThread start\n");
849 
850     hotkey = GlobalAddAtomW(L"Setup Shift+F10 Hotkey");
851 
852     if (!RegisterHotKey(NULL, hotkey, MOD_SHIFT, VK_F10))
853         DPRINT1("RegisterHotKey failed with %lu\n", GetLastError());
854 
855     while (GetMessage(&msg, NULL, 0, 0))
856     {
857         if (msg.hwnd == NULL && msg.message == WM_HOTKEY && msg.wParam == hotkey)
858         {
859             STARTUPINFOW si = { sizeof(si) };
860             PROCESS_INFORMATION pi;
861 
862             if (CreateProcessW(L"cmd.exe",
863                                NULL,
864                                NULL,
865                                NULL,
866                                FALSE,
867                                CREATE_NEW_CONSOLE,
868                                NULL,
869                                NULL,
870                                &si,
871                                &pi))
872             {
873                 CloseHandle(pi.hProcess);
874                 CloseHandle(pi.hThread);
875             }
876             else
877             {
878                 DPRINT1("Failed to launch command prompt: %lu\n", GetLastError());
879             }
880         }
881     }
882 
883     UnregisterHotKey(NULL, hotkey);
884     GlobalDeleteAtom(hotkey);
885 
886     DPRINT("HotkeyThread terminate\n");
887     return 0;
888 }
889 
890 
891 static
892 BOOL
893 InitializeProgramFilesDir(VOID)
894 {
895     LONG Error;
896     HKEY hKey;
897     DWORD dwLength;
898     WCHAR szProgramFilesDirPath[MAX_PATH];
899     WCHAR szCommonFilesDirPath[MAX_PATH];
900     WCHAR szBuffer[MAX_PATH];
901 
902     /* Load 'Program Files' location */
903     if (!LoadStringW(hDllInstance,
904                      IDS_PROGRAMFILES,
905                      szBuffer,
906                      ARRAYSIZE(szBuffer)))
907     {
908         DPRINT1("Error: %lu\n", GetLastError());
909         return FALSE;
910     }
911 
912     if (!LoadStringW(hDllInstance,
913                      IDS_COMMONFILES,
914                      szCommonFilesDirPath,
915                      ARRAYSIZE(szCommonFilesDirPath)))
916     {
917         DPRINT1("Warning: %lu\n", GetLastError());
918     }
919 
920     /* Expand it */
921     if (!ExpandEnvironmentStringsW(szBuffer,
922                                    szProgramFilesDirPath,
923                                    ARRAYSIZE(szProgramFilesDirPath)))
924     {
925         DPRINT1("Error: %lu\n", GetLastError());
926         return FALSE;
927     }
928 
929     wcscpy(szBuffer, szProgramFilesDirPath);
930     wcscat(szBuffer, L"\\");
931     wcscat(szBuffer, szCommonFilesDirPath);
932 
933     if (!ExpandEnvironmentStringsW(szBuffer,
934                                    szCommonFilesDirPath,
935                                    ARRAYSIZE(szCommonFilesDirPath)))
936     {
937         DPRINT1("Warning: %lu\n", GetLastError());
938     }
939 
940     /* Store it */
941     Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
942                           L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
943                           0,
944                           KEY_SET_VALUE,
945                           &hKey);
946     if (Error != ERROR_SUCCESS)
947     {
948         DPRINT1("Error: %lu\n", Error);
949         return FALSE;
950     }
951 
952     dwLength = (wcslen(szProgramFilesDirPath) + 1) * sizeof(WCHAR);
953     Error = RegSetValueExW(hKey,
954                            L"ProgramFilesDir",
955                            0,
956                            REG_SZ,
957                            (LPBYTE)szProgramFilesDirPath,
958                            dwLength);
959     if (Error != ERROR_SUCCESS)
960     {
961         DPRINT1("Error: %lu\n", Error);
962         RegCloseKey(hKey);
963         return FALSE;
964     }
965 
966     dwLength = (wcslen(szCommonFilesDirPath) + 1) * sizeof(WCHAR);
967     Error = RegSetValueExW(hKey,
968                            L"CommonFilesDir",
969                            0,
970                            REG_SZ,
971                            (LPBYTE)szCommonFilesDirPath,
972                            dwLength);
973     if (Error != ERROR_SUCCESS)
974     {
975         DPRINT1("Warning: %lu\n", Error);
976     }
977 
978     RegCloseKey(hKey);
979 
980     /* Create directory */
981     // FIXME: Security!
982     if (!CreateDirectoryW(szProgramFilesDirPath, NULL))
983     {
984         if (GetLastError() != ERROR_ALREADY_EXISTS)
985         {
986             DPRINT1("Error: %lu\n", GetLastError());
987             return FALSE;
988         }
989     }
990 
991     /* Create directory */
992     // FIXME: Security!
993     if (!CreateDirectoryW(szCommonFilesDirPath, NULL))
994     {
995         if (GetLastError() != ERROR_ALREADY_EXISTS)
996         {
997             DPRINT1("Warning: %lu\n", GetLastError());
998             // return FALSE;
999         }
1000     }
1001 
1002     return TRUE;
1003 }
1004 
1005 
1006 static
1007 VOID
1008 InitializeDefaultUserLocale(VOID)
1009 {
1010     WCHAR szBuffer[80];
1011     PWSTR ptr;
1012     HKEY hLocaleKey;
1013     DWORD ret;
1014     DWORD dwSize;
1015     LCID lcid;
1016     INT i;
1017 
1018     struct {LCTYPE LCType; PWSTR pValue;} LocaleData[] = {
1019         /* Number */
1020         {LOCALE_SDECIMAL, L"sDecimal"},
1021         {LOCALE_STHOUSAND, L"sThousand"},
1022         {LOCALE_SNEGATIVESIGN, L"sNegativeSign"},
1023         {LOCALE_SPOSITIVESIGN, L"sPositiveSign"},
1024         {LOCALE_SGROUPING, L"sGrouping"},
1025         {LOCALE_SLIST, L"sList"},
1026         {LOCALE_SNATIVEDIGITS, L"sNativeDigits"},
1027         {LOCALE_INEGNUMBER, L"iNegNumber"},
1028         {LOCALE_IDIGITS, L"iDigits"},
1029         {LOCALE_ILZERO, L"iLZero"},
1030         {LOCALE_IMEASURE, L"iMeasure"},
1031         {LOCALE_IDIGITSUBSTITUTION, L"NumShape"},
1032 
1033         /* Currency */
1034         {LOCALE_SCURRENCY, L"sCurrency"},
1035         {LOCALE_SMONDECIMALSEP, L"sMonDecimalSep"},
1036         {LOCALE_SMONTHOUSANDSEP, L"sMonThousandSep"},
1037         {LOCALE_SMONGROUPING, L"sMonGrouping"},
1038         {LOCALE_ICURRENCY, L"iCurrency"},
1039         {LOCALE_INEGCURR, L"iNegCurr"},
1040         {LOCALE_ICURRDIGITS, L"iCurrDigits"},
1041 
1042         /* Time */
1043         {LOCALE_STIMEFORMAT, L"sTimeFormat"},
1044         {LOCALE_STIME, L"sTime"},
1045         {LOCALE_S1159, L"s1159"},
1046         {LOCALE_S2359, L"s2359"},
1047         {LOCALE_ITIME, L"iTime"},
1048         {LOCALE_ITIMEMARKPOSN, L"iTimePrefix"},
1049         {LOCALE_ITLZERO, L"iTLZero"},
1050 
1051         /* Date */
1052         {LOCALE_SLONGDATE, L"sLongDate"},
1053         {LOCALE_SSHORTDATE, L"sShortDate"},
1054         {LOCALE_SDATE, L"sDate"},
1055         {LOCALE_IFIRSTDAYOFWEEK, L"iFirstDayOfWeek"},
1056         {LOCALE_IFIRSTWEEKOFYEAR, L"iFirstWeekOfYear"},
1057         {LOCALE_IDATE, L"iDate"},
1058         {LOCALE_ICALENDARTYPE, L"iCalendarType"},
1059 
1060         /* Misc */
1061         {LOCALE_SCOUNTRY, L"sCountry"},
1062         {LOCALE_SABBREVLANGNAME, L"sLanguage"},
1063         {LOCALE_ICOUNTRY, L"iCountry"},
1064         {0, NULL}};
1065 
1066     ret = RegOpenKeyExW(HKEY_USERS,
1067                         L".DEFAULT\\Control Panel\\International",
1068                         0,
1069                         KEY_READ | KEY_WRITE,
1070                         &hLocaleKey);
1071     if (ret != ERROR_SUCCESS)
1072     {
1073         return;
1074     }
1075 
1076     dwSize = 9 * sizeof(WCHAR);
1077     ret = RegQueryValueExW(hLocaleKey,
1078                            L"Locale",
1079                            NULL,
1080                            NULL,
1081                            (PBYTE)szBuffer,
1082                            &dwSize);
1083     if (ret != ERROR_SUCCESS)
1084         goto done;
1085 
1086     lcid = (LCID)wcstoul(szBuffer, &ptr, 16);
1087     if (lcid == 0)
1088         goto done;
1089 
1090     i = 0;
1091     while (LocaleData[i].pValue != NULL)
1092     {
1093         if (GetLocaleInfoW(lcid,
1094                            LocaleData[i].LCType | LOCALE_NOUSEROVERRIDE,
1095                            szBuffer,
1096                            ARRAYSIZE(szBuffer)))
1097         {
1098             RegSetValueExW(hLocaleKey,
1099                            LocaleData[i].pValue,
1100                            0,
1101                            REG_SZ,
1102                            (PBYTE)szBuffer,
1103                            (wcslen(szBuffer) + 1) * sizeof(WCHAR));
1104         }
1105 
1106         i++;
1107     }
1108 
1109 done:
1110     RegCloseKey(hLocaleKey);
1111 }
1112 
1113 
1114 static
1115 DWORD
1116 SaveDefaultUserHive(VOID)
1117 {
1118     WCHAR szDefaultUserHive[MAX_PATH];
1119     HKEY hUserKey = NULL;
1120     DWORD cchSize;
1121     DWORD dwError;
1122 
1123     DPRINT("SaveDefaultUserHive()\n");
1124 
1125     cchSize = ARRAYSIZE(szDefaultUserHive);
1126     GetDefaultUserProfileDirectoryW(szDefaultUserHive, &cchSize);
1127 
1128     wcscat(szDefaultUserHive, L"\\ntuser.dat");
1129 
1130     dwError = RegOpenKeyExW(HKEY_USERS,
1131                             L".DEFAULT",
1132                             0,
1133                             KEY_READ,
1134                             &hUserKey);
1135     if (dwError != ERROR_SUCCESS)
1136     {
1137         DPRINT1("RegOpenKeyExW() failed (Error %lu)\n", dwError);
1138         return dwError;
1139     }
1140 
1141     pSetupEnablePrivilege(L"SeBackupPrivilege", TRUE);
1142 
1143     /* Save the Default hive */
1144     dwError = RegSaveKeyExW(hUserKey,
1145                             szDefaultUserHive,
1146                             NULL,
1147                             REG_STANDARD_FORMAT);
1148     if (dwError == ERROR_ALREADY_EXISTS)
1149     {
1150         WCHAR szBackupHive[MAX_PATH];
1151 
1152         /* Build the backup hive file name by replacing the extension */
1153         wcscpy(szBackupHive, szDefaultUserHive);
1154         wcscpy(&szBackupHive[wcslen(szBackupHive) - 4], L".bak");
1155 
1156         /* Back up the existing default user hive by renaming it, replacing any possible existing old backup */
1157         if (!MoveFileExW(szDefaultUserHive,
1158                          szBackupHive,
1159                          MOVEFILE_REPLACE_EXISTING))
1160         {
1161             dwError = GetLastError();
1162             DPRINT1("Failed to create a default-user hive backup '%S', MoveFileExW failed (Error %lu)\n",
1163                     szBackupHive, dwError);
1164         }
1165         else
1166         {
1167             /* The backup has been done, retry saving the Default hive */
1168             dwError = RegSaveKeyExW(hUserKey,
1169                                     szDefaultUserHive,
1170                                     NULL,
1171                                     REG_STANDARD_FORMAT);
1172         }
1173     }
1174     if (dwError != ERROR_SUCCESS)
1175     {
1176         DPRINT1("RegSaveKeyExW() failed (Error %lu)\n", dwError);
1177     }
1178 
1179     pSetupEnablePrivilege(L"SeBackupPrivilege", FALSE);
1180 
1181     RegCloseKey(hUserKey);
1182 
1183     return dwError;
1184 }
1185 
1186 
1187 static
1188 DWORD
1189 InstallReactOS(VOID)
1190 {
1191     WCHAR szBuffer[MAX_PATH];
1192     HANDLE token;
1193     TOKEN_PRIVILEGES privs;
1194     HKEY hKey;
1195     HINF hShortcutsInf;
1196     HANDLE hHotkeyThread;
1197     BOOL ret;
1198 
1199     InitializeSetupActionLog(FALSE);
1200     LogItem(NULL, L"Installing ReactOS");
1201 
1202     CreateTempDir(L"TEMP");
1203     CreateTempDir(L"TMP");
1204 
1205     if (!InitializeProgramFilesDir())
1206     {
1207         FatalError("InitializeProgramFilesDir() failed");
1208         return 0;
1209     }
1210 
1211     if (!InitializeProfiles())
1212     {
1213         FatalError("InitializeProfiles() failed");
1214         return 0;
1215     }
1216 
1217     InitializeDefaultUserLocale();
1218 
1219     if (GetWindowsDirectoryW(szBuffer, ARRAYSIZE(szBuffer)))
1220     {
1221         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1222                           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
1223                           0,
1224                           KEY_WRITE,
1225                           &hKey) == ERROR_SUCCESS)
1226         {
1227             RegSetValueExW(hKey,
1228                            L"PathName",
1229                            0,
1230                            REG_SZ,
1231                            (LPBYTE)szBuffer,
1232                            (wcslen(szBuffer) + 1) * sizeof(WCHAR));
1233 
1234             RegSetValueExW(hKey,
1235                            L"SystemRoot",
1236                            0,
1237                            REG_SZ,
1238                            (LPBYTE)szBuffer,
1239                            (wcslen(szBuffer) + 1) * sizeof(WCHAR));
1240 
1241             RegCloseKey(hKey);
1242         }
1243 
1244         PathAddBackslash(szBuffer);
1245         wcscat(szBuffer, L"system");
1246         CreateDirectory(szBuffer, NULL);
1247     }
1248 
1249     if (SaveDefaultUserHive() != ERROR_SUCCESS)
1250     {
1251         FatalError("SaveDefaultUserHive() failed");
1252         return 0;
1253     }
1254 
1255     if (!CopySystemProfile(0))
1256     {
1257         FatalError("CopySystemProfile() failed");
1258         return 0;
1259     }
1260 
1261     hHotkeyThread = CreateThread(NULL, 0, HotkeyThread, NULL, 0, NULL);
1262 
1263     if (!CommonInstall())
1264         return 0;
1265 
1266     /* Install the TCP/IP protocol driver */
1267     ret = InstallNetworkComponent(L"MS_TCPIP");
1268     if (!ret && GetLastError() != ERROR_FILE_NOT_FOUND)
1269     {
1270         DPRINT("InstallNetworkComponent() failed with error 0x%lx\n", GetLastError());
1271     }
1272     else
1273     {
1274         /* Start the TCP/IP protocol driver */
1275         SetupStartService(L"Tcpip", FALSE);
1276         SetupStartService(L"Dhcp", FALSE);
1277     }
1278 
1279     InstallWizard();
1280 
1281     InstallSecurity();
1282 
1283     SetAutoAdminLogon();
1284 
1285     hShortcutsInf = SetupOpenInfFileW(L"shortcuts.inf",
1286                                       NULL,
1287                                       INF_STYLE_WIN4,
1288                                       NULL);
1289     if (hShortcutsInf == INVALID_HANDLE_VALUE)
1290     {
1291         FatalError("Failed to open shortcuts.inf");
1292         return 0;
1293     }
1294 
1295     if (!CreateShortcuts(hShortcutsInf, L"ShortcutFolders"))
1296     {
1297         FatalError("CreateShortcuts() failed");
1298         return 0;
1299     }
1300 
1301     SetupCloseInfFile(hShortcutsInf);
1302 
1303     hShortcutsInf = SetupOpenInfFileW(L"rosapps_shortcuts.inf",
1304                                        NULL,
1305                                        INF_STYLE_WIN4,
1306                                        NULL);
1307     if (hShortcutsInf != INVALID_HANDLE_VALUE)
1308     {
1309         if (!CreateShortcuts(hShortcutsInf, L"ShortcutFolders"))
1310         {
1311             FatalError("CreateShortcuts(rosapps) failed");
1312             return 0;
1313         }
1314         SetupCloseInfFile(hShortcutsInf);
1315     }
1316 
1317     SetupCloseInfFile(hSysSetupInf);
1318     SetSetupType(0);
1319 
1320     if (hHotkeyThread)
1321     {
1322         PostThreadMessage(GetThreadId(hHotkeyThread), WM_QUIT, 0, 0);
1323         CloseHandle(hHotkeyThread);
1324     }
1325 
1326     LogItem(NULL, L"Installing ReactOS done");
1327     TerminateSetupActionLog();
1328 
1329     if (AdminInfo.Name != NULL)
1330         RtlFreeHeap(RtlGetProcessHeap(), 0, AdminInfo.Name);
1331 
1332     if (AdminInfo.Domain != NULL)
1333         RtlFreeHeap(RtlGetProcessHeap(), 0, AdminInfo.Domain);
1334 
1335     if (AdminInfo.Password != NULL)
1336         RtlFreeHeap(RtlGetProcessHeap(), 0, AdminInfo.Password);
1337 
1338     /* Get shutdown privilege */
1339     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
1340     {
1341         FatalError("OpenProcessToken() failed!");
1342         return 0;
1343     }
1344     if (!LookupPrivilegeValue(NULL,
1345                               SE_SHUTDOWN_NAME,
1346                               &privs.Privileges[0].Luid))
1347     {
1348         FatalError("LookupPrivilegeValue() failed!");
1349         return 0;
1350     }
1351     privs.PrivilegeCount = 1;
1352     privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1353     if (AdjustTokenPrivileges(token,
1354                               FALSE,
1355                               &privs,
1356                               0,
1357                               (PTOKEN_PRIVILEGES)NULL,
1358                               NULL) == 0)
1359     {
1360         FatalError("AdjustTokenPrivileges() failed!");
1361         return 0;
1362     }
1363 
1364     ExitWindowsEx(EWX_REBOOT, 0);
1365     return 0;
1366 }
1367 
1368 
1369 /*
1370  * Standard Windows-compatible export, which dispatches
1371  * to either 'InstallReactOS' or 'InstallLiveCD'.
1372  */
1373 INT
1374 WINAPI
1375 InstallWindowsNt(INT argc, WCHAR** argv)
1376 {
1377     INT i;
1378     PWSTR p;
1379 
1380     for (i = 0; i < argc; ++i)
1381     {
1382         p = argv[i];
1383         if (*p == L'-')
1384         {
1385             p++;
1386 
1387             // NOTE: On Windows, "mini" means "minimal UI", and can be used
1388             // in addition to "newsetup"; these options are not exclusive.
1389             if (_wcsicmp(p, L"newsetup") == 0)
1390                 return (INT)InstallReactOS();
1391             else if (_wcsicmp(p, L"mini") == 0)
1392                 return (INT)InstallLiveCD();
1393 
1394             /* Add support for other switches */
1395         }
1396     }
1397 
1398     return 0;
1399 }
1400 
1401 
1402 /*
1403  * @unimplemented
1404  */
1405 DWORD WINAPI
1406 SetupChangeFontSize(
1407     IN HANDLE hWnd,
1408     IN LPCWSTR lpszFontSize)
1409 {
1410     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1411     return FALSE;
1412 }
1413 
1414 /*
1415  * @unimplemented
1416  */
1417 DWORD WINAPI
1418 SetupChangeLocaleEx(HWND hWnd,
1419                     LCID Lcid,
1420                     LPCWSTR lpSrcRootPath,
1421                     char Unknown,
1422                     DWORD dwUnused1,
1423                     DWORD dwUnused2)
1424 {
1425     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1426     return FALSE;
1427 }
1428 
1429 /*
1430  * @implemented
1431  */
1432 DWORD WINAPI
1433 SetupChangeLocale(HWND hWnd, LCID Lcid)
1434 {
1435     return SetupChangeLocaleEx(hWnd, Lcid, NULL, 0, 0, 0);
1436 }
1437 
1438 
1439 DWORD
1440 WINAPI
1441 SetupStartService(
1442     LPCWSTR lpServiceName,
1443     BOOL bWait)
1444 {
1445     SC_HANDLE hManager = NULL;
1446     SC_HANDLE hService = NULL;
1447     DWORD dwError = ERROR_SUCCESS;
1448 
1449     hManager = OpenSCManagerW(NULL,
1450                               NULL,
1451                               SC_MANAGER_ALL_ACCESS);
1452     if (hManager == NULL)
1453     {
1454         dwError = GetLastError();
1455         goto done;
1456     }
1457 
1458     hService = OpenServiceW(hManager,
1459                             lpServiceName,
1460                             SERVICE_START);
1461     if (hService == NULL)
1462     {
1463         dwError = GetLastError();
1464         goto done;
1465     }
1466 
1467     if (!StartService(hService, 0, NULL))
1468     {
1469         dwError = GetLastError();
1470         goto done;
1471     }
1472 
1473 done:
1474     if (hService != NULL)
1475         CloseServiceHandle(hService);
1476 
1477     if (hManager != NULL)
1478         CloseServiceHandle(hManager);
1479 
1480     return dwError;
1481 }
1482