[SYSSETUP] If creating the default-user hive file fails because the file already...
[reactos.git] / dll / win32 / syssetup / install.c
index 097537e..63fa4a2 100644 (file)
@@ -1,21 +1,3 @@
-/*
- *  ReactOS kernel
- *  Copyright (C) 2003 ReactOS Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
 /*
  * COPYRIGHT:         See COPYING in the top level directory
  * PROJECT:           ReactOS system libraries
 
 #include "precomp.h"
 
-#include <tchar.h>
+#define COBJMACROS
+
+#include <io.h>
 #include <wincon.h>
+#include <winnls.h>
 #include <winsvc.h>
 #include <userenv.h>
 #include <shlobj.h>
 #include <shlwapi.h>
+#include <shobjidl.h>
 #include <rpcproxy.h>
 #include <ndk/cmfuncs.h>
 
@@ -43,6 +29,9 @@
 DWORD WINAPI
 CMP_WaitNoPendingInstallEvents(DWORD dwTimeout);
 
+DWORD WINAPI
+SetupStartService(LPCWSTR lpServiceName, BOOL bWait);
+
 /* GLOBALS ******************************************************************/
 
 HINF hSysSetupInf = INVALID_HANDLE_VALUE;
@@ -60,7 +49,7 @@ FatalError(char *pszFmt,...)
     vsprintf(szBuffer, pszFmt, ap);
     va_end(ap);
 
-    LogItem(SYSSETUP_SEVERITY_FATAL_ERROR, L"Failed");
+    LogItem(NULL, L"Failed");
 
     strcat(szBuffer, "\nRebooting now!");
     MessageBoxA(NULL,
@@ -71,62 +60,44 @@ FatalError(char *pszFmt,...)
 
 static HRESULT
 CreateShellLink(
-    LPCTSTR pszLinkPath,
-    LPCTSTR pszCmd,
-    LPCTSTR pszArg,
-    LPCTSTR pszDir,
-    LPCTSTR pszIconPath,
-    int iIconNr,
-    LPCTSTR pszComment)
+    LPCWSTR pszLinkPath,
+    LPCWSTR pszCmd,
+    LPCWSTR pszArg,
+    LPCWSTR pszDir,
+    LPCWSTR pszIconPath,
+    INT iIconNr,
+    LPCWSTR pszComment)
 {
-    IShellLink *psl;
+    IShellLinkW *psl;
     IPersistFile *ppf;
-#ifndef _UNICODE
-    WCHAR wszBuf[MAX_PATH];
-#endif /* _UNICODE */
 
     HRESULT hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID*)&psl);
 
     if (SUCCEEDED(hr))
     {
-        hr = psl->lpVtbl->SetPath(psl, pszCmd);
+        hr = IShellLinkW_SetPath(psl, pszCmd);
 
         if (pszArg)
-        {
-            hr = psl->lpVtbl->SetArguments(psl, pszArg);
-        }
+            hr = IShellLinkW_SetArguments(psl, pszArg);
 
         if (pszDir)
-        {
-            hr = psl->lpVtbl->SetWorkingDirectory(psl, pszDir);
-        }
+            hr = IShellLinkW_SetWorkingDirectory(psl, pszDir);
 
         if (pszIconPath)
-        {
-            hr = psl->lpVtbl->SetIconLocation(psl, pszIconPath, iIconNr);
-        }
+            hr = IShellLinkW_SetIconLocation(psl, pszIconPath, iIconNr);
 
         if (pszComment)
-        {
-            hr = psl->lpVtbl->SetDescription(psl, pszComment);
-        }
+            hr = IShellLinkW_SetDescription(psl, pszComment);
 
-        hr = psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
+        hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
 
         if (SUCCEEDED(hr))
         {
-#ifdef _UNICODE
-            hr = ppf->lpVtbl->Save(ppf, pszLinkPath, TRUE);
-#else /* _UNICODE */
-            MultiByteToWideChar(CP_ACP, 0, pszLinkPath, -1, wszBuf, MAX_PATH);
-
-            hr = ppf->lpVtbl->Save(ppf, wszBuf, TRUE);
-#endif /* _UNICODE */
-
-            ppf->lpVtbl->Release(ppf);
+            hr = IPersistFile_Save(ppf, pszLinkPath, TRUE);
+            IPersistFile_Release(ppf);
         }
 
-        psl->lpVtbl->Release(psl);
+        IShellLinkW_Release(psl);
     }
 
     return hr;
