[SYSSETUP][BOOTDATA] Unattended execute GuiUnattended:DetachedProgram , SetupParams:UserExecute (#9276)

This PR implements two of the three hooks that allows you to run custom
applications during 2nd-stage setup (the 3rd hook is `...\$OEM$\Cmdlines.txt`,
but that requires file copy operations, etc.)

- `DetachedProgram` is documented[^1] for NT4...2003; it is started before
  installation and `UserExecute` after.
- `UserExecute` is a great place to install VM Guest Additions, one reboot
  less and you have shared folders/clipboard on the first normal boot.

[^1]: https://web.archive.org/web/20090902145959/http://technet.microsoft.com/en-us/library/cc757642(WS.10).aspx
This commit is contained in:
Whindmar Saksit
2026-07-16 23:22:27 +02:00
committed by GitHub
parent a5f51269c8
commit 30b02d038e
5 changed files with 80 additions and 25 deletions
+2
View File
@@ -235,6 +235,7 @@ InstallSetupInfFile(
if (!IniCache) if (!IniCache)
return; return;
#if 0 /* If you need to put something in this section, do it after the merge with Unattend.inf */
IniSection = IniAddSection(IniCache, L"SetupParams"); IniSection = IniAddSection(IniCache, L"SetupParams");
if (IniSection) if (IniSection)
{ {
@@ -243,6 +244,7 @@ InstallSetupInfFile(
// L"\"%s\"", L"WinNt5.2"); // L"\"%s\"", L"WinNt5.2");
// IniAddKey(IniSection, L"Version", PathBuffer); // IniAddKey(IniSection, L"Version", PathBuffer);
} }
#endif
IniSection = IniAddSection(IniCache, L"Data"); IniSection = IniAddSection(IniCache, L"Data");
if (IniSection) if (IniSection)
+9
View File
@@ -59,6 +59,15 @@ LocaleID = 409
; 3 - ReactOS Nano Server (Reserved) ; 3 - ReactOS Nano Server (Reserved)
InstallationType = 0 InstallationType = 0
;[GuiUnattended]
; Run a program early in 2nd-stage setup.
;DetachedProgram=%SystemRoot%\system32\cmd.exe
;Arguments="/C echo.Early 2nd-stage setup...&choice>nul /N /T:Y,5"
;[SetupParams]
; Run a program at the end of 2nd-stage setup.
;UserExecute="cmd.exe /C echo.End of 2nd-stage setup...&choice>nul /N /T:Y,5"
; Enable this section to automatically launch programs after 3rd boot. ; Enable this section to automatically launch programs after 3rd boot.
;[GuiRunOnce] ;[GuiRunOnce]
;%SystemRoot%\system32\cmd.exe ;%SystemRoot%\system32\cmd.exe
+9 -1
View File
@@ -73,6 +73,10 @@ HRESULT
InstallOptionalComponents( InstallOptionalComponents(
_In_ PITEMSDATA pItemsData); _In_ PITEMSDATA pItemsData);
HRESULT
RunCommandAndWait(
_In_ PWCHAR Command);
/* install */ /* install */
BOOL BOOL
@@ -109,6 +113,10 @@ VOID
SetAutoAdminLogon(VOID); SetAutoAdminLogon(VOID);
/* wizard.c */ /* wizard.c */
VOID InstallWizard (VOID); VOID
InstallWizard(VOID);
VOID
GetSetupInfPath(PWSTR szPath, UINT cchMax);
/* EOF */ /* EOF */
+25 -2
View File
@@ -4,6 +4,7 @@
* PURPOSE: System setup * PURPOSE: System setup
* FILE: dll/win32/syssetup/install.c * FILE: dll/win32/syssetup/install.c
* PROGRAMER: Eric Kohl * PROGRAMER: Eric Kohl
* Whindmar Saksit <[email protected]>
*/ */
/* INCLUDES *****************************************************************/ /* INCLUDES *****************************************************************/
@@ -985,6 +986,26 @@ cleanup:
return bConsoleBoot; return bConsoleBoot;
} }
static VOID
ProcessDetachedProgram(
_In_ PCWSTR pszInf)
{
WCHAR szInfApp[MAX_PATH], szInfArg[MAX_PATH * 3];
WCHAR szCmd[_countof(szInfApp) + _countof(szInfArg)];
UINT cch;
if (!GetPrivateProfileStringW(L"GuiUnattended", L"DetachedProgram", L"", szInfApp, _countof(szInfApp), pszInf) || !*szInfApp)
return;
cch = ExpandEnvironmentStrings(szInfApp, szCmd, _countof(szCmd) - 1);
if (GetPrivateProfileStringW(L"GuiUnattended", L"Arguments", L"", szInfArg, _countof(szInfArg), pszInf) && cch)
{
szCmd[cch - 1] = L' ';
szCmd[cch] = UNICODE_NULL;
ExpandEnvironmentStrings(szInfArg, szCmd + cch, _countof(szCmd) - cch);
}
RunCommandAndWait(szCmd);
}
extern VOID extern VOID
EnableVisualTheme( EnableVisualTheme(
_In_opt_ HWND hwndParent, _In_opt_ HWND hwndParent,
@@ -1010,8 +1031,7 @@ PreprocessUnattend(
{ {
/* See also wizard.c!ProcessSetupInf() /* See also wizard.c!ProcessSetupInf()
* Retrieve the path of the setup INF */ * Retrieve the path of the setup INF */
GetSystemDirectoryW(szPath, _countof(szPath)); GetSetupInfPath(szPath, _countof(szPath));
wcscat(szPath, L"\\$winnt$.inf");
} }
else else
{ {
@@ -1038,6 +1058,9 @@ PreprocessUnattend(
/* Enable the chosen theme, or use the classic theme */ /* Enable the chosen theme, or use the classic theme */
EnableVisualTheme(NULL, bDefaultThemesOff ? NULL : szValue); EnableVisualTheme(NULL, bDefaultThemesOff ? NULL : szValue);
if (IsInstall)
ProcessDetachedProgram(szPath);
} }
static BOOL static BOOL
+35 -22
View File
@@ -53,6 +53,13 @@ typedef struct _TIMEZONE_ENTRY
extern void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow); extern void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
VOID
GetSetupInfPath(PWSTR szPath, UINT cchMax)
{
GetSystemDirectoryW(szPath, cchMax);
wcscat(szPath, L"\\$winnt$.inf");
}
static VOID static VOID
CenterWindow(HWND hWnd) CenterWindow(HWND hWnd)
{ {
@@ -2740,31 +2747,38 @@ ProcessPageDlgProc(HWND hwndDlg,
static VOID static VOID
SetInstallationCompleted(VOID) SetInstallationCompleted(IN BOOL Unattended)
{ {
HKEY hKey = 0; HKEY hKey = 0;
DWORD InProgress = 0; DWORD InProgress = 0;
DWORD InstallDate; DWORD InstallDate;
if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion",
L"SYSTEM\\Setup", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
0,
KEY_WRITE,
&hKey ) == ERROR_SUCCESS)
{
RegSetValueExW( hKey, L"SystemSetupInProgress", 0, REG_DWORD, (LPBYTE)&InProgress, sizeof(InProgress) );
RegCloseKey( hKey );
}
if (RegOpenKeyExW( HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows NT\\CurrentVersion",
0,
KEY_WRITE,
&hKey ) == ERROR_SUCCESS)
{ {
InstallDate = (DWORD)time(NULL); InstallDate = (DWORD)time(NULL);
RegSetValueExW( hKey, L"InstallDate", 0, REG_DWORD, (LPBYTE)&InstallDate, sizeof(InstallDate) ); RegSetValueExW(hKey, L"InstallDate", 0, REG_DWORD, (LPBYTE)&InstallDate, sizeof(InstallDate));
RegCloseKey( hKey ); RegCloseKey(hKey);
}
if (Unattended)
{
WCHAR szInf[MAX_PATH];
WCHAR szInfCmd[MAX_PATH * 4], szCmd[_countof(szInfCmd)];
GetSetupInfPath(szInf, _countof(szInf));
if (GetPrivateProfileStringW(L"SetupParams", L"UserExecute", L"", szInfCmd, _countof(szInfCmd), szInf) && *szInfCmd)
{
*szCmd = UNICODE_NULL;
ExpandEnvironmentStringsW(szInfCmd, szCmd, _countof(szInfCmd));
RunCommandAndWait(szCmd);
}
}
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
{
RegSetValueExW(hKey, L"SystemSetupInProgress", 0, REG_DWORD, (LPBYTE)&InProgress, sizeof(InProgress));
RegCloseKey(hKey);
} }
} }
@@ -2790,7 +2804,7 @@ FinishDlgProc(HWND hwndDlg,
if (SetupData->UnattendSetup) if (SetupData->UnattendSetup)
{ {
KillTimer(hwndDlg, 1); KillTimer(hwndDlg, 1);
SetInstallationCompleted(); SetInstallationCompleted(TRUE);
PostQuitMessage(0); PostQuitMessage(0);
} }
@@ -2802,7 +2816,7 @@ FinishDlgProc(HWND hwndDlg,
case WM_DESTROY: case WM_DESTROY:
{ {
SetInstallationCompleted(); SetInstallationCompleted(FALSE);
PostQuitMessage(0); PostQuitMessage(0);
return TRUE; return TRUE;
} }
@@ -3335,8 +3349,7 @@ ProcessSetupInf(
pSetupData->hSetupInf = INVALID_HANDLE_VALUE; pSetupData->hSetupInf = INVALID_HANDLE_VALUE;
/* Retrieve the path of the setup INF */ /* Retrieve the path of the setup INF */
GetSystemDirectoryW(szPath, _countof(szPath)); GetSetupInfPath(szPath, _countof(szPath));
wcscat(szPath, L"\\$winnt$.inf");
/* Open the setup INF */ /* Open the setup INF */
pSetupData->hSetupInf = SetupOpenInfFileW(szPath, pSetupData->hSetupInf = SetupOpenInfFileW(szPath,