mirror of
https://github.com/reactos/reactos.git
synced 2025-05-03 21:00:15 +00:00

Use a `[Shell]` section with `DefaultThemesOff` and `CustomDefaultThemeFile` values, specifying respectively whether to use the classic theme or a custom one, and the complete path to the custom .theme (or .msstyles) file. These values are compatible with those documented in the "MS Windows Preinstallation Reference" help file of the Windows installation CD (DEPLOY.CAB\ref.chm) [BOOTDATA] bootcd/unattend.inf: Use the Shell/CustomDefaultThemeFile value to specify which theme to use
1811 lines
49 KiB
C
1811 lines
49 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* PURPOSE: System setup
|
|
* FILE: dll/win32/syssetup/install.c
|
|
* PROGRAMER: Eric Kohl
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "precomp.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>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
//DWORD WINAPI
|
|
//CMP_WaitNoPendingInstallEvents(DWORD dwTimeout);
|
|
|
|
DWORD WINAPI
|
|
SetupStartService(LPCWSTR lpServiceName, BOOL bWait);
|
|
|
|
/* GLOBALS ******************************************************************/
|
|
|
|
HINF hSysSetupInf = INVALID_HANDLE_VALUE;
|
|
ADMIN_INFO AdminInfo;
|
|
|
|
typedef struct _DLG_DATA
|
|
{
|
|
HBITMAP hLogoBitmap;
|
|
HBITMAP hBarBitmap;
|
|
HWND hWndBarCtrl;
|
|
DWORD BarCounter;
|
|
DWORD BarWidth;
|
|
DWORD BarHeight;
|
|
} DLG_DATA, *PDLG_DATA;
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
static VOID
|
|
FatalError(char *pszFmt,...)
|
|
{
|
|
char szBuffer[512];
|
|
va_list ap;
|
|
|
|
va_start(ap, pszFmt);
|
|
vsprintf(szBuffer, pszFmt, ap);
|
|
va_end(ap);
|
|
|
|
LogItem(NULL, L"Failed");
|
|
|
|
strcat(szBuffer, "\nRebooting now!");
|
|
MessageBoxA(NULL,
|
|
szBuffer,
|
|
"ReactOS Setup",
|
|
MB_OK);
|
|
}
|
|
|
|
static HRESULT
|
|
CreateShellLink(
|
|
LPCWSTR pszLinkPath,
|
|
LPCWSTR pszCmd,
|
|
LPCWSTR pszArg,
|
|
LPCWSTR pszDir,
|
|
LPCWSTR pszIconPath,
|
|
INT iIconNr,
|
|
LPCWSTR pszComment)
|
|
{
|
|
IShellLinkW *psl;
|
|
IPersistFile *ppf;
|
|
|
|
HRESULT hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID*)&psl);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IShellLinkW_SetPath(psl, pszCmd);
|
|
|
|
if (pszArg)
|
|
hr = IShellLinkW_SetArguments(psl, pszArg);
|
|
|
|
if (pszDir)
|
|
hr = IShellLinkW_SetWorkingDirectory(psl, pszDir);
|
|
|
|
if (pszIconPath)
|
|
hr = IShellLinkW_SetIconLocation(psl, pszIconPath, iIconNr);
|
|
|
|
if (pszComment)
|
|
hr = IShellLinkW_SetDescription(psl, pszComment);
|
|
|
|
hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IPersistFile_Save(ppf, pszLinkPath, TRUE);
|
|
IPersistFile_Release(ppf);
|
|
}
|
|
|
|
IShellLinkW_Release(psl);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
CreateShortcut(
|
|
LPCWSTR pszFolder,
|
|
LPCWSTR pszName,
|
|
LPCWSTR pszCommand,
|
|
LPCWSTR pszDescription,
|
|
INT iIconNr,
|
|
LPCWSTR pszWorkingDir,
|
|
LPCWSTR pszArgs)
|
|
{
|
|
DWORD dwLen;
|
|
LPWSTR Ptr;
|
|
LPWSTR lpFilePart;
|
|
WCHAR szPath[MAX_PATH];
|
|
WCHAR szWorkingDirBuf[MAX_PATH];
|
|
|
|
/* If no working directory is provided, try to compute a default one */
|
|
if (pszWorkingDir == NULL || pszWorkingDir[0] == L'\0')
|
|
{
|
|
if (ExpandEnvironmentStringsW(pszCommand, szPath, ARRAYSIZE(szPath)) == 0)
|
|
wcscpy(szPath, pszCommand);
|
|
|
|
dwLen = GetFullPathNameW(szPath,
|
|
ARRAYSIZE(szWorkingDirBuf),
|
|
szWorkingDirBuf,
|
|
&lpFilePart);
|
|
if (dwLen != 0 && dwLen <= ARRAYSIZE(szWorkingDirBuf))
|
|
{
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
wcscpy(Ptr, pszName);
|
|
|
|
/* Create the shortcut */
|
|
return SUCCEEDED(CreateShellLink(szPath,
|
|
pszCommand,
|
|
pszArgs,
|
|
pszWorkingDir,
|
|
/* Special value to indicate no icon */
|
|
(iIconNr != -1 ? pszCommand : NULL),
|
|
iIconNr,
|
|
pszDescription));
|
|
}
|
|
|
|
|
|
static BOOL
|
|
CreateShortcutsFromSection(
|
|
_In_ PITEMSDATA pItemsData,
|
|
_In_ PREGISTRATIONNOTIFY pNotify,
|
|
_In_ HINF hinf,
|
|
_In_ LPWSTR pszSection,
|
|
_In_ LPCWSTR pszFolder)
|
|
{
|
|
INFCONTEXT Context;
|
|
DWORD dwFieldCount;
|
|
INT iIconNr;
|
|
WCHAR szCommand[MAX_PATH];
|
|
WCHAR szName[MAX_PATH];
|
|
WCHAR szDescription[MAX_PATH];
|
|
WCHAR szDirectory[MAX_PATH];
|
|
WCHAR szArgs[MAX_PATH];
|
|
|
|
if (!SetupFindFirstLine(hinf, pszSection, NULL, &Context))
|
|
return FALSE;
|
|
|
|
do
|
|
{
|
|
pNotify->Progress++;
|
|
|
|
SendMessage(pItemsData->hwndDlg, PM_STEP_START, 0, (LPARAM)pNotify);
|
|
|
|
dwFieldCount = SetupGetFieldCount(&Context);
|
|
if (dwFieldCount < 3)
|
|
continue;
|
|
|
|
if (!SetupGetStringFieldW(&Context, 1, szCommand, ARRAYSIZE(szCommand), NULL))
|
|
continue;
|
|
|
|
if (!SetupGetStringFieldW(&Context, 2, szName, ARRAYSIZE(szName), NULL))
|
|
continue;
|
|
|
|
if (!SetupGetStringFieldW(&Context, 3, szDescription, ARRAYSIZE(szDescription), NULL))
|
|
continue;
|
|
|
|
if (dwFieldCount < 4 || !SetupGetIntField(&Context, 4, &iIconNr))
|
|
iIconNr = -1; /* Special value to indicate no icon */
|
|
|
|
if (dwFieldCount < 5 || !SetupGetStringFieldW(&Context, 5, szDirectory, ARRAYSIZE(szDirectory), NULL))
|
|
szDirectory[0] = L'\0';
|
|
|
|
if (dwFieldCount < 6 || !SetupGetStringFieldW(&Context, 6, szArgs, ARRAYSIZE(szArgs), NULL))
|
|
szArgs[0] = L'\0';
|
|
|
|
wcscat(szName, L".lnk");
|
|
|
|
CreateShortcut(pszFolder, szName, szCommand, szDescription, iIconNr, szDirectory, szArgs);
|
|
|
|
SendMessage(pItemsData->hwndDlg, PM_STEP_END, 0, (LPARAM)pNotify);
|
|
|
|
} while (SetupFindNextLine(&Context, &Context));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
CreateShortcuts(
|
|
_In_ PITEMSDATA pItemsData,
|
|
_In_ PREGISTRATIONNOTIFY pNotify,
|
|
_In_ HINF hinf,
|
|
_In_ LPCWSTR szSection)
|
|
{
|
|
INFCONTEXT Context;
|
|
WCHAR szPath[MAX_PATH];
|
|
WCHAR szFolder[MAX_PATH];
|
|
WCHAR szFolderSection[MAX_PATH];
|
|
INT csidl;
|
|
|
|
CoInitialize(NULL);
|
|
|
|
if (!SetupFindFirstLine(hinf, szSection, NULL, &Context))
|
|
return FALSE;
|
|
|
|
do
|
|
{
|
|
if (SetupGetFieldCount(&Context) < 2)
|
|
continue;
|
|
|
|
if (!SetupGetStringFieldW(&Context, 0, szFolderSection, ARRAYSIZE(szFolderSection), NULL))
|
|
continue;
|
|
|
|
if (!SetupGetIntField(&Context, 1, &csidl))
|
|
continue;
|
|
|
|
if (!SetupGetStringFieldW(&Context, 2, szFolder, ARRAYSIZE(szFolder), NULL))
|
|
continue;
|
|
|
|
if (FAILED(SHGetFolderPathAndSubDirW(NULL, csidl|CSIDL_FLAG_CREATE, (HANDLE)-1, SHGFP_TYPE_DEFAULT, szFolder, szPath)))
|
|
continue;
|
|
|
|
CreateShortcutsFromSection(pItemsData, pNotify, hinf, szFolderSection, szPath);
|
|
|
|
} while (SetupFindNextLine(&Context, &Context));
|
|
|
|
CoUninitialize();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static LONG
|
|
CountShortcuts(
|
|
_In_ HINF hinf,
|
|
_In_ LPCWSTR szSection)
|
|
{
|
|
INFCONTEXT Context;
|
|
WCHAR szFolderSection[MAX_PATH];
|
|
LONG Steps = 0;
|
|
|
|
if (!SetupFindFirstLine(hinf, szSection, NULL, &Context))
|
|
return FALSE;
|
|
|
|
do
|
|
{
|
|
if (SetupGetFieldCount(&Context) < 2)
|
|
continue;
|
|
|
|
if (!SetupGetStringFieldW(&Context, 0, szFolderSection, ARRAYSIZE(szFolderSection), NULL))
|
|
continue;
|
|
|
|
Steps += SetupGetLineCountW(hinf, szFolderSection);
|
|
} while (SetupFindNextLine(&Context, &Context));
|
|
|
|
return Steps;
|
|
}
|
|
|
|
VOID
|
|
InstallStartMenuItems(
|
|
_In_ PITEMSDATA pItemsData)
|
|
{
|
|
HINF hShortcutsInf1 = INVALID_HANDLE_VALUE;
|
|
HINF hShortcutsInf2 = INVALID_HANDLE_VALUE;
|
|
LONG Steps = 0;
|
|
DWORD LastError = 0;
|
|
REGISTRATIONNOTIFY Notify;
|
|
|
|
ZeroMemory(&Notify, sizeof(Notify));
|
|
|
|
hShortcutsInf1 = SetupOpenInfFileW(L"shortcuts.inf",
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
if (hShortcutsInf1 == INVALID_HANDLE_VALUE)
|
|
{
|
|
DPRINT1("Failed to open shortcuts.inf");
|
|
return;
|
|
}
|
|
|
|
hShortcutsInf2 = SetupOpenInfFileW(L"rosapps_shortcuts.inf",
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
|
|
Steps = CountShortcuts(hShortcutsInf1, L"ShortcutFolders");
|
|
if (hShortcutsInf2 != INVALID_HANDLE_VALUE)
|
|
Steps += CountShortcuts(hShortcutsInf2, L"ShortcutFolders");
|
|
|
|
SendMessage(pItemsData->hwndDlg, PM_ITEM_START, 1, (LPARAM)Steps);
|
|
|
|
if (!CreateShortcuts(pItemsData, &Notify, hShortcutsInf1, L"ShortcutFolders"))
|
|
{
|
|
DPRINT1("CreateShortcuts() failed");
|
|
goto done;
|
|
}
|
|
|
|
if (hShortcutsInf2 != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!CreateShortcuts(pItemsData, &Notify, hShortcutsInf2, L"ShortcutFolders"))
|
|
{
|
|
DPRINT1("CreateShortcuts(rosapps) failed");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (hShortcutsInf2 != INVALID_HANDLE_VALUE)
|
|
SetupCloseInfFile(hShortcutsInf2);
|
|
|
|
if (hShortcutsInf1 != INVALID_HANDLE_VALUE)
|
|
SetupCloseInfFile(hShortcutsInf1);
|
|
|
|
SendMessage(pItemsData->hwndDlg, PM_ITEM_END, 1, LastError);
|
|
}
|
|
|
|
static VOID
|
|
CreateTempDir(
|
|
IN LPCWSTR VarName)
|
|
{
|
|
WCHAR szTempDir[MAX_PATH];
|
|
WCHAR szBuffer[MAX_PATH];
|
|
DWORD dwLength;
|
|
HKEY hKey;
|
|
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey) != ERROR_SUCCESS)
|
|
{
|
|
FatalError("Error: %lu\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
/* Get temp dir */
|
|
dwLength = sizeof(szBuffer);
|
|
if (RegQueryValueExW(hKey,
|
|
VarName,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)szBuffer,
|
|
&dwLength) != ERROR_SUCCESS)
|
|
{
|
|
FatalError("Error: %lu\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Expand it */
|
|
if (!ExpandEnvironmentStringsW(szBuffer, szTempDir, ARRAYSIZE(szTempDir)))
|
|
{
|
|
FatalError("Error: %lu\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Create profiles directory */
|
|
if (!CreateDirectoryW(szTempDir, NULL))
|
|
{
|
|
if (GetLastError() != ERROR_ALREADY_EXISTS)
|
|
{
|
|
FatalError("Error: %lu\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
static BOOL
|
|
InstallSysSetupInfDevices(VOID)
|
|
{
|
|
INFCONTEXT InfContext;
|
|
WCHAR szLineBuffer[256];
|
|
DWORD dwLineLength;
|
|
|
|
if (!SetupFindFirstLineW(hSysSetupInf,
|
|
L"DeviceInfsToInstall",
|
|
NULL,
|
|
&InfContext))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (!SetupGetStringFieldW(&InfContext,
|
|
0,
|
|
szLineBuffer,
|
|
ARRAYSIZE(szLineBuffer),
|
|
&dwLineLength))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetupDiInstallClassW(NULL, szLineBuffer, DI_QUIETINSTALL, NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
while (SetupFindNextLine(&InfContext, &InfContext));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
InstallSysSetupInfComponents(VOID)
|
|
{
|
|
INFCONTEXT InfContext;
|
|
WCHAR szNameBuffer[256];
|
|
WCHAR szSectionBuffer[256];
|
|
HINF hComponentInf = INVALID_HANDLE_VALUE;
|
|
|
|
if (!SetupFindFirstLineW(hSysSetupInf,
|
|
L"Infs.Always",
|
|
NULL,
|
|
&InfContext))
|
|
{
|
|
DPRINT("No Inf.Always section found\n");
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (!SetupGetStringFieldW(&InfContext,
|
|
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,
|
|
ARRAYSIZE(szSectionBuffer),
|
|
NULL))
|
|
{
|
|
FatalError("Error while trying to get component install section\n");
|
|
return FALSE;
|
|
}
|
|
|
|
DPRINT("Trying to execute install section '%S' from '%S'\n", szSectionBuffer, szNameBuffer);
|
|
|
|
hComponentInf = SetupOpenInfFileW(szNameBuffer,
|
|
NULL,
|
|
INF_STYLE_WIN4,
|
|
NULL);
|
|
|
|
if (hComponentInf == INVALID_HANDLE_VALUE)
|
|
{
|
|
FatalError("SetupOpenInfFileW() failed to open '%S' (Error: %lu)\n", szNameBuffer, GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetupInstallFromInfSectionW(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);
|
|
return FALSE;
|
|
}
|
|
|
|
SetupCloseInfFile(hComponentInf);
|
|
}
|
|
while (SetupFindNextLine(&InfContext, &InfContext));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
RegisterTypeLibraries(
|
|
_In_ PITEMSDATA pItemsData,
|
|
_In_ PREGISTRATIONNOTIFY pNotify,
|
|
_In_ HINF hinf,
|
|
_In_ LPCWSTR szSection)
|
|
{
|
|
INFCONTEXT InfContext;
|
|
BOOL res;
|
|
WCHAR szName[MAX_PATH];
|
|
WCHAR szPath[MAX_PATH];
|
|
INT csidl;
|
|
LPWSTR p;
|
|
HMODULE hmod;
|
|
HRESULT hret;
|
|
|
|
/* Begin iterating the entries in the inf section */
|
|
res = SetupFindFirstLine(hinf, szSection, NULL, &InfContext);
|
|
if (!res) return FALSE;
|
|
|
|
do
|
|
{
|
|
/* Get the name of the current type library */
|
|
if (!SetupGetStringFieldW(&InfContext, 1, szName, ARRAYSIZE(szName), NULL))
|
|
{
|
|
FatalError("SetupGetStringFieldW failed\n");
|
|
continue;
|
|
}
|
|
|
|
if (!SetupGetIntField(&InfContext, 2, &csidl))
|
|
csidl = CSIDL_SYSTEM;
|
|
|
|
hret = SHGetFolderPathW(NULL, csidl, NULL, 0, szPath);
|
|
if (FAILED(hret))
|
|
{
|
|
FatalError("SHGetFolderPathW failed hret=0x%lx\n", hret);
|
|
continue;
|
|
}
|
|
|
|
p = PathAddBackslash(szPath);
|
|
wcscpy(p, szName);
|
|
|
|
if (pItemsData && pNotify)
|
|
{
|
|
pNotify->Progress++;
|
|
pNotify->CurrentItem = szName;
|
|
|
|
DPRINT("RegisterTypeLibraries: Start step %ld\n", pNotify->Progress);
|
|
SendMessage(pItemsData->hwndDlg, PM_STEP_START, 0, (LPARAM)pNotify);
|
|
}
|
|
|
|
hmod = LoadLibraryW(szPath);
|
|
if (hmod == NULL)
|
|
{
|
|
FatalError("LoadLibraryW failed\n");
|
|
continue;
|
|
}
|
|
|
|
__wine_register_resources(hmod);
|
|
|
|
if (pItemsData && pNotify)
|
|
{
|
|
DPRINT("RegisterTypeLibraries: End step %ld\n", pNotify->Progress);
|
|
SendMessage(pItemsData->hwndDlg, PM_STEP_END, 0, (LPARAM)pNotify);
|
|
}
|
|
|
|
} while (SetupFindNextLine(&InfContext, &InfContext));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
EnableUserModePnpManager(VOID)
|
|
{
|
|
SC_HANDLE hSCManager = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
SERVICE_STATUS_PROCESS ServiceStatus;
|
|
BOOL bRet = FALSE;
|
|
DWORD BytesNeeded, WaitTime;
|
|
|
|
hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
|
|
if (hSCManager == NULL)
|
|
{
|
|
DPRINT1("Unable to open the service control manager.\n");
|
|
DPRINT1("Last Error %d\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
hService = OpenServiceW(hSCManager,
|
|
L"PlugPlay",
|
|
SERVICE_CHANGE_CONFIG | SERVICE_START | SERVICE_QUERY_STATUS);
|
|
if (hService == NULL)
|
|
{
|
|
DPRINT1("Unable to open PlugPlay service\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
bRet = ChangeServiceConfigW(hService,
|
|
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");
|
|
goto cleanup;
|
|
}
|
|
|
|
bRet = StartServiceW(hService, 0, NULL);
|
|
if (!bRet && (GetLastError() != ERROR_SERVICE_ALREADY_RUNNING))
|
|
{
|
|
DPRINT1("Unable to start service\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
bRet = QueryServiceStatusEx(hService,
|
|
SC_STATUS_PROCESS_INFO,
|
|
(LPBYTE)&ServiceStatus,
|
|
sizeof(ServiceStatus),
|
|
&BytesNeeded);
|
|
if (!bRet)
|
|
{
|
|
DPRINT1("QueryServiceStatusEx() failed for PlugPlay service (error 0x%x)\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
if (ServiceStatus.dwCurrentState != SERVICE_START_PENDING)
|
|
break;
|
|
|
|
WaitTime = ServiceStatus.dwWaitHint / 10;
|
|
if (WaitTime < 1000) WaitTime = 1000;
|
|
else if (WaitTime > 10000) WaitTime = 10000;
|
|
Sleep(WaitTime);
|
|
};
|
|
|
|
if (ServiceStatus.dwCurrentState != SERVICE_RUNNING)
|
|
{
|
|
bRet = FALSE;
|
|
DPRINT1("Failed to start PlugPlay service\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
cleanup:
|
|
if (hService != NULL)
|
|
CloseServiceHandle(hService);
|
|
if (hSCManager != NULL)
|
|
CloseServiceHandle(hSCManager);
|
|
return bRet;
|
|
}
|
|
|
|
static VOID
|
|
AdjustStatusMessageWindow(HWND hwndDlg, PDLG_DATA pDlgData)
|
|
{
|
|
INT xOld, yOld, cxOld, cyOld;
|
|
INT xNew, yNew, cxNew, cyNew;
|
|
INT cxLabel, cyLabel, dyLabel;
|
|
RECT rc, rcBar, rcLabel, rcWnd;
|
|
BITMAP bmLogo, bmBar;
|
|
DWORD style, exstyle;
|
|
HWND hwndLogo = GetDlgItem(hwndDlg, IDC_ROSLOGO);
|
|
HWND hwndBar = GetDlgItem(hwndDlg, IDC_BAR);
|
|
HWND hwndLabel = GetDlgItem(hwndDlg, IDC_STATUSLABEL);
|
|
|
|
/* This adjustment is for CJK only */
|
|
switch (PRIMARYLANGID(GetUserDefaultLangID()))
|
|
{
|
|
case LANG_CHINESE:
|
|
case LANG_JAPANESE:
|
|
case LANG_KOREAN:
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (!GetObjectW(pDlgData->hLogoBitmap, sizeof(BITMAP), &bmLogo) ||
|
|
!GetObjectW(pDlgData->hBarBitmap, sizeof(BITMAP), &bmBar))
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetWindowRect(hwndBar, &rcBar);
|
|
MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcBar, 2);
|
|
dyLabel = bmLogo.bmHeight - rcBar.top;
|
|
|
|
GetWindowRect(hwndLabel, &rcLabel);
|
|
MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcLabel, 2);
|
|
cxLabel = rcLabel.right - rcLabel.left;
|
|
cyLabel = rcLabel.bottom - rcLabel.top;
|
|
|
|
MoveWindow(hwndLogo, 0, 0, bmLogo.bmWidth, bmLogo.bmHeight, TRUE);
|
|
MoveWindow(hwndBar, 0, bmLogo.bmHeight, bmLogo.bmWidth, bmBar.bmHeight, TRUE);
|
|
MoveWindow(hwndLabel, rcLabel.left, rcLabel.top + dyLabel, cxLabel, cyLabel, TRUE);
|
|
|
|
GetWindowRect(hwndDlg, &rcWnd);
|
|
xOld = rcWnd.left;
|
|
yOld = rcWnd.top;
|
|
cxOld = rcWnd.right - rcWnd.left;
|
|
cyOld = rcWnd.bottom - rcWnd.top;
|
|
|
|
GetClientRect(hwndDlg, &rc);
|
|
SetRect(&rc, 0, 0, bmLogo.bmWidth, rc.bottom - rc.top); /* new client size */
|
|
|
|
style = (DWORD)GetWindowLongPtrW(hwndDlg, GWL_STYLE);
|
|
exstyle = (DWORD)GetWindowLongPtrW(hwndDlg, GWL_EXSTYLE);
|
|
AdjustWindowRectEx(&rc, style, FALSE, exstyle);
|
|
|
|
cxNew = rc.right - rc.left;
|
|
cyNew = (rc.bottom - rc.top) + dyLabel;
|
|
xNew = xOld - (cxNew - cxOld) / 2;
|
|
yNew = yOld - (cyNew - cyOld) / 2;
|
|
MoveWindow(hwndDlg, xNew, yNew, cxNew, cyNew, TRUE);
|
|
}
|
|
|
|
static INT_PTR CALLBACK
|
|
StatusMessageWindowProc(
|
|
IN HWND hwndDlg,
|
|
IN UINT uMsg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
PDLG_DATA pDlgData;
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
|
|
pDlgData = (PDLG_DATA)GetWindowLongPtrW(hwndDlg, GWLP_USERDATA);
|
|
|
|
/* pDlgData is required for each case except WM_INITDIALOG */
|
|
if (uMsg != WM_INITDIALOG && pDlgData == NULL) return FALSE;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
BITMAP bm;
|
|
WCHAR szMsg[256];
|
|
|
|
/* Allocate pDlgData */
|
|
pDlgData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pDlgData));
|
|
if (pDlgData)
|
|
{
|
|
/* Set pDlgData to GWLP_USERDATA, so we can get it for new messages */
|
|
SetWindowLongPtrW(hwndDlg, GWLP_USERDATA, (LONG_PTR)pDlgData);
|
|
|
|
/* Load bitmaps */
|
|
pDlgData->hLogoBitmap = LoadImageW(hDllInstance,
|
|
MAKEINTRESOURCEW(IDB_REACTOS), IMAGE_BITMAP,
|
|
0, 0, LR_DEFAULTCOLOR);
|
|
|
|
pDlgData->hBarBitmap = LoadImageW(hDllInstance, MAKEINTRESOURCEW(IDB_LINE),
|
|
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
|
|
GetObject(pDlgData->hBarBitmap, sizeof(bm), &bm);
|
|
pDlgData->BarWidth = bm.bmWidth;
|
|
pDlgData->BarHeight = bm.bmHeight;
|
|
|
|
if (pDlgData->hLogoBitmap && pDlgData->hBarBitmap)
|
|
{
|
|
if (SetTimer(hwndDlg, IDT_BAR, 20, NULL) == 0)
|
|
{
|
|
DPRINT1("SetTimer(IDT_BAR) failed: %lu\n", GetLastError());
|
|
}
|
|
|
|
/* Get the animation bar control */
|
|
pDlgData->hWndBarCtrl = GetDlgItem(hwndDlg, IDC_BAR);
|
|
}
|
|
}
|
|
|
|
/* Get and set status text */
|
|
if (!LoadStringW(hDllInstance, IDS_STATUS_INSTALL_DEV, szMsg, ARRAYSIZE(szMsg)))
|
|
return FALSE;
|
|
SetDlgItemTextW(hwndDlg, IDC_STATUSLABEL, szMsg);
|
|
|
|
AdjustStatusMessageWindow(hwndDlg, pDlgData);
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
{
|
|
if (pDlgData->hBarBitmap)
|
|
{
|
|
/*
|
|
* Default rotation bar image width is 413 (same as logo)
|
|
* We can divide 413 by 7 without remainder
|
|
*/
|
|
pDlgData->BarCounter = (pDlgData->BarCounter + 7) % pDlgData->BarWidth;
|
|
InvalidateRect(pDlgData->hWndBarCtrl, NULL, FALSE);
|
|
UpdateWindow(pDlgData->hWndBarCtrl);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DRAWITEM:
|
|
{
|
|
LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam;
|
|
|
|
if (lpDis->CtlID != IDC_BAR)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (pDlgData->hBarBitmap)
|
|
{
|
|
HDC hdcMem;
|
|
HGDIOBJ hOld;
|
|
DWORD off = pDlgData->BarCounter;
|
|
DWORD iw = pDlgData->BarWidth;
|
|
DWORD ih = pDlgData->BarHeight;
|
|
|
|
hdcMem = CreateCompatibleDC(lpDis->hDC);
|
|
hOld = SelectObject(hdcMem, pDlgData->hBarBitmap);
|
|
BitBlt(lpDis->hDC, off, 0, iw - off, ih, hdcMem, 0, 0, SRCCOPY);
|
|
BitBlt(lpDis->hDC, 0, 0, off, ih, hdcMem, iw - off, 0, SRCCOPY);
|
|
SelectObject(hdcMem, hOld);
|
|
DeleteDC(hdcMem);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
if (pDlgData->hBarBitmap)
|
|
{
|
|
KillTimer(hwndDlg, IDT_BAR);
|
|
}
|
|
|
|
DeleteObject(pDlgData->hLogoBitmap);
|
|
DeleteObject(pDlgData->hBarBitmap);
|
|
HeapFree(GetProcessHeap(), 0, pDlgData);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static DWORD WINAPI
|
|
ShowStatusMessageThread(
|
|
IN LPVOID lpParameter)
|
|
{
|
|
HWND hWnd;
|
|
MSG Msg;
|
|
UNREFERENCED_PARAMETER(lpParameter);
|
|
|
|
hWnd = CreateDialogParam(hDllInstance,
|
|
MAKEINTRESOURCE(IDD_STATUSWINDOW_DLG),
|
|
GetDesktopWindow(),
|
|
StatusMessageWindowProc,
|
|
(LPARAM)NULL);
|
|
if (!hWnd)
|
|
return 0;
|
|
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
|
|
/* Message loop for the Status window */
|
|
while (GetMessage(&Msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&Msg);
|
|
DispatchMessage(&Msg);
|
|
}
|
|
|
|
EndDialog(hWnd, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LONG
|
|
ReadRegSzKey(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR pszKey,
|
|
OUT LPWSTR* pValue)
|
|
{
|
|
LONG rc;
|
|
DWORD dwType;
|
|
DWORD cbData = 0;
|
|
LPWSTR pwszValue;
|
|
|
|
if (!pValue)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
*pValue = NULL;
|
|
rc = RegQueryValueExW(hKey, pszKey, NULL, &dwType, NULL, &cbData);
|
|
if (rc != ERROR_SUCCESS)
|
|
return rc;
|
|
if (dwType != REG_SZ)
|
|
return ERROR_FILE_NOT_FOUND;
|
|
pwszValue = HeapAlloc(GetProcessHeap(), 0, cbData + sizeof(WCHAR));
|
|
if (!pwszValue)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
rc = RegQueryValueExW(hKey, pszKey, NULL, NULL, (LPBYTE)pwszValue, &cbData);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, pwszValue);
|
|
return rc;
|
|
}
|
|
/* NULL-terminate the string */
|
|
pwszValue[cbData / sizeof(WCHAR)] = '\0';
|
|
|
|
*pValue = pwszValue;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static BOOL
|
|
IsConsoleBoot(VOID)
|
|
{
|
|
HKEY hControlKey = NULL;
|
|
LPWSTR pwszSystemStartOptions = NULL;
|
|
LPWSTR pwszCurrentOption, pwszNextOption; /* Pointers into SystemStartOptions */
|
|
BOOL bConsoleBoot = FALSE;
|
|
LONG rc;
|
|
|
|
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\CurrentControlSet\\Control",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hControlKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
|
|
rc = ReadRegSzKey(hControlKey, L"SystemStartOptions", &pwszSystemStartOptions);
|
|
if (rc != ERROR_SUCCESS)
|
|
goto cleanup;
|
|
|
|
/* Check for CONSOLE switch in SystemStartOptions */
|
|
pwszCurrentOption = pwszSystemStartOptions;
|
|
while (pwszCurrentOption)
|
|
{
|
|
pwszNextOption = wcschr(pwszCurrentOption, L' ');
|
|
if (pwszNextOption)
|
|
*pwszNextOption = L'\0';
|
|
if (_wcsicmp(pwszCurrentOption, L"CONSOLE") == 0)
|
|
{
|
|
DPRINT("Found %S. Switching to console boot\n", pwszCurrentOption);
|
|
bConsoleBoot = TRUE;
|
|
goto cleanup;
|
|
}
|
|
pwszCurrentOption = pwszNextOption ? pwszNextOption + 1 : NULL;
|
|
}
|
|
|
|
cleanup:
|
|
if (hControlKey != NULL)
|
|
RegCloseKey(hControlKey);
|
|
if (pwszSystemStartOptions)
|
|
HeapFree(GetProcessHeap(), 0, pwszSystemStartOptions);
|
|
return bConsoleBoot;
|
|
}
|
|
|
|
extern VOID
|
|
EnableVisualTheme(
|
|
_In_opt_ HWND hwndParent,
|
|
_In_opt_ PCWSTR ThemeFile);
|
|
|
|
/**
|
|
* @brief
|
|
* Pre-process unattended file to apply early settings.
|
|
*
|
|
* @param[in] IsInstall
|
|
* TRUE if this is ReactOS installation, invoked from InstallReactOS(),
|
|
* FALSE if this is run as part of LiveCD, invoked form InstallLiveCD().
|
|
**/
|
|
static VOID
|
|
PreprocessUnattend(
|
|
_In_ BOOL IsInstall)
|
|
{
|
|
WCHAR szPath[MAX_PATH];
|
|
WCHAR szValue[MAX_PATH];
|
|
BOOL bDefaultThemesOff;
|
|
|
|
if (IsInstall)
|
|
{
|
|
/* See also wizard.c!ProcessSetupInf()
|
|
* Retrieve the path of the setup INF */
|
|
GetSystemDirectoryW(szPath, _countof(szPath));
|
|
wcscat(szPath, L"\\$winnt$.inf");
|
|
}
|
|
else
|
|
{
|
|
/* See also userinit/livecd.c!RunLiveCD() */
|
|
GetWindowsDirectoryW(szPath, _countof(szPath));
|
|
wcscat(szPath, L"\\unattend.inf");
|
|
}
|
|
|
|
/*
|
|
* Apply initial default theming
|
|
*/
|
|
|
|
/* Check whether to use the classic theme (TRUE) instead of the default theme */
|
|
bDefaultThemesOff = FALSE;
|
|
if (GetPrivateProfileStringW(L"Shell", L"DefaultThemesOff", L"no", szValue, _countof(szValue), szPath) && *szValue)
|
|
bDefaultThemesOff = (_wcsicmp(szValue, L"yes") == 0);
|
|
|
|
if (!bDefaultThemesOff)
|
|
{
|
|
/* Retrieve the complete path to a .theme (or for ReactOS, a .msstyles) file */
|
|
if (!GetPrivateProfileStringW(L"Shell", L"CustomDefaultThemeFile", NULL, szValue, _countof(szValue), szPath) || !*szValue)
|
|
bDefaultThemesOff = TRUE; // None specified, fall back to the classic theme.
|
|
}
|
|
|
|
/* Enable the chosen theme, or use the classic theme */
|
|
EnableVisualTheme(NULL, bDefaultThemesOff ? NULL : szValue);
|
|
}
|
|
|
|
static BOOL
|
|
CommonInstall(VOID)
|
|
{
|
|
HANDLE hThread = NULL;
|
|
BOOL bResult = FALSE;
|
|
|
|
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());
|
|
return FALSE;
|
|
}
|
|
|
|
if (!InstallSysSetupInfDevices())
|
|
{
|
|
FatalError("InstallSysSetupInfDevices() failed!\n");
|
|
goto Exit;
|
|
}
|
|
|
|
if (!InstallSysSetupInfComponents())
|
|
{
|
|
FatalError("InstallSysSetupInfComponents() failed!\n");
|
|
goto Exit;
|
|
}
|
|
|
|
if (!IsConsoleBoot())
|
|
{
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
ShowStatusMessageThread,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
if (!EnableUserModePnpManager())
|
|
{
|
|
FatalError("EnableUserModePnpManager() failed!\n");
|
|
goto Exit;
|
|
}
|
|
|
|
if (CMP_WaitNoPendingInstallEvents(INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
FatalError("CMP_WaitNoPendingInstallEvents() failed!\n");
|
|
goto Exit;
|
|
}
|
|
|
|
bResult = TRUE;
|
|
|
|
Exit:
|
|
if (bResult == FALSE)
|
|
{
|
|
SetupCloseInfFile(hSysSetupInf);
|
|
}
|
|
|
|
if (hThread != NULL)
|
|
{
|
|
PostThreadMessage(GetThreadId(hThread), WM_QUIT, 0, 0);
|
|
WaitForSingleObject(hThread, INFINITE);
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
static
|
|
DWORD
|
|
InstallLiveCD(VOID)
|
|
{
|
|
STARTUPINFOW StartupInfo;
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
BOOL bRes;
|
|
|
|
PreprocessUnattend(FALSE);
|
|
if (!CommonInstall())
|
|
goto error;
|
|
|
|
/* Install the TCP/IP protocol driver */
|
|
bRes = InstallNetworkComponent(L"MS_TCPIP");
|
|
if (!bRes && GetLastError() != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
DPRINT("InstallNetworkComponent() failed with error 0x%lx\n", GetLastError());
|
|
}
|
|
else
|
|
{
|
|
/* Start the TCP/IP protocol driver */
|
|
SetupStartService(L"Tcpip", FALSE);
|
|
SetupStartService(L"Dhcp", FALSE);
|
|
SetupStartService(L"Dnscache", FALSE);
|
|
}
|
|
|
|
/* Register components */
|
|
_SEH2_TRY
|
|
{
|
|
if (!SetupInstallFromInfSectionW(NULL,
|
|
hSysSetupInf, L"RegistrationPhase2",
|
|
SPINST_ALL,
|
|
0, NULL, 0, NULL, NULL, NULL, NULL))
|
|
{
|
|
DPRINT1("SetupInstallFromInfSectionW failed!\n");
|
|
}
|
|
|
|
RegisterTypeLibraries(NULL, NULL, hSysSetupInf, L"TypeLibraries");
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
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);
|
|
if (!bRes)
|
|
goto error;
|
|
|
|
CloseHandle(ProcessInformation.hThread);
|
|
CloseHandle(ProcessInformation.hProcess);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
MessageBoxW(
|
|
NULL,
|
|
L"Failed to load LiveCD! You can shutdown your computer, or press ENTER to reboot.",
|
|
L"ReactOS LiveCD",
|
|
MB_OK);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
SetSetupType(DWORD dwSetupType)
|
|
{
|
|
DWORD dwError;
|
|
HKEY hKey;
|
|
|
|
dwError = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"SYSTEM\\Setup",
|
|
0,
|
|
KEY_SET_VALUE,
|
|
&hKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
dwError = RegSetValueExW(
|
|
hKey,
|
|
L"SetupType",
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&dwSetupType,
|
|
sizeof(DWORD));
|
|
RegCloseKey(hKey);
|
|
if (dwError != ERROR_SUCCESS)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD CALLBACK
|
|
HotkeyThread(LPVOID Parameter)
|
|
{
|
|
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 (GetMessageW(&msg, NULL, 0, 0))
|
|
{
|
|
if (msg.hwnd == NULL && msg.message == WM_HOTKEY && msg.wParam == hotkey)
|
|
{
|
|
WCHAR CmdLine[] = L"cmd.exe"; // CreateProcess can modify this buffer.
|
|
STARTUPINFOW si = { sizeof(si) };
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if (CreateProcessW(NULL,
|
|
CmdLine,
|
|
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;
|
|
HANDLE hHotkeyThread;
|
|
|
|
InitializeSetupActionLog(FALSE);
|
|
LogItem(NULL, L"Installing ReactOS");
|
|
|
|
CreateTempDir(L"TEMP");
|
|
CreateTempDir(L"TMP");
|
|
|
|
if (!InitializeProgramFilesDir())
|
|
{
|
|
FatalError("InitializeProgramFilesDir() failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!InitializeProfiles())
|
|
{
|
|
FatalError("InitializeProfiles() failed");
|
|
return 0;
|
|
}
|
|
|
|
InitializeDefaultUserLocale();
|
|
|
|
if (GetWindowsDirectoryW(szBuffer, ARRAYSIZE(szBuffer)))
|
|
{
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
|
|
0,
|
|
KEY_WRITE,
|
|
&hKey) == ERROR_SUCCESS)
|
|
{
|
|
RegSetValueExW(hKey,
|
|
L"PathName",
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)szBuffer,
|
|
(wcslen(szBuffer) + 1) * sizeof(WCHAR));
|
|
|
|
RegSetValueExW(hKey,
|
|
L"SystemRoot",
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)szBuffer,
|
|
(wcslen(szBuffer) + 1) * sizeof(WCHAR));
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
PathAddBackslash(szBuffer);
|
|
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);
|
|
|
|
PreprocessUnattend(TRUE);
|
|
if (!CommonInstall())
|
|
return 0;
|
|
|
|
InstallWizard();
|
|
|
|
InstallSecurity();
|
|
|
|
SetAutoAdminLogon();
|
|
|
|
SetupCloseInfFile(hSysSetupInf);
|
|
SetSetupType(0);
|
|
|
|
if (hHotkeyThread)
|
|
{
|
|
PostThreadMessage(GetThreadId(hHotkeyThread), WM_QUIT, 0, 0);
|
|
CloseHandle(hHotkeyThread);
|
|
}
|
|
|
|
LogItem(NULL, L"Installing ReactOS done");
|
|
TerminateSetupActionLog();
|
|
|
|
if (AdminInfo.Name != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, AdminInfo.Name);
|
|
|
|
if (AdminInfo.Domain != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, AdminInfo.Domain);
|
|
|
|
if (AdminInfo.Password != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, AdminInfo.Password);
|
|
|
|
/* Get shutdown privilege */
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
|
|
{
|
|
FatalError("OpenProcessToken() failed!");
|
|
return 0;
|
|
}
|
|
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)
|
|
{
|
|
FatalError("AdjustTokenPrivileges() failed!");
|
|
return 0;
|
|
}
|
|
|
|
ExitWindowsEx(EWX_REBOOT, 0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
DWORD WINAPI
|
|
SetupChangeFontSize(
|
|
IN HANDLE hWnd,
|
|
IN LPCWSTR lpszFontSize)
|
|
{
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
DWORD WINAPI
|
|
SetupChangeLocaleEx(HWND hWnd,
|
|
LCID Lcid,
|
|
LPCWSTR lpSrcRootPath,
|
|
char Unknown,
|
|
DWORD dwUnused1,
|
|
DWORD dwUnused2)
|
|
{
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD WINAPI
|
|
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;
|
|
}
|