@@ -135,101 +106,108 @@ CreateShellLink(
 
 static BOOL
 CreateShortcut(
-    LPCTSTR pszFolder,
-    LPCTSTR pszName,
-    LPCTSTR pszCommand,
-    LPCTSTR pszDescription,
-    INT iIconNr)
+    LPCWSTR pszFolder,
+    LPCWSTR pszName,
+    LPCWSTR pszCommand,
+    LPCWSTR pszDescription,
+    INT iIconNr,
+    LPCWSTR pszWorkingDir)
 {
-    TCHAR szPath[MAX_PATH];
-    TCHAR szExeName[MAX_PATH];
-    LPTSTR Ptr;
-    TCHAR szWorkingDirBuf[MAX_PATH];
-    LPTSTR pszWorkingDir = NULL;
-    LPTSTR lpFilePart;
     DWORD dwLen;
+    LPWSTR Ptr;
+    LPWSTR lpFilePart;
+    WCHAR szPath[MAX_PATH];
+    WCHAR szWorkingDirBuf[MAX_PATH];
 
-    if (ExpandEnvironmentStrings(pszCommand,
-                                 szPath,
-                                 sizeof(szPath) / sizeof(szPath[0])) == 0)
-    {
-        _tcscpy(szPath, pszCommand);
-    }
-
-    if ((_taccess(szPath, 0 )) == -1)
-        /* Expected error, don't return FALSE */
-        return TRUE;
-
-    dwLen = GetFullPathName(szPath,
-                            sizeof(szWorkingDirBuf) / sizeof(szWorkingDirBuf[0]),
-                            szWorkingDirBuf,
-                            &lpFilePart);
-    if (dwLen != 0 && dwLen <= sizeof(szWorkingDirBuf) / sizeof(szWorkingDirBuf[0]))
+    /* If no working directory is provided, try to compute a default one */
+    if (pszWorkingDir == NULL || pszWorkingDir[0] == L'\0')
     {
-        /* Since those should only be called with (.exe) files,
-           lpFilePart has not to be NULL */
-        ASSERT(lpFilePart != NULL);
-
-        /* Save the file name */
-        _tcscpy(szExeName, lpFilePart);
-
-        /* We're only interested in the path. Cut the file name off.
-           Also remove the trailing backslash unless the working directory
-           is only going to be a drive, ie. C:\ */
-        *(lpFilePart--) = _T('\0');
-        if (!(lpFilePart - szWorkingDirBuf == 2 && szWorkingDirBuf[1] == _T(':') &&
-              szWorkingDirBuf[2] == _T('\\')))
+        if (ExpandEnvironmentStringsW(pszCommand, szPath, ARRAYSIZE(szPath)) == 0)
+            wcscpy(szPath, pszCommand);
+
+        dwLen = GetFullPathNameW(szPath,
+                                 ARRAYSIZE(szWorkingDirBuf),
+                                 szWorkingDirBuf,
+                                 &lpFilePart);
+        if (dwLen != 0 && dwLen <= ARRAYSIZE(szWorkingDirBuf))
         {
-            *lpFilePart = _T('\0');
+            /* Since those should only be called with (.exe) files,
+               lpFilePart has not to be NULL */
+            ASSERT(lpFilePart != NULL);
+
+            /* We're only interested in the path. Cut the file name off.
+               Also remove the trailing backslash unless the working directory
+               is only going to be a drive, i.e. C:\ */
+            *(lpFilePart--) = L'\0';
+            if (!(lpFilePart - szWorkingDirBuf == 2 &&
+                  szWorkingDirBuf[1] == L':' && szWorkingDirBuf[2] == L'\\'))
+            {
+                *lpFilePart = L'\0';
+            }
+            pszWorkingDir = szWorkingDirBuf;
         }
-
-        pszWorkingDir = szWorkingDirBuf;
     }
 
-    _tcscpy(szPath, pszFolder);
+    /* If we failed to compute a working directory, just do not use one */
+    if (pszWorkingDir && pszWorkingDir[0] == L'\0')
+        pszWorkingDir = NULL;
 
+    /* Build the shortcut file name */
+    wcscpy(szPath, pszFolder);
     Ptr = PathAddBackslash(szPath);
-
-    _tcscpy(Ptr, pszName);
-
-    // FIXME: we should pass 'command' straight in here, but shell32 doesn't expand it
-    return SUCCEEDED(CreateShellLink(szPath, szExeName, _T(""), pszWorkingDir, szExeName, iIconNr, pszDescription));
+    wcscpy(Ptr, pszName);
+
+    /* Create the shortcut */
+    return SUCCEEDED(CreateShellLink(szPath,
+                                     pszCommand,
+                                     L"",
+                                     pszWorkingDir,
+                                     /* Special value to indicate no icon */
+                                     (iIconNr != -1 ? pszCommand : NULL),
+                                     iIconNr,
+                                     pszDescription));
 }
 
 
-static BOOL CreateShortcutsFromSection(HINF hinf, LPWSTR  pszSection, LPCWSTR pszFolder)
+static BOOL CreateShortcutsFromSection(HINF hinf, LPWSTR pszSection, LPCWSTR pszFolder)
 {
     INFCONTEXT Context;
+    DWORD dwFieldCount;
+    INT iIconNr;
     WCHAR szCommand[MAX_PATH];
     WCHAR szName[MAX_PATH];
     WCHAR szDescription[MAX_PATH];
-    INT iIconNr;
+    WCHAR szDirectory[MAX_PATH];
 
     if (!SetupFindFirstLine(hinf, pszSection, NULL, &Context))
         return FALSE;
 
     do
     {
-        if (SetupGetFieldCount(&Context) < 4)
+        dwFieldCount = SetupGetFieldCount(&Context);
+        if (dwFieldCount < 3)
             continue;
 
-        if (!SetupGetStringFieldW(&Context, 1, szCommand, MAX_PATH, NULL))
+        if (!SetupGetStringFieldW(&Context, 1, szCommand, ARRAYSIZE(szCommand), NULL))
             continue;
 
-        if (!SetupGetStringFieldW(&Context, 2, szName, MAX_PATH, NULL))
+        if (!SetupGetStringFieldW(&Context, 2, szName, ARRAYSIZE(szName), NULL))
             continue;
 
-        if (!SetupGetStringFieldW(&Context, 3, szDescription, MAX_PATH, NULL))
+        if (!SetupGetStringFieldW(&Context, 3, szDescription, ARRAYSIZE(szDescription), NULL))
             continue;
 
-        if (!SetupGetIntField(&Context, 4, &iIconNr))
-            continue;
+        if (dwFieldCount < 4 || !SetupGetIntField(&Context, 4, &iIconNr))
+            iIconNr = -1; /* Special value to indicate no icon */
 
-        _tcscat(szName, L".lnk");
+        if (dwFieldCount < 5 || !SetupGetStringFieldW(&Context, 5, szDirectory, ARRAYSIZE(szDirectory), NULL))
+            szDirectory[0] = L'\0';
 
-        CreateShortcut(pszFolder, szName, szCommand, szDescription, iIconNr);
+        wcscat(szName, L".lnk");
 
-    }while (SetupFindNextLine(&Context, &Context));
+        CreateShortcut(pszFolder, szName, szCommand, szDescription, iIconNr, szDirectory);
+
+    } while (SetupFindNextLine(&Context, &Context));
 
     return TRUE;
 }
@@ -241,7 +219,6 @@ static BOOL CreateShortcuts(HINF hinf, LPCWSTR szSection)
     WCHAR szFolder[MAX_PATH];
     WCHAR szFolderSection[MAX_PATH];
     INT csidl;
-    LPWSTR p;
 
     CoInitialize(NULL);
 
@@ -253,32 +230,21 @@ static BOOL CreateShortcuts(HINF hinf, LPCWSTR szSection)
         if (SetupGetFieldCount(&Context) < 2)
             continue;
 
-        if (!SetupGetStringFieldW(&Context, 0, szFolderSection, MAX_PATH, NULL))
+        if (!SetupGetStringFieldW(&Context, 0, szFolderSection, ARRAYSIZE(szFolderSection), NULL))
             continue;
 
         if (!SetupGetIntField(&Context, 1, &csidl))
             continue;
 
-        if (!SetupGetStringFieldW(&Context, 2, szFolder, MAX_PATH, NULL))
+        if (!SetupGetStringFieldW(&Context, 2, szFolder, ARRAYSIZE(szFolder), NULL))
             continue;
 
-        if (!SHGetSpecialFolderPathW(0, szPath, csidl, TRUE))
+        if (FAILED(SHGetFolderPathAndSubDirW(NULL, csidl|CSIDL_FLAG_CREATE, (HANDLE)-1, SHGFP_TYPE_DEFAULT, szFolder, szPath)))
             continue;
 
-        p = PathAddBackslash(szPath);
-        _tcscpy(p, szFolder);
-
-        if (!CreateDirectory(szPath, NULL))
-        {
-            if (GetLastError() != ERROR_ALREADY_EXISTS) 
-            {
-                continue;
-            }
-        }
-
         CreateShortcutsFromSection(hinf, szFolderSection, szPath);
 
-    }while (SetupFindNextLine(&Context, &Context));
+    } while (SetupFindNextLine(&Context, &Context));
 
     CoUninitialize();
 
@@ -295,32 +261,30 @@ CreateTempDir(
     HKEY hKey;
 
     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-                     L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
-                     0,
-                     KEY_QUERY_VALUE,
-                     &hKey) != ERROR_SUCCESS)
+                      L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
+                      0,
+                      KEY_QUERY_VALUE,
+                      &hKey) != ERROR_SUCCESS)
     {
         FatalError("Error: %lu\n", GetLastError());
         return;
     }
 
     /* Get temp dir */
-    dwLength = MAX_PATH * sizeof(WCHAR);
+    dwLength = sizeof(szBuffer);
     if (RegQueryValueExW(hKey,
-                        VarName,
-                        NULL,
-                        NULL,
-                        (LPBYTE)szBuffer,
-                        &dwLength) != ERROR_SUCCESS)
+                         VarName,
+                         NULL,
+                         NULL,
+                         (LPBYTE)szBuffer,
+                         &dwLength) != ERROR_SUCCESS)
     {
         FatalError("Error: %lu\n", GetLastError());
         goto cleanup;
     }
 
     /* Expand it */
-    if (!ExpandEnvironmentStringsW(szBuffer,
-                                  szTempDir,
-                                  MAX_PATH))
+    if (!ExpandEnvironmentStringsW(szBuffer, szTempDir, ARRAYSIZE(szTempDir)))
     {
         FatalError("Error: %lu\n", GetLastError());
         goto cleanup;
@@ -358,10 +322,10 @@ InstallSysSetupInfDevices(VOID)
     do
     {
         if (!SetupGetStringFieldW(&InfContext,
-                                 0,
-                                 szLineBuffer,
-                                 sizeof(szLineBuffer)/sizeof(szLineBuffer[0]),
-                                 &dwLineLength))
+                                  0,
+                                  szLineBuffer,
+                                  ARRAYSIZE(szLineBuffer),
+                                  &dwLineLength))
         {
             return FALSE;
         }
@@ -385,9 +349,9 @@ InstallSysSetupInfComponents(VOID)
     HINF hComponentInf = INVALID_HANDLE_VALUE;
 
     if (!SetupFindFirstLineW(hSysSetupInf,
-                            L"Infs.Always",
-                            NULL,
-                            &InfContext))
+                             L"Infs.Always",
+                             NULL,
+                             &InfContext))
     {
         DPRINT("No Inf.Always section found\n");
     }
@@ -396,20 +360,20 @@ InstallSysSetupInfComponents(VOID)
         do
         {
             if (!SetupGetStringFieldW(&InfContext,
-                                     1, // Get the component name
-                                     szNameBuffer,
-                                     sizeof(szNameBuffer)/sizeof(szNameBuffer[0]),
-                                     NULL))
+                                      1, // Get the component name
+                                      szNameBuffer,
+                                      ARRAYSIZE(szNameBuffer),
+                                      NULL))
             {
                 FatalError("Error while trying to get component name \n");
                 return FALSE;
             }
 
             if (!SetupGetStringFieldW(&InfContext,
-                                     2, // Get the component install section
-                                     szSectionBuffer,
-                                     sizeof(szSectionBuffer)/sizeof(szSectionBuffer[0]),
-                                     NULL))
+                                      2, // Get the component install section
+                                      szSectionBuffer,
+                                      ARRAYSIZE(szSectionBuffer),
+                                      NULL))
             {
                 FatalError("Error while trying to get component install section \n");
                 return FALSE;
@@ -429,16 +393,16 @@ InstallSysSetupInfComponents(VOID)
             }
 
             if (!SetupInstallFromInfSectionW(NULL,
-                                            hComponentInf,
-                                            szSectionBuffer,
-                                            SPINST_ALL,
-                                            NULL,
-                                            NULL,
-                                            SP_COPY_NEWER,
-                                            SetupDefaultQueueCallbackW,
-                                            NULL,
-                                            NULL,
-                                            NULL))
+                                             hComponentInf,
+                                             szSectionBuffer,
+                                             SPINST_ALL,
+                                             NULL,
+                                             NULL,
+                                             SP_COPY_NEWER,
+                                             SetupDefaultQueueCallbackW,
+                                             NULL,
+                                             NULL,
+                                             NULL))
            {
                 FatalError("Error while trying to install : %S (Error: %lu)\n", szNameBuffer, GetLastError());
                 SetupCloseInfFile(hComponentInf);
@@ -455,9 +419,8 @@ InstallSysSetupInfComponents(VOID)
 
 
 
-
 BOOL
-RegisterTypeLibraries (HINF hinf, LPCWSTR szSection)
+RegisterTypeLibraries(HINF hinf, LPCWSTR szSection)
 {
     INFCONTEXT InfContext;
     BOOL res;
@@ -475,7 +438,7 @@ RegisterTypeLibraries (HINF hinf, LPCWSTR szSection)
     do
     {
         /* Get the name of the current type library */
-        if (!SetupGetStringFieldW(&InfContext, 1, szName, MAX_PATH, NULL))
+        if (!SetupGetStringFieldW(&InfContext, 1, szName, ARRAYSIZE(szName), NULL))
         {
             FatalError("SetupGetStringFieldW failed\n");
             continue;
@@ -487,12 +450,12 @@ RegisterTypeLibraries (HINF hinf, LPCWSTR szSection)
         hret = SHGetFolderPathW(NULL, csidl, NULL, 0, szPath);
         if (FAILED(hret))
         {
-            FatalError("SHGetSpecialFolderPathW failed hret=0x%d\n", hret);
+            FatalError("SHGetFolderPathW failed hret=0x%lx\n", hret);
             continue;
         }
 
         p = PathAddBackslash(szPath);
-        _tcscpy(p, szName);
+        wcscpy(p, szName);
 
         hmod = LoadLibraryW(szName);
         if (hmod == NULL)
@@ -503,7 +466,7 @@ RegisterTypeLibraries (HINF hinf, LPCWSTR szSection)
 
         __wine_register_resources(hmod);
 
-    }while (SetupFindNextLine(&InfContext, &InfContext));
+    } while (SetupFindNextLine(&InfContext, &InfContext));
 
     return TRUE;
 }
@@ -533,11 +496,11 @@ EnableUserModePnpManager(VOID)
     }
 
     bRet = ChangeServiceConfigW(hService,
-                               SERVICE_NO_CHANGE,
-                               SERVICE_AUTO_START,
-                               SERVICE_NO_CHANGE,
-                               NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL);
+                                SERVICE_NO_CHANGE,
+                                SERVICE_AUTO_START,
+                                SERVICE_NO_CHANGE,
+                                NULL, NULL, NULL,
+                                NULL, NULL, NULL, NULL);
     if (!bRet)
     {
         DPRINT1("Unable to change the service configuration\n");
@@ -576,7 +539,7 @@ StatusMessageWindowProc(
         {
             WCHAR szMsg[256];
 
-            if (!LoadStringW(hDllInstance, IDS_STATUS_INSTALL_DEV, szMsg, sizeof(szMsg)/sizeof(szMsg[0])))
+            if (!LoadStringW(hDllInstance, IDS_STATUS_INSTALL_DEV, szMsg, ARRAYSIZE(szMsg)))
                 return FALSE;
             SetDlgItemTextW(hwndDlg, IDC_STATUSLABEL, szMsg);
             return TRUE;
@@ -590,21 +553,26 @@ ShowStatusMessageThread(
     IN LPVOID lpParameter)
 {
     HWND *phWnd = (HWND *)lpParameter;
-    HWND hWnd;
+    HWND hWnd, hItem;
     MSG Msg;
 
-    hWnd = CreateDialogParam(
-        hDllInstance,
-        MAKEINTRESOURCE(IDD_STATUSWINDOW_DLG),
-        GetDesktopWindow(),
-        StatusMessageWindowProc,
-        (LPARAM)NULL);
+    hWnd = CreateDialogParam(hDllInstance,
+                             MAKEINTRESOURCE(IDD_STATUSWINDOW_DLG),
+                             GetDesktopWindow(),
+                             StatusMessageWindowProc,
+                             (LPARAM)NULL);
     if (!hWnd)
         return 0;
     *phWnd = hWnd;
 
     ShowWindow(hWnd, SW_SHOW);
 
+    hItem = GetDlgItem(hWnd, IDC_STATUSPROGRESS);
+    if (hItem)
+    {
+        PostMessage(hItem, PBM_SETMARQUEE, TRUE, 40);
+    }
+
     /* Message loop for the Status window */
     while (GetMessage(&Msg, NULL, 0, 0))
     {
@@ -660,12 +628,11 @@ IsConsoleBoot(VOID)
     BOOL bConsoleBoot = FALSE;
     LONG rc;
 
-    rc = RegOpenKeyExW(
-        HKEY_LOCAL_MACHINE,
-        L"SYSTEM\\CurrentControlSet\\Control",
-        0,
-        KEY_QUERY_VALUE,
-        &hControlKey);
+    rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                       L"SYSTEM\\CurrentControlSet\\Control",
+                       0,
+                       KEY_QUERY_VALUE,
+                       &hControlKey);
     if (rc != ERROR_SUCCESS)
         goto cleanup;
 
@@ -702,11 +669,10 @@ CommonInstall(VOID)
 {
     HWND hWnd = NULL;
 
-    hSysSetupInf = SetupOpenInfFileW(
-        L"syssetup.inf",
-        NULL,
-        INF_STYLE_WIN4,
-        NULL);
+    hSysSetupInf = SetupOpenInfFileW(L"syssetup.inf",
+                                     NULL,
+                                     INF_STYLE_WIN4,
+                                     NULL);
     if (hSysSetupInf == INVALID_HANDLE_VALUE)
     {
         FatalError("SetupOpenInfFileW() failed to open 'syssetup.inf' (Error: %lu)\n", GetLastError());
@@ -729,14 +695,12 @@ CommonInstall(VOID)
     {
         HANDLE hThread;
 
-        hThread = CreateThread(
-            NULL,
-            0,
-            ShowStatusMessageThread,
-            (LPVOID)&hWnd,
-            0,
-            NULL);
-
+        hThread = CreateThread(NULL,
+                               0,
+                               ShowStatusMessageThread,
+                               (LPVOID)&hWnd,
+                               0,
+                               NULL);
         if (hThread)
             CloseHandle(hThread);
     }
@@ -763,23 +727,104 @@ error:
     return FALSE;
 }
 
-DWORD WINAPI
-InstallLiveCD(IN HINSTANCE hInstance)
+/* Install a section of a .inf file
+ * Returns TRUE if success, FALSE if failure. Error code can
+ * be retrieved with GetLastError()
+ */
+static
+BOOL
+InstallInfSection(
+    IN HWND hWnd,
+    IN LPCWSTR InfFile,
+    IN LPCWSTR InfSection OPTIONAL,
+    IN LPCWSTR InfService OPTIONAL)
+{
+    WCHAR Buffer[MAX_PATH];
+    HINF hInf = INVALID_HANDLE_VALUE;
+    UINT BufferSize;
+    PVOID Context = NULL;
+    BOOL ret = FALSE;
+
+    /* Get Windows directory */
+    BufferSize = ARRAYSIZE(Buffer) - 5 - wcslen(InfFile);
+    if (GetWindowsDirectoryW(Buffer, BufferSize) > BufferSize)
+    {
+        /* Function failed */
+        SetLastError(ERROR_GEN_FAILURE);
+        goto cleanup;
+    }
+    /* We have enough space to add some information in the buffer */
+    if (Buffer[wcslen(Buffer) - 1] != '\\')
+        wcscat(Buffer, L"\\");
+    wcscat(Buffer, L"Inf\\");
+    wcscat(Buffer, InfFile);
+
+    /* Install specified section */
+    hInf = SetupOpenInfFileW(Buffer, NULL, INF_STYLE_WIN4, NULL);
+    if (hInf == INVALID_HANDLE_VALUE)
+        goto cleanup;
+
+    Context = SetupInitDefaultQueueCallback(hWnd);
+    if (Context == NULL)
+        goto cleanup;
+
+    ret = TRUE;
+    if (ret && InfSection)
+    {
+        ret = SetupInstallFromInfSectionW(
+                hWnd, hInf,
+                InfSection, SPINST_ALL,
+                NULL, NULL, SP_COPY_NEWER,
+                SetupDefaultQueueCallbackW, Context,
+                NULL, NULL);
+    }
+    if (ret && InfService)
+    {
+        ret = SetupInstallServicesFromInfSectionW(hInf, InfService, 0);
+    }
+
+cleanup:
+    if (Context)
+        SetupTermDefaultQueueCallback(Context);
+    if (hInf != INVALID_HANDLE_VALUE)
+        SetupCloseInfFile(hInf);
+    return ret;
+}
+
+static
+DWORD
+InstallLiveCD(VOID)
 {
     STARTUPINFOW StartupInfo;
     PROCESS_INFORMATION ProcessInformation;
     BOOL bRes;
 
+    /* Hack: Install TCP/IP protocol driver */
+    bRes = InstallInfSection(NULL,
+                             L"nettcpip.inf",
+                             L"MS_TCPIP.PrimaryInstall",
+                             L"MS_TCPIP.PrimaryInstall.Services");
+    if (!bRes && GetLastError() != ERROR_FILE_NOT_FOUND)
+    {
+        DPRINT("InstallInfSection() failed with error 0x%lx\n", GetLastError());
+    }
+    else
+    {
+        /* Start the TCP/IP protocol driver */
+        SetupStartService(L"Tcpip", FALSE);
+        SetupStartService(L"Dhcp", FALSE);
+    }
+
     if (!CommonInstall())
         goto error;
-    
+
     /* Register components */
     _SEH2_TRY
     {
         if (!SetupInstallFromInfSectionW(NULL,
-            hSysSetupInf, L"RegistrationPhase2",
-            SPINST_ALL,
-            0, NULL, 0, NULL, NULL, NULL, NULL))
+                                         hSysSetupInf, L"RegistrationPhase2",
+                                         SPINST_ALL,
+                                         0, NULL, 0, NULL, NULL, NULL, NULL))
         {
             DPRINT1("SetupInstallFromInfSectionW failed!\n");
         }
@@ -791,23 +836,22 @@ InstallLiveCD(IN HINSTANCE hInstance)
         DPRINT1("Catching exception\n");
     }
     _SEH2_END;
-    
+
     SetupCloseInfFile(hSysSetupInf);
 
     /* Run the shell */
     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
     StartupInfo.cb = sizeof(StartupInfo);
-    bRes = CreateProcessW(
-        L"userinit.exe",
-        NULL,
-        NULL,
-        NULL,
-        FALSE,
-        0,
-        NULL,
-        NULL,
-        &StartupInfo,
-        &ProcessInformation);
+    bRes = CreateProcessW(L"userinit.exe",
+                          NULL,
+                          NULL,
+                          NULL,
+                          FALSE,
+                          0,
+                          NULL,
+                          NULL,
+                          &StartupInfo,
+                          &ProcessInformation);
     if (!bRes)
         goto error;
 
@@ -855,17 +899,374 @@ SetSetupType(DWORD dwSetupType)
     return TRUE;
 }
 
-DWORD WINAPI
-InstallReactOS(HINSTANCE hInstance)
+static DWORD CALLBACK
+HotkeyThread(LPVOID Parameter)
 {
-    TCHAR szBuffer[MAX_PATH];
+    ATOM hotkey;
+    MSG msg;
+
+    DPRINT("HotkeyThread start\n");
+
+    hotkey = GlobalAddAtomW(L"Setup Shift+F10 Hotkey");
+
+    if (!RegisterHotKey(NULL, hotkey, MOD_SHIFT, VK_F10))
+        DPRINT1("RegisterHotKey failed with %lu\n", GetLastError());
+
+    while (GetMessage(&msg, NULL, 0, 0))
+    {
+        if (msg.hwnd == NULL && msg.message == WM_HOTKEY && msg.wParam == hotkey)
+        {
+            STARTUPINFOW si = { sizeof(si) };
+            PROCESS_INFORMATION pi;
+
+            if (CreateProcessW(L"cmd.exe",
+                               NULL,
+                               NULL,
+                               NULL,
+                               FALSE,
+                               CREATE_NEW_CONSOLE,
+                               NULL,
+                               NULL,
+                               &si,
+                               &pi))
+            {
+                CloseHandle(pi.hProcess);
+                CloseHandle(pi.hThread);
+            }
+            else
+            {
+                DPRINT1("Failed to launch command prompt: %lu\n", GetLastError());
+            }
+        }
+    }
+
+    UnregisterHotKey(NULL, hotkey);
+    GlobalDeleteAtom(hotkey);
+
+    DPRINT("HotkeyThread terminate\n");
+    return 0;
+}
+
+
+static
+BOOL
+InitializeProgramFilesDir(VOID)
+{
+    LONG Error;
+    HKEY hKey;
+    DWORD dwLength;
+    WCHAR szProgramFilesDirPath[MAX_PATH];
+    WCHAR szCommonFilesDirPath[MAX_PATH];
+    WCHAR szBuffer[MAX_PATH];
+
+    /* Load 'Program Files' location */
+    if (!LoadStringW(hDllInstance,
+                     IDS_PROGRAMFILES,
+                     szBuffer,
+                     ARRAYSIZE(szBuffer)))
+    {
+        DPRINT1("Error: %lu\n", GetLastError());
+        return FALSE;
+    }
+
+    if (!LoadStringW(hDllInstance,
+                     IDS_COMMONFILES,
+                     szCommonFilesDirPath,
+                     ARRAYSIZE(szCommonFilesDirPath)))
+    {
+        DPRINT1("Warning: %lu\n", GetLastError());
+    }
+
+    /* Expand it */
+    if (!ExpandEnvironmentStringsW(szBuffer,
+                                   szProgramFilesDirPath,
+                                   ARRAYSIZE(szProgramFilesDirPath)))
+    {
+        DPRINT1("Error: %lu\n", GetLastError());
+        return FALSE;
+    }
+
+    wcscpy(szBuffer, szProgramFilesDirPath);
+    wcscat(szBuffer, L"\\");
+    wcscat(szBuffer, szCommonFilesDirPath);
+
+    if (!ExpandEnvironmentStringsW(szBuffer,
+                                   szCommonFilesDirPath,
+                                   ARRAYSIZE(szCommonFilesDirPath)))
+    {
+        DPRINT1("Warning: %lu\n", GetLastError());
+    }
+
+    /* Store it */
+    Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                          L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
+                          0,
+                          KEY_SET_VALUE,
+                          &hKey);
+    if (Error != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", Error);
+        return FALSE;
+    }
+
+    dwLength = (wcslen(szProgramFilesDirPath) + 1) * sizeof(WCHAR);
+    Error = RegSetValueExW(hKey,
+                           L"ProgramFilesDir",
+                           0,
+                           REG_SZ,
+                           (LPBYTE)szProgramFilesDirPath,
+                           dwLength);
+    if (Error != ERROR_SUCCESS)
+    {
+        DPRINT1("Error: %lu\n", Error);
+        RegCloseKey(hKey);
+        return FALSE;
+    }
+
+    dwLength = (wcslen(szCommonFilesDirPath) + 1) * sizeof(WCHAR);
+    Error = RegSetValueExW(hKey,
+                           L"CommonFilesDir",
+                           0,
+                           REG_SZ,
+                           (LPBYTE)szCommonFilesDirPath,
+                           dwLength);
+    if (Error != ERROR_SUCCESS)
+    {
+        DPRINT1("Warning: %lu\n", Error);
+    }
+
+    RegCloseKey(hKey);
+
+    /* Create directory */
+    // FIXME: Security!
+    if (!CreateDirectoryW(szProgramFilesDirPath, NULL))
+    {
+        if (GetLastError() != ERROR_ALREADY_EXISTS)
+        {
+            DPRINT1("Error: %lu\n", GetLastError());
+            return FALSE;
+        }
+    }
+
+    /* Create directory */
+    // FIXME: Security!
+    if (!CreateDirectoryW(szCommonFilesDirPath, NULL))
+    {
+        if (GetLastError() != ERROR_ALREADY_EXISTS)
+        {
+            DPRINT1("Warning: %lu\n", GetLastError());
+            // return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+
+static
+VOID
+InitializeDefaultUserLocale(VOID)
+{
+    WCHAR szBuffer[80];
+    PWSTR ptr;
+    HKEY hLocaleKey;
+    DWORD ret;
+    DWORD dwSize;
+    LCID lcid;
+    INT i;
+
+    struct {LCTYPE LCType; PWSTR pValue;} LocaleData[] = {
+        /* Number */
+        {LOCALE_SDECIMAL, L"sDecimal"},
+        {LOCALE_STHOUSAND, L"sThousand"},
+        {LOCALE_SNEGATIVESIGN, L"sNegativeSign"},
+        {LOCALE_SPOSITIVESIGN, L"sPositiveSign"},
+        {LOCALE_SGROUPING, L"sGrouping"},
+        {LOCALE_SLIST, L"sList"},
+        {LOCALE_SNATIVEDIGITS, L"sNativeDigits"},
+        {LOCALE_INEGNUMBER, L"iNegNumber"},
+        {LOCALE_IDIGITS, L"iDigits"},
+        {LOCALE_ILZERO, L"iLZero"},
+        {LOCALE_IMEASURE, L"iMeasure"},
+        {LOCALE_IDIGITSUBSTITUTION, L"NumShape"},
+
+        /* Currency */
+        {LOCALE_SCURRENCY, L"sCurrency"},
+        {LOCALE_SMONDECIMALSEP, L"sMonDecimalSep"},
+        {LOCALE_SMONTHOUSANDSEP, L"sMonThousandSep"},
+        {LOCALE_SMONGROUPING, L"sMonGrouping"},
+        {LOCALE_ICURRENCY, L"iCurrency"},
+        {LOCALE_INEGCURR, L"iNegCurr"},
+        {LOCALE_ICURRDIGITS, L"iCurrDigits"},
+
+        /* Time */
+        {LOCALE_STIMEFORMAT, L"sTimeFormat"},
+        {LOCALE_STIME, L"sTime"},
+        {LOCALE_S1159, L"s1159"},
+        {LOCALE_S2359, L"s2359"},
+        {LOCALE_ITIME, L"iTime"},
+        {LOCALE_ITIMEMARKPOSN, L"iTimePrefix"},
+        {LOCALE_ITLZERO, L"iTLZero"},
+
+        /* Date */
+        {LOCALE_SLONGDATE, L"sLongDate"},
+        {LOCALE_SSHORTDATE, L"sShortDate"},
+        {LOCALE_SDATE, L"sDate"},
+        {LOCALE_IFIRSTDAYOFWEEK, L"iFirstDayOfWeek"},
+        {LOCALE_IFIRSTWEEKOFYEAR, L"iFirstWeekOfYear"},
+        {LOCALE_IDATE, L"iDate"},
+        {LOCALE_ICALENDARTYPE, L"iCalendarType"},
+
+        /* Misc */
+        {LOCALE_SCOUNTRY, L"sCountry"},
+        {LOCALE_SABBREVLANGNAME, L"sLanguage"},
+        {LOCALE_ICOUNTRY, L"iCountry"},
+        {0, NULL}};
+
+    ret = RegOpenKeyExW(HKEY_USERS,
+                        L".DEFAULT\\Control Panel\\International",
+                        0,
+                        KEY_READ | KEY_WRITE,
+                        &hLocaleKey);
+    if (ret != ERROR_SUCCESS)
+    {
+        return;
+    }
+
+    dwSize = 9 * sizeof(WCHAR);
+    ret = RegQueryValueExW(hLocaleKey,
+                           L"Locale",
+                           NULL,
+                           NULL,
+                           (PBYTE)szBuffer,
+                           &dwSize);
+    if (ret != ERROR_SUCCESS)
+        goto done;
+
+    lcid = (LCID)wcstoul(szBuffer, &ptr, 16);
+    if (lcid == 0)
+        goto done;
+
+    i = 0;
+    while (LocaleData[i].pValue != NULL)
+    {
+        if (GetLocaleInfoW(lcid,
+                           LocaleData[i].LCType | LOCALE_NOUSEROVERRIDE,
+                           szBuffer,
+                           ARRAYSIZE(szBuffer)))
+        {
+            RegSetValueExW(hLocaleKey,
+                           LocaleData[i].pValue,
+                           0,
+                           REG_SZ,
+                           (PBYTE)szBuffer,
+                           (wcslen(szBuffer) + 1) * sizeof(WCHAR));
+        }
+
+        i++;
+    }
+
+done:
+    RegCloseKey(hLocaleKey);
+}
+
+
+static
+DWORD
+SaveDefaultUserHive(VOID)
+{
+    WCHAR szDefaultUserHive[MAX_PATH];
+    HKEY hUserKey = NULL;
+    DWORD cchSize;
+    DWORD dwError;
+
+    DPRINT("SaveDefaultUserHive()\n");
+
+    cchSize = ARRAYSIZE(szDefaultUserHive);
+    GetDefaultUserProfileDirectoryW(szDefaultUserHive, &cchSize);
+
+    wcscat(szDefaultUserHive, L"\\ntuser.dat");
+
+    dwError = RegOpenKeyExW(HKEY_USERS,
+                            L".DEFAULT",
+                            0,
+                            KEY_READ,
+                            &hUserKey);
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("RegOpenKeyExW() failed (Error %lu)\n", dwError);
+        return dwError;
+    }
+
+    pSetupEnablePrivilege(L"SeBackupPrivilege", TRUE);
+
+    /* Save the Default hive */
+    dwError = RegSaveKeyExW(hUserKey,
+                            szDefaultUserHive,
+                            NULL,
+                            REG_STANDARD_FORMAT);
+    if (dwError == ERROR_ALREADY_EXISTS)
+    {
+        WCHAR szBackupHive[MAX_PATH];
+
+        /* Build the backup hive file name by replacing the extension */
+        wcscpy(szBackupHive, szDefaultUserHive);
+        wcscpy(&szBackupHive[wcslen(szBackupHive) - 4], L".bak");
+
+        /* Back up the existing default user hive by renaming it, replacing any possible existing old backup */
+        if (!MoveFileExW(szDefaultUserHive,
+                         szBackupHive,
+                         MOVEFILE_REPLACE_EXISTING))
+        {
+            dwError = GetLastError();
+            DPRINT1("Failed to create a default-user hive backup '%S', MoveFileExW failed (Error %lu)\n",
+                    szBackupHive, dwError);
+        }
+        else
+        {
+            /* The backup has been done, retry saving the Default hive */
+            dwError = RegSaveKeyExW(hUserKey,
+                                    szDefaultUserHive,
+                                    NULL,
+                                    REG_STANDARD_FORMAT);
+        }
+    }
+    if (dwError != ERROR_SUCCESS)
+    {
+        DPRINT1("RegSaveKeyExW() failed (Error %lu)\n", dwError);
+    }
+
+    pSetupEnablePrivilege(L"SeBackupPrivilege", FALSE);
+
+    RegCloseKey(hUserKey);
+
+    return dwError;
+}
+
+
+static
+DWORD
+InstallReactOS(VOID)
+{
+    WCHAR szBuffer[MAX_PATH];
     HANDLE token;
     TOKEN_PRIVILEGES privs;
     HKEY hKey;
     HINF hShortcutsInf;
+    HANDLE hHotkeyThread;
+    BOOL ret;
 
     InitializeSetupActionLog(FALSE);
-    LogItem(SYSSETUP_SEVERITY_INFORMATION, L"Installing ReactOS");
+    LogItem(NULL, L"Installing ReactOS");
+
+    CreateTempDir(L"TEMP");
+    CreateTempDir(L"TMP");
+
+    if (!InitializeProgramFilesDir())
+    {
+        FatalError("InitializeProgramFilesDir() failed");
+        return 0;
+    }
 
     if (!InitializeProfiles())
     {
@@ -873,10 +1274,9 @@ InstallReactOS(HINSTANCE hInstance)
         return 0;
     }
 
-    CreateTempDir(L"TEMP");
-    CreateTempDir(L"TMP");
+    InitializeDefaultUserLocale();
 
-    if (GetWindowsDirectory(szBuffer, sizeof(szBuffer) / sizeof(TCHAR)))
+    if (GetWindowsDirectoryW(szBuffer, ARRAYSIZE(szBuffer)))
     {
         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
@@ -902,10 +1302,41 @@ InstallReactOS(HINSTANCE hInstance)
         }
 
         PathAddBackslash(szBuffer);
-        _tcscat(szBuffer, _T("system"));
+        wcscat(szBuffer, L"system");
         CreateDirectory(szBuffer, NULL);
     }
 
+    if (SaveDefaultUserHive() != ERROR_SUCCESS)
+    {
+        FatalError("SaveDefaultUserHive() failed");
+        return 0;
+    }
+
+    if (!CopySystemProfile(0))
+    {
+        FatalError("CopySystemProfile() failed");
+        return 0;
+    }
+
+    hHotkeyThread = CreateThread(NULL, 0, HotkeyThread, NULL, 0, NULL);
+
+    /* Hack: Install TCP/IP protocol driver */
+    ret = InstallInfSection(NULL,
+                            L"nettcpip.inf",
+                            L"MS_TCPIP.PrimaryInstall",
+                            L"MS_TCPIP.PrimaryInstall.Services");
+    if (!ret && GetLastError() != ERROR_FILE_NOT_FOUND)
+    {
+        DPRINT("InstallInfSection() failed with error 0x%lx\n", GetLastError());
+    }
+    else
+    {
+        /* Start the TCP/IP protocol driver */
+        SetupStartService(L"Tcpip", FALSE);
+        SetupStartService(L"Dhcp", FALSE);
+    }
+
+
     if (!CommonInstall())
         return 0;
 
@@ -919,7 +1350,7 @@ InstallReactOS(HINSTANCE hInstance)
                                       NULL,
                                       INF_STYLE_WIN4,
                                       NULL);
-    if (hShortcutsInf == INVALID_HANDLE_VALUE) 
+    if (hShortcutsInf == INVALID_HANDLE_VALUE)
     {
         FatalError("Failed to open shortcuts.inf");
         return 0;
@@ -933,45 +1364,30 @@ InstallReactOS(HINSTANCE hInstance)
 
     SetupCloseInfFile(hShortcutsInf);
 
-    /* ROS HACK, as long as NtUnloadKey is not implemented */
+    hShortcutsInf = SetupOpenInfFileW(L"rosapps_shortcuts.inf",
+                                       NULL,
+                                       INF_STYLE_WIN4,
+                                       NULL);
+    if (hShortcutsInf != INVALID_HANDLE_VALUE)
     {
-        NTSTATUS Status = NtUnloadKey(NULL);
-        if (Status == STATUS_NOT_IMPLEMENTED)
+        if (!CreateShortcuts(hShortcutsInf, L"ShortcutFolders"))
         {
-            /* Create the Administrator profile */
-            PROFILEINFOW ProfileInfo;
-            HANDLE hToken;
-            BOOL ret;
-
-            ret = LogonUserW(AdminInfo.Name,
-                             AdminInfo.Domain,
-                             AdminInfo.Password,
-                             LOGON32_LOGON_INTERACTIVE,
-                             LOGON32_PROVIDER_DEFAULT,
-                             &hToken);
-            if (!ret)
-            {
-                FatalError("LogonUserW() failed!");
-                return 0;
-            }
-            ZeroMemory(&ProfileInfo, sizeof(PROFILEINFOW));
-            ProfileInfo.dwSize = sizeof(PROFILEINFOW);
-            ProfileInfo.lpUserName = L"Administrator";
-            ProfileInfo.dwFlags = PI_NOUI;
-            LoadUserProfileW(hToken, &ProfileInfo);
-            CloseHandle(hToken);
-        }
-        else
-        {
-            DPRINT1("ROS HACK not needed anymore. Please remove it\n");
+            FatalError("CreateShortcuts(rosapps) failed");
+            return 0;
         }
+        SetupCloseInfFile(hShortcutsInf);
     }
-    /* END OF ROS HACK */
 
     SetupCloseInfFile(hSysSetupInf);
     SetSetupType(0);
 
-    LogItem(SYSSETUP_SEVERITY_INFORMATION, L"Installing ReactOS done");
+    if (hHotkeyThread)
+    {
+        PostThreadMessage(GetThreadId(hHotkeyThread), WM_QUIT, 0, 0);
+        CloseHandle(hHotkeyThread);
+    }
+
+    LogItem(NULL, L"Installing ReactOS done");
     TerminateSetupActionLog();
 
     if (AdminInfo.Name != NULL)
@@ -989,23 +1405,21 @@ InstallReactOS(HINSTANCE hInstance)
         FatalError("OpenProcessToken() failed!");
         return 0;
     }
-    if (!LookupPrivilegeValue(
-        NULL,
-        SE_SHUTDOWN_NAME,
-        &privs.Privileges[0].Luid))
+    if (!LookupPrivilegeValue(NULL,
+                              SE_SHUTDOWN_NAME,
+                              &privs.Privileges[0].Luid))
     {
         FatalError("LookupPrivilegeValue() failed!");
         return 0;
     }
     privs.PrivilegeCount = 1;
     privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-    if (AdjustTokenPrivileges(
-        token,
-        FALSE,
-        &privs,
-        0,
-        (PTOKEN_PRIVILEGES)NULL,
-        NULL) == 0)
+    if (AdjustTokenPrivileges(token,
+                              FALSE,
+                              &privs,
+                              0,
+                              (PTOKEN_PRIVILEGES)NULL,
+                              NULL) == 0)
     {
         FatalError("AdjustTokenPrivileges() failed!");
         return 0;
@@ -1016,6 +1430,39 @@ InstallReactOS(HINSTANCE hInstance)
 }
 
 
+/*
+ * Standard Windows-compatible export, which dispatches
+ * to either 'InstallReactOS' or 'InstallLiveCD'.
+ */
+INT
+WINAPI
+InstallWindowsNt(INT argc, WCHAR** argv)
+{
+    INT i;
+    PWSTR p;
+
+    for (i = 0; i < argc; ++i)
+    {
+        p = argv[i];
+        if (*p == L'-')
+        {
+            p++;
+
+            // NOTE: On Windows, "mini" means "minimal UI", and can be used
+            // in addition to "newsetup"; these options are not exclusive.
+            if (_wcsicmp(p, L"newsetup") == 0)
+                return (INT)InstallReactOS();
+            else if (_wcsicmp(p, L"mini") == 0)
+                return (INT)InstallLiveCD();
+
+            /* Add support for other switches */
+        }
+    }
+
+    return 0;
+}
+
+
 /*
  * @unimplemented
  */
@@ -1051,3 +1498,48 @@ SetupChangeLocale(HWND hWnd, LCID Lcid)
 {
     return SetupChangeLocaleEx(hWnd, Lcid, NULL, 0, 0, 0);
 }
+
+
+DWORD
+WINAPI
+SetupStartService(
+    LPCWSTR lpServiceName,
+    BOOL bWait)
+{
+    SC_HANDLE hManager = NULL;
+    SC_HANDLE hService = NULL;
+    DWORD dwError = ERROR_SUCCESS;
+
+    hManager = OpenSCManagerW(NULL,
+                              NULL,
+                              SC_MANAGER_ALL_ACCESS);
+    if (hManager == NULL)
+    {
+        dwError = GetLastError();
+        goto done;
+    }
+
+    hService = OpenServiceW(hManager,
+                            lpServiceName,
+                            SERVICE_START);
+    if (hService == NULL)
+    {
+        dwError = GetLastError();
+        goto done;
+    }
+
+    if (!StartService(hService, 0, NULL))
+    {
+        dwError = GetLastError();
+        goto done;
+    }
+
+done:
+    if (hService != NULL)
+        CloseServiceHandle(hService);
+
+    if (hManager != NULL)
+        CloseServiceHandle(hManager);
+
+    return dwError;
+}