You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1938 lines
51 KiB
1938 lines
51 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
winnt32.c
|
|
|
|
Abstract:
|
|
|
|
Stub loader for WinNT Setup program files.
|
|
|
|
Author:
|
|
|
|
|
|
Revisions:
|
|
|
|
Ovidiu Temereanca (ovidiut) 09-Dec-1998
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <winver.h>
|
|
#include <ntverp.h>
|
|
#include <setupbat.h>
|
|
#include "winnt32.h"
|
|
#include "winnt32p.h"
|
|
|
|
#define STRSAFE_NO_DEPRECATE
|
|
#include <strsafe.h>
|
|
|
|
#define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0]))
|
|
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
|
|
#if defined(_AMD64)
|
|
|
|
#include "amd64\download.h"
|
|
|
|
#else
|
|
|
|
#include "i386\download.h"
|
|
|
|
#endif
|
|
|
|
#define INTERNAL_WINNT32_DIR TEXT("\\WINNT32")
|
|
#define MAX_RETRY_INTERVAL_SECONDS 3600L
|
|
|
|
#endif
|
|
|
|
#define MAX_UPGCHK_ELAPSED_SECONDS (30 * 60)
|
|
#define S_CHKSUM_FILE TEXT("DOSNET.INF")
|
|
|
|
#define MAKEULONGLONG(low,high) ((ULONGLONG)(((DWORD)(low)) | ((ULONGLONG)((DWORD)(high))) << 32))
|
|
|
|
#define ALLOC_TEXT(chars) ((PTSTR)HeapAlloc (GetProcessHeap (), 0, ((chars) + 1) * sizeof (TCHAR)))
|
|
#define FREE(p) HeapFree (GetProcessHeap (), 0, p)
|
|
#define CHARS(string) (sizeof (string) / sizeof ((string)[0]) - 1)
|
|
|
|
#define pFindEOS(String) pFindChar(String, 0)
|
|
|
|
|
|
PTSTR
|
|
FindLastWack (
|
|
IN PTSTR String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
FindLastWack returns a pointer to the last backslash character
|
|
in the String
|
|
|
|
Arguments:
|
|
|
|
String - Specifies the string
|
|
|
|
Return Value:
|
|
|
|
The position of the last '\\' in the string or NULL if not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR p;
|
|
PTSTR LastChar = NULL;
|
|
|
|
for(p = String; *p; p = CharNext(p)) {
|
|
if(*p == TEXT('\\')) { // the char '\' is never a lead byte
|
|
LastChar = p;
|
|
}
|
|
}
|
|
|
|
return LastChar;
|
|
}
|
|
|
|
|
|
PTSTR
|
|
DuplicateText (
|
|
IN PCTSTR Text
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
DuplicateText allocates memory and then copies a source string into that memory.
|
|
Caller is responsible for freeing that memory.
|
|
|
|
Arguments:
|
|
|
|
Text - Specifies the source text
|
|
|
|
Return Value:
|
|
|
|
A pointer to the duplicate string; NULL if not enough memory.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR Dup;
|
|
|
|
Dup = ALLOC_TEXT(lstrlen (Text));
|
|
if (Dup) {
|
|
lstrcpy (Dup, Text);
|
|
}
|
|
|
|
return Dup;
|
|
}
|
|
|
|
|
|
PTSTR
|
|
pFindChar (
|
|
IN PTSTR String,
|
|
IN UINT Char
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pFindChar returns a pointer to the first occurence of the Char
|
|
in the String
|
|
|
|
Arguments:
|
|
|
|
String - Specifies the string
|
|
|
|
Char - Specifies the char to look for; can be null
|
|
|
|
Return Value:
|
|
|
|
A pointer to the first occurence of the char in this string
|
|
or NULL if not found
|
|
|
|
--*/
|
|
|
|
{
|
|
while (*String) {
|
|
|
|
if ((UINT)*String == Char) {
|
|
return String;
|
|
}
|
|
|
|
String = CharNext (String);
|
|
}
|
|
|
|
return Char ? NULL : String;
|
|
}
|
|
|
|
|
|
VOID
|
|
ConcatenatePaths (
|
|
IN PTSTR LeadingPath,
|
|
IN PCTSTR TrailingPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ConcatenatePaths concatenates the two given paths, taking care to
|
|
insert only one backslash between them. The resulting path is stored
|
|
in LeadingPath.
|
|
|
|
Arguments:
|
|
|
|
LeadingPath - Specifies the leading path
|
|
|
|
TrailingPath - Specifies the trailing path
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR p;
|
|
|
|
//
|
|
// check for "\" at the end of leading dir
|
|
//
|
|
p = FindLastWack (LeadingPath);
|
|
if (!p) {
|
|
p = pFindEOS (LeadingPath);
|
|
#pragma prefast(suppress:11, p is never NULL because 0 is ALWAYS found as the string terminator)
|
|
*p++ = TEXT('\\');
|
|
} else {
|
|
if (*(p + 1) == 0) {
|
|
p++;
|
|
} else {
|
|
p = pFindEOS (p);
|
|
*p++ = TEXT('\\');
|
|
}
|
|
}
|
|
//
|
|
// check for "\" at the beginning of trailing dir
|
|
//
|
|
if (*TrailingPath == TEXT('\\')) {
|
|
TrailingPath++;
|
|
}
|
|
lstrcpy (p, TrailingPath);
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetFileVersion (
|
|
IN PCTSTR FilePath,
|
|
OUT PDWORD FileVersionMS, OPTIONAL
|
|
OUT PDWORD FileVersionLS OPTIONAL
|
|
)
|
|
{
|
|
DWORD dwLength, dwTemp;
|
|
LPVOID lpData;
|
|
VS_FIXEDFILEINFO *VsInfo;
|
|
UINT DataLength;
|
|
BOOL b = FALSE;
|
|
|
|
if (GetFileAttributes (FilePath) != (DWORD)-1) {
|
|
if (dwLength = GetFileVersionInfoSize ((PTSTR)FilePath, &dwTemp)) {
|
|
if (lpData = LocalAlloc (LPTR, dwLength)) {
|
|
if (GetFileVersionInfo ((PTSTR)FilePath, 0, dwLength, lpData)) {
|
|
if (VerQueryValue (lpData, TEXT("\\"), &VsInfo, &DataLength)) {
|
|
if (FileVersionMS) {
|
|
*FileVersionMS = VsInfo->dwFileVersionMS;
|
|
}
|
|
if (FileVersionLS) {
|
|
*FileVersionLS = VsInfo->dwFileVersionLS;
|
|
}
|
|
b = TRUE;
|
|
}
|
|
}
|
|
LocalFree (lpData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
|
|
BOOL
|
|
pReRun (
|
|
IN PCTSTR StartDir,
|
|
IN PCTSTR WackExeName,
|
|
IN PCTSTR CmdLineArguments,
|
|
IN PCTSTR DefSourcesDir OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pReRun tries to launch a instance of this exe from a local drive,
|
|
specifing an additional command line parameter (/S:<Source_Dir>).
|
|
|
|
Arguments:
|
|
|
|
StartDir - Specifies the starting directory from where the instance will be launched
|
|
|
|
WackExeName - Specifies the file name only of the EXE to launch, preceded
|
|
by a backslash
|
|
|
|
CmdLineArguments - Specifies the command line arguments initially supplied
|
|
|
|
DefSourcesDir - Specifies the default directory containing instalation files
|
|
|
|
Return Value:
|
|
|
|
TRUE if the launch was successful
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR CmdLine;
|
|
INT Chars;
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION pi;
|
|
BOOL b = FALSE;
|
|
DWORD rc;
|
|
|
|
Chars = lstrlen (StartDir) + lstrlen (WackExeName) + CHARS(" ") + lstrlen (CmdLineArguments);
|
|
if (DefSourcesDir) {
|
|
Chars += CHARS(" /S:") + lstrlen (DefSourcesDir);
|
|
}
|
|
|
|
CmdLine = ALLOC_TEXT(Chars);
|
|
if (!CmdLine) {
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
lstrcpy (CmdLine, StartDir);
|
|
lstrcat (CmdLine, WackExeName);
|
|
lstrcat (CmdLine, TEXT(" "));
|
|
lstrcat (CmdLine, CmdLineArguments);
|
|
if (DefSourcesDir) {
|
|
lstrcat (CmdLine, TEXT(" /S:"));
|
|
lstrcat (CmdLine, DefSourcesDir);
|
|
}
|
|
|
|
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
|
|
b = CreateProcess (
|
|
NULL,
|
|
CmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NORMAL_PRIORITY_CLASS,
|
|
NULL,
|
|
StartDir,
|
|
&StartupInfo,
|
|
&pi
|
|
);
|
|
|
|
rc = GetLastError ();
|
|
|
|
FREE (CmdLine);
|
|
|
|
SetLastError (rc);
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pCleanup (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pCleanup deletes all locally installed files and marks current running
|
|
instance for deletion the next time system will reboot.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if the operation completed successfully; the machine will need to
|
|
reboot before actual complete delete will take place.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR RunningInstancePath[MAX_PATH];
|
|
TCHAR Buffer[MAX_PATH];
|
|
BOOL b;
|
|
DWORD StartingTime;
|
|
PCTSTR p;
|
|
|
|
if (!GetModuleFileName (NULL, RunningInstancePath, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// wait until WINNT32\WINNT32.EXE file can be deleted
|
|
// or the retry interval of time elapses
|
|
//
|
|
if (!GetWindowsDirectory (Buffer, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCbCat(Buffer, sizeof(Buffer), INTERNAL_WINNT32_DIR))) {
|
|
return FALSE;
|
|
}
|
|
|
|
p = FindLastWack ((PTSTR)RunningInstancePath);
|
|
if (!p) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCbCat (Buffer, sizeof(Buffer), p))) {
|
|
return FALSE;
|
|
}
|
|
|
|
StartingTime = GetTickCount ();
|
|
while (GetFileAttributes (Buffer) != (DWORD)-1) {
|
|
//
|
|
// try to delete it
|
|
//
|
|
if (DeleteNode (Buffer)) {
|
|
break;
|
|
}
|
|
//
|
|
// give up if time elapses
|
|
//
|
|
if (GetTickCount () - StartingTime > 1000L * MAX_RETRY_INTERVAL_SECONDS) {
|
|
break;
|
|
}
|
|
//
|
|
// nothing useful to do; let the other processes run
|
|
//
|
|
Sleep (0);
|
|
}
|
|
|
|
//
|
|
// wait until WINNT32\SETUPLOG.EXE file can be deleted
|
|
// or the retry interval of time elapses
|
|
//
|
|
if (!GetWindowsDirectory (Buffer, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringCbCat (Buffer, sizeof(Buffer), INTERNAL_WINNT32_DIR);
|
|
|
|
if (FAILED(StringCbCat (Buffer, sizeof(Buffer), TEXT("\\SETUPLOG.EXE")))) {
|
|
return FALSE;
|
|
}
|
|
|
|
StartingTime = GetTickCount ();
|
|
while (GetFileAttributes (Buffer) != (DWORD)-1) {
|
|
if (DeleteNode (Buffer)) {
|
|
break;
|
|
}
|
|
if (GetTickCount () - StartingTime > 1000L * MAX_RETRY_INTERVAL_SECONDS) {
|
|
break;
|
|
}
|
|
Sleep (0);
|
|
}
|
|
|
|
if (!GetWindowsDirectory (Buffer, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCbCat (Buffer, sizeof(Buffer), INTERNAL_WINNT32_DIR))) {
|
|
return FALSE;
|
|
}
|
|
|
|
b = DeleteNode (Buffer);
|
|
|
|
if (!GetWindowsDirectory (Buffer, MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCbCat (Buffer, sizeof(Buffer), TEXT("\\WININIT.INI")))) {
|
|
return FALSE;
|
|
}
|
|
|
|
return
|
|
WritePrivateProfileString (TEXT("rename"), TEXT("NUL"), RunningInstancePath, Buffer) && b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pShouldDownloadToLocalDisk (
|
|
IN PTSTR Path
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pShouldDownloadToLocalDisk returns TRUE if winnt32 files should be
|
|
downloaded to a local disk first (like in the case of sources on
|
|
a remote disk or on a CD)
|
|
|
|
Arguments:
|
|
|
|
Path - Specifies the path
|
|
|
|
Return Value:
|
|
|
|
TRUE if the specified path is on an untrusted media
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR ch;
|
|
BOOL Remote = TRUE;
|
|
UINT type;
|
|
|
|
if (Path[1] == TEXT(':') && Path[2] == TEXT('\\')) {
|
|
ch = Path[3];
|
|
Path[3] = 0;
|
|
type = GetDriveType (Path);
|
|
Remote = (type == DRIVE_REMOTE) || (type == DRIVE_CDROM);
|
|
Path[3] = ch;
|
|
}
|
|
return Remote;
|
|
}
|
|
|
|
|
|
VOID
|
|
pCenterWindowOnDesktop (
|
|
HWND WndToCenter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Centers a dialog relative to the 'work area' of the desktop.
|
|
|
|
Arguments:
|
|
|
|
WndToCenter - window handle of dialog to center
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RECT rcFrame, rcWindow;
|
|
LONG x, y, w, h;
|
|
POINT point;
|
|
HWND Desktop = GetDesktopWindow ();
|
|
|
|
point.x = point.y = 0;
|
|
ClientToScreen(Desktop, &point);
|
|
GetWindowRect(WndToCenter, &rcWindow);
|
|
GetClientRect(Desktop, &rcFrame);
|
|
|
|
w = rcWindow.right - rcWindow.left + 1;
|
|
h = rcWindow.bottom - rcWindow.top + 1;
|
|
x = point.x + ((rcFrame.right - rcFrame.left + 1 - w) / 2);
|
|
y = point.y + ((rcFrame.bottom - rcFrame.top + 1 - h) / 2);
|
|
|
|
//
|
|
// Get the work area for the current desktop (i.e., the area that
|
|
// the tray doesn't occupy).
|
|
//
|
|
if(!SystemParametersInfo (SPI_GETWORKAREA, 0, (PVOID)&rcFrame, 0)) {
|
|
//
|
|
// For some reason SPI failed, so use the full screen.
|
|
//
|
|
rcFrame.top = rcFrame.left = 0;
|
|
rcFrame.right = GetSystemMetrics(SM_CXSCREEN);
|
|
rcFrame.bottom = GetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
|
|
if(x + w > rcFrame.right) {
|
|
x = rcFrame.right - w;
|
|
} else if(x < rcFrame.left) {
|
|
x = rcFrame.left;
|
|
}
|
|
if(y + h > rcFrame.bottom) {
|
|
y = rcFrame.bottom - h;
|
|
} else if(y < rcFrame.top) {
|
|
y = rcFrame.top;
|
|
}
|
|
|
|
MoveWindow(WndToCenter, x, y, w, h, FALSE);
|
|
}
|
|
|
|
|
|
BOOL CALLBACK DlgProc (
|
|
HWND Dlg,
|
|
UINT Msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback procedure for the dialog displayed while
|
|
components are copied from the network
|
|
|
|
Arguments:
|
|
|
|
Dlg - Specifies the dialog window handle
|
|
|
|
Msg - Specifies the message
|
|
|
|
wParam - Specifies the first param
|
|
|
|
lParam - Specifies the second param
|
|
|
|
Return Value:
|
|
|
|
Depends on the specific message.
|
|
|
|
--*/
|
|
|
|
{
|
|
static HANDLE Bitmap = NULL;
|
|
static HCURSOR Cursor = NULL;
|
|
|
|
RECT rect;
|
|
HWND Text;
|
|
BITMAP bm;
|
|
INT i;
|
|
|
|
switch (Msg) {
|
|
|
|
case WM_INITDIALOG:
|
|
Cursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
|
|
ShowCursor (TRUE);
|
|
Bitmap = LoadBitmap (GetModuleHandle (NULL), MAKEINTRESOURCE(IDB_INIT_WIN2000));
|
|
if (Bitmap) {
|
|
if (GetObject (Bitmap, sizeof (bm), &bm)) {
|
|
GetClientRect (Dlg, &rect);
|
|
rect.right = bm.bmWidth;
|
|
AdjustWindowRect (&rect, GetWindowLong (Dlg, GWL_STYLE), FALSE);
|
|
SetWindowPos (
|
|
Dlg,
|
|
NULL,
|
|
0,
|
|
0,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
SendDlgItemMessage(Dlg, IDC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)Bitmap);
|
|
}
|
|
GetClientRect (Dlg, &rect);
|
|
i = rect.right - rect.left;
|
|
Text = GetDlgItem (Dlg, IDC_TEXT);
|
|
if (GetWindowRect (Text, &rect)) {
|
|
i = (i - (rect.right - rect.left)) / 2;
|
|
ScreenToClient (Dlg, (LPPOINT)&rect);
|
|
SetWindowPos (
|
|
Text,
|
|
NULL,
|
|
i,
|
|
rect.top,
|
|
0,
|
|
0,
|
|
SWP_NOSIZE | SWP_NOZORDER);
|
|
}
|
|
pCenterWindowOnDesktop (Dlg);
|
|
return TRUE;
|
|
|
|
case WM_DESTROY:
|
|
ShowCursor (FALSE);
|
|
if (Cursor) {
|
|
SetCursor (Cursor);
|
|
Cursor = NULL;
|
|
}
|
|
if (Bitmap) {
|
|
DeleteObject (Bitmap);
|
|
Bitmap = NULL;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
INT
|
|
pStringICompareCharCount (
|
|
IN PCTSTR String1,
|
|
IN PCTSTR String2,
|
|
IN DWORD CharCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine behaves like _tcsnicmp.
|
|
|
|
Arguments:
|
|
|
|
String1 - Specifies the first string
|
|
|
|
String2 - Specifies the second string
|
|
|
|
CharCount - Specifies the number of chars to compare at most
|
|
|
|
Return Value:
|
|
|
|
0 if strings are equal; -1 if first string is lesser; 1 if first is greater
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR ch1, ch2;
|
|
|
|
if (!CharCount) {
|
|
return 0;
|
|
}
|
|
|
|
while (*String1) {
|
|
ch1 = (TCHAR)CharUpper ((LPTSTR)*String1);
|
|
ch2 = (TCHAR)CharUpper ((LPTSTR)*String2);
|
|
if (ch1 - ch2) {
|
|
return ch1 - ch2;
|
|
}
|
|
|
|
CharCount--;
|
|
if (!CharCount) {
|
|
return 0;
|
|
}
|
|
|
|
String1 = CharNext (String1);
|
|
String2 = CharNext (String2);
|
|
}
|
|
|
|
return -(*String2);
|
|
}
|
|
|
|
VOID
|
|
pParseCmdLine (
|
|
IN PTSTR CmdStart,
|
|
OUT PTSTR* ArgValues,
|
|
OUT PTSTR pStr,
|
|
OUT INT *NumArgs,
|
|
OUT INT *NumBytes
|
|
)
|
|
|
|
/*
|
|
|
|
Routine Description:
|
|
|
|
pParseCmdLine parses the command line and sets up the ArgValues array.
|
|
On entry, CmdStart should point to the command line,
|
|
ArgValues should point to memory for the ArgValues array,
|
|
pStr points to memory to place the text of the arguments.
|
|
If these are NULL, then no storing (only counting)
|
|
is done. On exit, *NumArgs has the number of
|
|
arguments (plus one for a final NULL argument),
|
|
and *NumBytes has the number of bytes used in the buffer
|
|
pointed to by args.
|
|
|
|
Arguments:
|
|
|
|
CmdStart - Specifies the command line having the form:
|
|
<progname><nul><args><nul>
|
|
ArgValues - Receives the arguments array;
|
|
NULL means don't build array
|
|
pStr - Receives the argument text; NULL means don't store text
|
|
|
|
NumArgs - Receives the number of ArgValues entries created
|
|
|
|
NumBytes - Receives the number of bytes used in buffer
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
*/
|
|
|
|
{
|
|
PTSTR p;
|
|
TCHAR c;
|
|
INT inquote; /* 1 = inside quotes */
|
|
INT copychar; /* 1 = copy char to *args */
|
|
WORD numslash; /* num of backslashes seen */
|
|
|
|
*NumBytes = 0;
|
|
*NumArgs = 1; /* the program name at least */
|
|
|
|
/* first scan the program name, copy it, and count the bytes */
|
|
p = CmdStart;
|
|
if (ArgValues)
|
|
*ArgValues++ = pStr;
|
|
|
|
/* A quoted program name is handled here. The handling is much
|
|
simpler than for other arguments. Basically, whatever lies
|
|
between the leading double-quote and next one, or a terminal null
|
|
character is simply accepted. Fancier handling is not required
|
|
because the program name must be a legal NTFS/HPFS file name.
|
|
Note that the double-quote characters are not copied, nor do they
|
|
contribute to NumBytes. */
|
|
if (*p == TEXT('\"'))
|
|
{
|
|
/* scan from just past the first double-quote through the next
|
|
double-quote, or up to a null, whichever comes first */
|
|
while ((*(++p) != TEXT('\"')) && (*p != TEXT('\0')))
|
|
{
|
|
*NumBytes += sizeof(TCHAR);
|
|
if (pStr)
|
|
*pStr++ = *p;
|
|
}
|
|
/* append the terminating null */
|
|
*NumBytes += sizeof(TCHAR);
|
|
if (pStr)
|
|
*pStr++ = TEXT('\0');
|
|
|
|
/* if we stopped on a double-quote (usual case), skip over it */
|
|
if (*p == TEXT('\"'))
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
/* Not a quoted program name */
|
|
do {
|
|
*NumBytes += sizeof(TCHAR);
|
|
if (pStr)
|
|
*pStr++ = *p;
|
|
|
|
c = *p++;
|
|
|
|
} while (c > TEXT(' '));
|
|
|
|
if (c == TEXT('\0'))
|
|
{
|
|
p--;
|
|
}
|
|
else
|
|
{
|
|
if (pStr)
|
|
*(pStr - 1) = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
inquote = 0;
|
|
|
|
/* loop on each argument */
|
|
for ( ; ; )
|
|
{
|
|
if (*p)
|
|
{
|
|
while (*p == TEXT(' ') || *p == TEXT('\t'))
|
|
++p;
|
|
}
|
|
|
|
if (*p == TEXT('\0'))
|
|
break; /* end of args */
|
|
|
|
/* scan an argument */
|
|
if (ArgValues)
|
|
*ArgValues++ = pStr; /* store ptr to arg */
|
|
++*NumArgs;
|
|
|
|
/* loop through scanning one argument */
|
|
for ( ; ; )
|
|
{
|
|
copychar = 1;
|
|
/* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
|
|
2N+1 backslashes + " ==> N backslashes + literal "
|
|
N backslashes ==> N backslashes */
|
|
numslash = 0;
|
|
while (*p == TEXT('\\'))
|
|
{
|
|
/* count number of backslashes for use below */
|
|
++p;
|
|
++numslash;
|
|
}
|
|
if (*p == TEXT('\"'))
|
|
{
|
|
/* if 2N backslashes before, start/end quote, otherwise
|
|
copy literally */
|
|
if (numslash % 2 == 0)
|
|
{
|
|
if (inquote)
|
|
if (p[1] == TEXT('\"'))
|
|
p++; /* Double quote inside quoted string */
|
|
else /* skip first quote char and copy second */
|
|
copychar = 0;
|
|
else
|
|
copychar = 0; /* don't copy quote */
|
|
|
|
inquote = !inquote;
|
|
}
|
|
numslash /= 2; /* divide numslash by two */
|
|
}
|
|
|
|
/* copy slashes */
|
|
while (numslash--)
|
|
{
|
|
if (pStr)
|
|
*pStr++ = TEXT('\\');
|
|
*NumBytes += sizeof(TCHAR);
|
|
}
|
|
|
|
/* if at end of arg, break loop */
|
|
if (*p == TEXT('\0') || (!inquote && (*p == TEXT(' ') || *p == TEXT('\t'))))
|
|
break;
|
|
|
|
/* copy character into argument */
|
|
if (copychar)
|
|
{
|
|
if (pStr)
|
|
*pStr++ = *p;
|
|
*NumBytes += sizeof(TCHAR);
|
|
}
|
|
++p;
|
|
}
|
|
|
|
/* null-terminate the argument */
|
|
|
|
if (pStr)
|
|
*pStr++ = TEXT('\0'); /* terminate string */
|
|
*NumBytes += sizeof(TCHAR);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PTSTR*
|
|
pCommandLineToArgv (
|
|
OUT INT* NumArgs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pCommandLineToArgv tokens the command line in an array of arguments
|
|
created on the heap. The number of entries in this array of args is
|
|
stored in *NumArgs. The caller is responsible for freeing this array.
|
|
|
|
Arguments:
|
|
|
|
NumArgs - Receives the number of arguments in the array that is returned
|
|
|
|
Return Value:
|
|
|
|
An array of pointer to individual arguments specified on the command line
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR CommandLine;
|
|
TCHAR ModuleName[MAX_PATH];
|
|
PTSTR Start;
|
|
INT Size;
|
|
PTSTR* Args;
|
|
|
|
CommandLine = GetCommandLine();
|
|
GetModuleFileName (NULL, ModuleName, MAX_PATH);
|
|
|
|
//
|
|
// If there's no command line at all (won't happen from cmd.exe, but
|
|
// possibly another program), then we use pgmname as the command line
|
|
// to parse, so that ArgValues[0] is initialized to the program name
|
|
//
|
|
Start = *CommandLine ? CommandLine : ModuleName;
|
|
|
|
//
|
|
// Find out how much space is needed to store args,
|
|
// allocate space for ArgValues[] vector and strings,
|
|
// and store args and ArgValues ptrs in block we allocate
|
|
//
|
|
|
|
pParseCmdLine (Start, NULL, NULL, NumArgs, &Size);
|
|
|
|
Args = (PTSTR*) LocalAlloc (
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
((*NumArgs + 1) * sizeof(PTSTR)) + Size
|
|
);
|
|
if (!Args) {
|
|
return NULL;
|
|
}
|
|
|
|
pParseCmdLine (Start, Args, (PTSTR)(Args + *NumArgs), NumArgs, &Size);
|
|
|
|
return Args;
|
|
}
|
|
|
|
|
|
VOID
|
|
GetCmdLineArgs (
|
|
IN PCTSTR CommandLine,
|
|
OUT BOOL* Cleanup,
|
|
OUT BOOL* NoDownload,
|
|
OUT PCTSTR* UnattendPrefix,
|
|
OUT PCTSTR* UnattendFileName,
|
|
OUT BOOL* DisableDynamicUpdates,
|
|
OUT PCTSTR* DynamicUpdatesShare,
|
|
OUT PCTSTR* RestartAnswerFile,
|
|
OUT BOOL* LocalWinnt32,
|
|
OUT BOOL* CheckUpgradeOnly,
|
|
OUT PTSTR RemainingArgs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
GetCmdLineArgs retrieves download-specific commands
|
|
from the specified command line and stores them in supplied buffers.
|
|
|
|
Arguments:
|
|
|
|
CommandLine - Specifies the command line to interpret
|
|
|
|
Cleanup - Receives a bool indicating if a cleanup option
|
|
was specified
|
|
|
|
NoDownload - Receives a bool indicating if a no-download option
|
|
was specified
|
|
|
|
UnattendPrefix - Receives a pointer to the unattend command-line option, as
|
|
specified by the user (including the terminating column)
|
|
or NULL if not specified; caller is responsible
|
|
for freeing the memory
|
|
|
|
UnattendFileName - Receives a pointer to the unattended file name
|
|
or NULL if not specified; caller is responsible
|
|
for freeing the memory
|
|
|
|
DisableDynamicUpdates - Receives a bool set if DU is to be disabled
|
|
|
|
DynamicUpdatesShare - Receives a pointer to the dynamic updates share;
|
|
caller is responsible for freeing the memory
|
|
|
|
RestartAnswerFile - Receives a pointer to the /Restart: answer file
|
|
|
|
LocalWinnt32 - Receives a bool indicating if a winnt32 runs from a local disk
|
|
(after an automatic download)
|
|
|
|
CheckUpgradeOnly - Receives a bool indicating if winnt32 runs in CheckUpgradeOnly mode
|
|
|
|
RemainingArgs - Receives all remaining arguments not related
|
|
to the download operation
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
INT ArgCount;
|
|
PTSTR *ArgValues, *CrtArg;
|
|
PTSTR CurrentArg, p;
|
|
BOOL PassOn;
|
|
|
|
*Cleanup = FALSE;
|
|
*NoDownload = FALSE;
|
|
*UnattendPrefix = NULL;
|
|
*UnattendFileName = NULL;
|
|
*DisableDynamicUpdates = FALSE;
|
|
*DynamicUpdatesShare = NULL;
|
|
*RemainingArgs = 0;
|
|
*LocalWinnt32 = FALSE;
|
|
*CheckUpgradeOnly = FALSE;
|
|
*RestartAnswerFile = NULL;
|
|
|
|
CrtArg = ArgValues = pCommandLineToArgv (&ArgCount);
|
|
|
|
//
|
|
// Skip program name. We should always get back ArgCount as at least 1,
|
|
// but be robust anyway.
|
|
//
|
|
if (ArgCount) {
|
|
ArgCount--;
|
|
CrtArg++;
|
|
}
|
|
|
|
while (ArgCount--) {
|
|
CurrentArg = *CrtArg++;
|
|
PassOn = TRUE;
|
|
|
|
if ((*CurrentArg == TEXT('/')) || (*CurrentArg == TEXT('-'))) {
|
|
|
|
if (lstrcmpi (CurrentArg + 1, TEXT("LOCAL")) == 0) {
|
|
*LocalWinnt32 = TRUE;
|
|
PassOn = FALSE;
|
|
} else if (lstrcmpi (CurrentArg + 1, TEXT("CLEANUP")) == 0) {
|
|
*Cleanup = TRUE;
|
|
PassOn = FALSE;
|
|
} else if (lstrcmpi (CurrentArg + 1, TEXT("NODOWNLOAD")) == 0) {
|
|
*NoDownload = TRUE;
|
|
PassOn = FALSE;
|
|
} else if (lstrcmpi (CurrentArg + 1, TEXT("CHECKUPGRADEONLY")) == 0) {
|
|
*CheckUpgradeOnly = TRUE;
|
|
} else if (pStringICompareCharCount (CurrentArg + 1, TEXT("UNATTEND"), 8) == 0) {
|
|
p = pFindChar (CurrentArg + 1 + 8, TEXT(':'));
|
|
if (p && *(p + 1)) {
|
|
p++;
|
|
*UnattendFileName = DuplicateText (p);
|
|
*p = 0;
|
|
*UnattendPrefix = DuplicateText (CurrentArg);
|
|
PassOn = FALSE;
|
|
}
|
|
} else if (pStringICompareCharCount (CurrentArg + 1, TEXT("UNATTENDED"), 10) == 0) {
|
|
p = pFindChar (CurrentArg + 1 + 10, TEXT(':'));
|
|
if (p && *(p + 1)) {
|
|
p++;
|
|
*UnattendFileName = DuplicateText (p);
|
|
*p = 0;
|
|
*UnattendPrefix = DuplicateText (CurrentArg);
|
|
PassOn = FALSE;
|
|
}
|
|
} else if (lstrcmpi (CurrentArg + 1, WINNT_U_DYNAMICUPDATESDISABLE) == 0) {
|
|
*DisableDynamicUpdates = TRUE;
|
|
} else if (pStringICompareCharCount (CurrentArg + 1, WINNT_U_DYNAMICUPDATESHARE, sizeof (WINNT_U_DYNAMICUPDATESHARE_A) - 1) == 0 &&
|
|
CurrentArg[sizeof (WINNT_U_DYNAMICUPDATESHARE_A)] == TEXT(':')) {
|
|
*DynamicUpdatesShare = DuplicateText (CurrentArg + 1 + sizeof (WINNT_U_DYNAMICUPDATESHARE_A));
|
|
} else if (pStringICompareCharCount (CurrentArg + 1, TEXT("RESTART:"), 8) == 0) {
|
|
*RestartAnswerFile = DuplicateText (CurrentArg + 1 + 8);
|
|
}
|
|
}
|
|
|
|
if (PassOn) {
|
|
if (*RemainingArgs) {
|
|
lstrcat(RemainingArgs, TEXT(" "));
|
|
}
|
|
lstrcat(RemainingArgs, CurrentArg);
|
|
}
|
|
}
|
|
|
|
LocalFree ((HLOCAL) ArgValues);
|
|
}
|
|
|
|
BOOL
|
|
DoesDirExist (
|
|
IN PCTSTR Path
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
TCHAR test[MAX_PATH];
|
|
HANDLE h;
|
|
BOOL b = FALSE;
|
|
HRESULT hr;
|
|
|
|
if (Path) {
|
|
|
|
if (FAILED(StringCbCopy(test, sizeof(test), Path))) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FAILED(StringCbCat(test, sizeof(test), TEXT("\\*")))) {
|
|
return FALSE;
|
|
}
|
|
|
|
h = FindFirstFile (test, &fd);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
FindClose (h);
|
|
b = TRUE;
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
ULONGLONG
|
|
SystemTimeToFileTime64 (
|
|
IN PSYSTEMTIME SystemTime
|
|
)
|
|
{
|
|
FILETIME ft;
|
|
ULARGE_INTEGER result;
|
|
|
|
SystemTimeToFileTime (SystemTime, &ft);
|
|
result.LowPart = ft.dwLowDateTime;
|
|
result.HighPart = ft.dwHighDateTime;
|
|
|
|
return result.QuadPart;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pComputeChecksum (
|
|
IN PCTSTR FileName,
|
|
OUT PDWORD Chksum
|
|
)
|
|
{
|
|
DWORD chksum, size, dwords, bytes;
|
|
HANDLE hFile, hMap = NULL;
|
|
PVOID viewBase = NULL;
|
|
PDWORD base, limit;
|
|
PBYTE base2;
|
|
DWORD rc;
|
|
BOOL b = FALSE;
|
|
|
|
hFile = CreateFile(
|
|
FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if(hFile == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
__try {
|
|
size = GetFileSize (hFile, NULL);
|
|
if (size == (DWORD)-1) {
|
|
__leave;
|
|
}
|
|
hMap = CreateFileMapping (
|
|
hFile,
|
|
NULL,
|
|
PAGE_READONLY,
|
|
0,
|
|
size,
|
|
NULL
|
|
);
|
|
if (!hMap) {
|
|
__leave;
|
|
}
|
|
viewBase = MapViewOfFile (hMap, FILE_MAP_READ, 0, 0, size);
|
|
if (!viewBase) {
|
|
__leave;
|
|
}
|
|
|
|
dwords = size / sizeof (DWORD);
|
|
base = (PDWORD)viewBase;
|
|
limit = base + dwords;
|
|
chksum = 0;
|
|
while (base < limit) {
|
|
chksum += *base;
|
|
base++;
|
|
}
|
|
bytes = size % sizeof (DWORD);
|
|
base2 = (PBYTE)base;
|
|
while (bytes) {
|
|
chksum += *base2;
|
|
base2++;
|
|
bytes--;
|
|
}
|
|
b = TRUE;
|
|
}
|
|
__finally {
|
|
if (!b) {
|
|
rc = GetLastError ();
|
|
}
|
|
if (viewBase) {
|
|
UnmapViewOfFile (viewBase);
|
|
}
|
|
if (hMap) {
|
|
CloseHandle (hMap);
|
|
}
|
|
CloseHandle (hFile);
|
|
if (!b) {
|
|
SetLastError (rc);
|
|
}
|
|
}
|
|
|
|
if (b) {
|
|
*Chksum = chksum;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pGetFiletimeStamps (
|
|
IN PCTSTR FileName,
|
|
OUT PFILETIME CreationTime,
|
|
OUT PFILETIME LastWriteTime
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
HANDLE h;
|
|
|
|
h = FindFirstFile (FileName, &fd);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
FindClose (h);
|
|
*CreationTime = fd.ftCreationTime;
|
|
*LastWriteTime = fd.ftLastWriteTime;
|
|
return TRUE;
|
|
}
|
|
|
|
PTSTR
|
|
pGetRecentDUShare (
|
|
IN DWORD MaxElapsedSeconds
|
|
)
|
|
{
|
|
SYSTEMTIME lastDownload, currentTime;
|
|
ULONGLONG lastDownloadIn100Ns, currentTimeIn100Ns;
|
|
ULONGLONG difference;
|
|
DWORD rc, size, type;
|
|
HKEY key = NULL;
|
|
BOOL b = FALSE;
|
|
PTSTR duShare = NULL;
|
|
TCHAR filePath[MAX_PATH];
|
|
PTSTR p;
|
|
FILETIME ftCreationTime;
|
|
FILETIME ftLastWriteTime;
|
|
ULONGLONG data[2], storedData[2];
|
|
DWORD chksum, storedChksum;
|
|
|
|
if (!GetModuleFileName (NULL, filePath, MAX_PATH)) {
|
|
return NULL;
|
|
}
|
|
p = FindLastWack (filePath);
|
|
if (!p) {
|
|
return NULL;
|
|
}
|
|
|
|
p++; //now, p points after the wack.
|
|
|
|
//Note that p cannot be greater than (filePath+MAX_PATH), since
|
|
//we used GetModuleFileName() with MAX_PATH to put a string in the filePath buffer,
|
|
//and FindLastWack() cannot go beyond the end of the string buffer.
|
|
if (FAILED(StringCchCopy(p, (filePath+ARRAYSIZE(filePath)) - p, S_CHKSUM_FILE))) {
|
|
return NULL;
|
|
}
|
|
|
|
rc = RegOpenKeyEx (
|
|
HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\Winnt32\\5.1\\DUShare"),
|
|
0,
|
|
KEY_READ,
|
|
&key
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
size = sizeof (lastDownload);
|
|
rc = RegQueryValueEx (
|
|
key,
|
|
TEXT("LastDownloadTime"),
|
|
NULL,
|
|
&type,
|
|
(PBYTE) (&lastDownload),
|
|
&size
|
|
);
|
|
}
|
|
|
|
if (rc == ERROR_SUCCESS && type == REG_BINARY && size == sizeof (lastDownload)) {
|
|
//
|
|
// Compare current time to report time
|
|
//
|
|
|
|
GetSystemTime (¤tTime);
|
|
|
|
lastDownloadIn100Ns = SystemTimeToFileTime64 (&lastDownload);
|
|
currentTimeIn100Ns = SystemTimeToFileTime64 (¤tTime);
|
|
|
|
if (currentTimeIn100Ns > lastDownloadIn100Ns) {
|
|
//
|
|
// Compute difference in seconds
|
|
//
|
|
difference = currentTimeIn100Ns - lastDownloadIn100Ns;
|
|
difference /= (10 * 1000 * 1000);
|
|
|
|
if (difference < MaxElapsedSeconds) {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (b) {
|
|
rc = RegQueryValueEx (
|
|
key,
|
|
TEXT(""),
|
|
NULL,
|
|
&type,
|
|
NULL,
|
|
&size
|
|
);
|
|
if (rc == ERROR_SUCCESS && type == REG_SZ && size > 0) {
|
|
duShare = ALLOC_TEXT (size / sizeof (TCHAR));
|
|
if (duShare) {
|
|
rc = RegQueryValueEx (
|
|
key,
|
|
TEXT(""),
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)duShare,
|
|
&size
|
|
);
|
|
if (rc != ERROR_SUCCESS || !DoesDirExist (duShare)) {
|
|
FREE (duShare);
|
|
duShare = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (duShare) {
|
|
b = FALSE;
|
|
if (pGetFiletimeStamps (filePath, &ftCreationTime, &ftLastWriteTime)) {
|
|
rc = RegQueryValueEx (
|
|
key,
|
|
TEXT("TimeStamp"),
|
|
0,
|
|
&type,
|
|
(LPBYTE)storedData,
|
|
&size
|
|
);
|
|
if (rc == ERROR_SUCCESS && type == REG_BINARY) {
|
|
data[0] = ((ULONGLONG)ftCreationTime.dwHighDateTime << 32) | (ULONGLONG)ftCreationTime.dwLowDateTime;
|
|
data[1] = ((ULONGLONG)ftLastWriteTime.dwHighDateTime << 32 ) | (ULONGLONG)ftLastWriteTime.dwLowDateTime;
|
|
if (data[0] == storedData[0] && data[1] == storedData[1]) {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (b) {
|
|
b = FALSE;
|
|
if (pComputeChecksum (filePath, &chksum)) {
|
|
rc = RegQueryValueEx (
|
|
key,
|
|
TEXT("Checksum"),
|
|
NULL,
|
|
&type,
|
|
(LPBYTE)&storedChksum,
|
|
&size
|
|
);
|
|
if (rc == ERROR_SUCCESS && type == REG_DWORD && storedChksum == chksum) {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (!b) {
|
|
FREE (duShare);
|
|
duShare = NULL;
|
|
}
|
|
}
|
|
|
|
if (key) {
|
|
RegCloseKey (key);
|
|
}
|
|
|
|
return duShare;
|
|
}
|
|
|
|
void
|
|
_stdcall
|
|
ModuleEntry(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
ModuleEntry is the stub program that loads Windows 2000 Setup DLLs.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none. ExitProcess will set the process' exit code.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR RunningInstancePath[MAX_PATH];
|
|
TCHAR Temp[MAX_PATH];
|
|
TCHAR Text1[MAX_PATH+sizeof("msvcrt.dll")];
|
|
TCHAR Text2[MAX_PATH+MAX_PATH];
|
|
TCHAR Text3[MAX_PATH];
|
|
TCHAR *WackExeName, *p;
|
|
TCHAR winnt32DllPath[MAX_PATH+MAX_PATH];
|
|
HMODULE WinNT32;
|
|
BOOL Downloaded;
|
|
DWORD d;
|
|
BOOL b;
|
|
HWND Dlg = NULL;
|
|
HANDLE WinNT32Stub = NULL;
|
|
PWINNT32 winnt32;
|
|
HKEY key;
|
|
DWORD type;
|
|
PCTSTR moduleName;
|
|
PSTR restartCmdLine = NULL;
|
|
PTSTR RemainingArgs, NewCmdLine, UnattendPrefix, UnattendFileName;
|
|
PTSTR DynamicUpdatesShare;
|
|
BOOL Cleanup, NoDownload, DisableDynamicUpdates, LocalWinnt32, CheckUpgradeOnly;
|
|
PTSTR RestartAnswerFile;
|
|
UINT CmdLineLen;
|
|
PTSTR FileName;
|
|
PCTSTR ExtraFiles[2];
|
|
TCHAR cdFilePath[MAX_PATH+sizeof("win9xupg\\msvcrt.dll")];
|
|
PTSTR duShare = NULL;
|
|
UINT tcharsNeeded;
|
|
HRESULT hr;
|
|
|
|
#if defined(_X86_)
|
|
|
|
TCHAR DownloadDest[MAX_PATH] = TEXT("");
|
|
TCHAR DefSourcesDir[MAX_PATH];
|
|
BOOL IsWin9x;
|
|
|
|
//
|
|
// Check OS version. Disallow Win32s and NT < 4.00
|
|
//
|
|
d = GetVersion();
|
|
if((d & 0xff) < 4) {
|
|
|
|
if (LoadString (GetModuleHandle (NULL), IDS_VERERROR, Text1, sizeof(Text1)/sizeof(Text1[0]))
|
|
&& LoadString (GetModuleHandle (NULL), IDS_APPNAME, Text2, sizeof(Text2)/sizeof(Text2[0])))
|
|
{
|
|
MessageBox (NULL, Text1, Text2, MB_ICONERROR | MB_OK | MB_SYSTEMMODAL);
|
|
}
|
|
|
|
ExitProcess (ERROR_OLD_WIN_VERSION);
|
|
}
|
|
|
|
IsWin9x = (d & 0x80000000) != 0;
|
|
|
|
#else
|
|
|
|
#define IsWin9x ((BOOL)FALSE)
|
|
|
|
#endif
|
|
|
|
//
|
|
// get this instance's path
|
|
//
|
|
if (!GetModuleFileName(NULL, RunningInstancePath, MAX_PATH)) {
|
|
ExitProcess (GetLastError ());
|
|
}
|
|
WackExeName = FindLastWack (RunningInstancePath);
|
|
if (!WackExeName) { // shut up PREfix. This case should never happen.
|
|
ExitProcess (ERROR_BAD_PATHNAME);
|
|
}
|
|
|
|
//
|
|
// Ansi version on Win95. Unicode on NT.
|
|
//
|
|
moduleName = IsWin9x ? TEXT("WINNT32A.DLL") : TEXT("WINNT32U.DLL");
|
|
winnt32DllPath[0] = 0;
|
|
|
|
//
|
|
// get command line options
|
|
// allocate a bigger buffer for safety
|
|
//
|
|
RemainingArgs = ALLOC_TEXT(lstrlen(GetCommandLine()) * 2);
|
|
if (!RemainingArgs) {
|
|
ExitProcess (GetLastError ());
|
|
}
|
|
|
|
GetCmdLineArgs (
|
|
GetCommandLine (),
|
|
&Cleanup,
|
|
&NoDownload,
|
|
&UnattendPrefix,
|
|
&UnattendFileName,
|
|
&DisableDynamicUpdates,
|
|
&DynamicUpdatesShare,
|
|
&RestartAnswerFile,
|
|
&LocalWinnt32,
|
|
&CheckUpgradeOnly,
|
|
RemainingArgs
|
|
);
|
|
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
|
|
if (Cleanup) {
|
|
pCleanup ();
|
|
ExitProcess (0);
|
|
}
|
|
|
|
#if defined(_X86_)
|
|
|
|
if (IsWin9x) {
|
|
|
|
WinNT32Stub = CreateEvent (NULL, FALSE, FALSE, TEXT("_WinNT32_Stub_"));
|
|
if (!WinNT32Stub) {
|
|
ExitProcess (GetLastError ());
|
|
}
|
|
|
|
b = (GetLastError() == ERROR_SUCCESS);
|
|
|
|
if (!NoDownload && !DynamicUpdatesShare && pShouldDownloadToLocalDisk (RunningInstancePath)) {
|
|
|
|
Dlg = CreateDialog (GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SETUPINIT), NULL, DlgProc);
|
|
|
|
GetWindowsDirectory (DownloadDest, MAX_PATH);
|
|
StringCbCat (DownloadDest, sizeof(DownloadDest), INTERNAL_WINNT32_DIR);
|
|
*WackExeName = 0;
|
|
|
|
if (UnattendFileName &&
|
|
GetFullPathName (UnattendFileName, MAX_PATH, Temp, &FileName) &&
|
|
lstrcmpi (UnattendFileName, Temp)
|
|
) {
|
|
ExtraFiles[0] = Temp;
|
|
ExtraFiles[1] = NULL;
|
|
} else {
|
|
ExtraFiles[0] = NULL;
|
|
FileName = UnattendFileName;
|
|
}
|
|
|
|
Downloaded = DownloadProgramFiles (
|
|
RunningInstancePath,
|
|
DownloadDest,
|
|
ExtraFiles
|
|
);
|
|
|
|
*WackExeName = TEXT('\\');
|
|
|
|
if (Downloaded) {
|
|
//
|
|
// get default sources dir
|
|
//
|
|
|
|
//this lstrcpy is ok, since both buffers are of size MAX_PATH
|
|
lstrcpy (DefSourcesDir, RunningInstancePath);
|
|
*FindLastWack (DefSourcesDir) = 0;
|
|
p = FindLastWack (DefSourcesDir);
|
|
if (p && lstrcmpi(p, INTERNAL_WINNT32_DIR) == 0) {
|
|
*p = 0;
|
|
}
|
|
|
|
if (FileName) {
|
|
CmdLineLen = lstrlen (RemainingArgs);
|
|
if (CmdLineLen > 0) {
|
|
//
|
|
// count the space between args
|
|
//
|
|
CmdLineLen += CHARS(" ");
|
|
}
|
|
CmdLineLen += lstrlen (UnattendPrefix);
|
|
CmdLineLen += lstrlen (FileName);
|
|
NewCmdLine = ALLOC_TEXT(CmdLineLen);
|
|
if (NewCmdLine) {
|
|
if (*RemainingArgs) {
|
|
lstrcpy (NewCmdLine, RemainingArgs);
|
|
lstrcat (NewCmdLine, TEXT(" "));
|
|
} else {
|
|
*NewCmdLine = 0;
|
|
}
|
|
lstrcat (NewCmdLine, UnattendPrefix);
|
|
lstrcat (NewCmdLine, FileName);
|
|
|
|
FREE (RemainingArgs);
|
|
RemainingArgs = NewCmdLine;
|
|
NewCmdLine = NULL;
|
|
}
|
|
}
|
|
//
|
|
// append /LOCAL to the new processes command line
|
|
// to let it know it's running from a local share
|
|
//
|
|
NewCmdLine = ALLOC_TEXT(lstrlen (RemainingArgs) + sizeof(TEXT(" /LOCAL"))/sizeof(TCHAR) + 1);
|
|
if (NewCmdLine) {
|
|
|
|
//we have pre-calculated the size of the NewCmdLine buffer
|
|
wsprintf (NewCmdLine, TEXT("%s /%s"), RemainingArgs, TEXT("LOCAL"));
|
|
|
|
if (pReRun (DownloadDest, WackExeName, NewCmdLine, DefSourcesDir)) {
|
|
//
|
|
// the new process will do it; this one will just die
|
|
// but after the signal that the Setup Wizard is on
|
|
// anyway, if something goes very wrong,
|
|
// don't wait more than 10 sec.
|
|
// this should be enough for the wizard to appear
|
|
// (or any error message box) on any machine that installs W2K
|
|
//
|
|
WaitForSingleObject (WinNT32Stub, 10000);
|
|
CloseHandle (WinNT32Stub);
|
|
if (Dlg) {
|
|
DestroyWindow (Dlg);
|
|
}
|
|
d = 0;
|
|
} else {
|
|
d = GetLastError ();
|
|
}
|
|
} else {
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
ExitProcess (d);
|
|
}
|
|
}
|
|
|
|
if (!Dlg && WinNT32Stub) {
|
|
CloseHandle (WinNT32Stub);
|
|
WinNT32Stub = NULL;
|
|
}
|
|
}
|
|
|
|
#endif // defined(_X86_)
|
|
|
|
#endif // defined(_AMD64_) || defined(_X86_)
|
|
|
|
if (RemainingArgs) {
|
|
FREE(RemainingArgs);
|
|
RemainingArgs = NULL;
|
|
}
|
|
if (UnattendPrefix) {
|
|
FREE(UnattendPrefix);
|
|
UnattendPrefix = NULL;
|
|
}
|
|
|
|
if (!DisableDynamicUpdates && !DynamicUpdatesShare) {
|
|
PCTSTR af = NULL;
|
|
if (RestartAnswerFile) {
|
|
af = RestartAnswerFile;
|
|
} else if (UnattendFileName) {
|
|
if (GetFullPathName (UnattendFileName, MAX_PATH, Temp, &FileName)) {
|
|
af = Temp;
|
|
}
|
|
}
|
|
//
|
|
// get the path from this answer file
|
|
//
|
|
if (af) {
|
|
GetPrivateProfileString (
|
|
WINNT_UNATTENDED,
|
|
WINNT_U_DYNAMICUPDATESDISABLE,
|
|
TEXT(""),
|
|
Text2,
|
|
MAX_PATH,
|
|
af
|
|
);
|
|
DisableDynamicUpdates = !lstrcmpi (Text2, WINNT_A_YES);
|
|
|
|
if (!DisableDynamicUpdates) {
|
|
if (GetPrivateProfileString (
|
|
WINNT_UNATTENDED,
|
|
WINNT_U_DYNAMICUPDATESHARE,
|
|
TEXT(""),
|
|
Text2,
|
|
MAX_PATH,
|
|
af
|
|
)) {
|
|
DynamicUpdatesShare = DuplicateText (Text2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UnattendFileName) {
|
|
FREE(UnattendFileName);
|
|
UnattendFileName = NULL;
|
|
}
|
|
|
|
b = FALSE;
|
|
if (!CheckUpgradeOnly && !DisableDynamicUpdates && !DynamicUpdatesShare) {
|
|
DynamicUpdatesShare = pGetRecentDUShare (MAX_UPGCHK_ELAPSED_SECONDS);
|
|
if (DynamicUpdatesShare) {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
|
|
d = ERROR_SUCCESS;
|
|
|
|
if (!DisableDynamicUpdates && DynamicUpdatesShare) {
|
|
DWORD regFileVersionMS, regFileVersionLS;
|
|
DWORD cdFileVersionMS, cdFileVersionLS;
|
|
//
|
|
// check if there is a replacement module newer than the CD version
|
|
//
|
|
if (GetFileAttributes (DynamicUpdatesShare) == (DWORD)-1) {
|
|
if (!b) {
|
|
d = GetLastError ();
|
|
|
|
if (LoadString (GetModuleHandle (NULL), IDS_APPNAME, Text3, sizeof(Text3)/sizeof(Text3[0]))
|
|
&& LoadString (GetModuleHandle (NULL), IDS_PATHERROR, Text1, sizeof(Text1)/sizeof(Text1[0])))
|
|
{
|
|
//Note: Text2 is 2*MAX_PATH, so it is large enough to hold the message defined in the
|
|
//resource string table, and one string of max size MAX_PATH.
|
|
wsprintf (Text2, Text1, DynamicUpdatesShare);
|
|
MessageBox (NULL, Text2, Text3, MB_ICONERROR | MB_OK | MB_SYSTEMMODAL);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//Note: DynamicUpdatesShare comes from the registry, so we have to treat it with caution
|
|
StringCbCopy(Text2, sizeof(Text2), DynamicUpdatesShare);
|
|
StringCbCat(Text2, sizeof(Text2), TEXT("\\WINNT32\\"));
|
|
hr = StringCbCat(Text2, sizeof(Text2), moduleName);
|
|
|
|
if (FAILED(hr)) {
|
|
ExitProcess(ERROR_BUFFER_OVERFLOW);
|
|
}
|
|
|
|
|
|
if (GetFileAttributes (Text2) != (DWORD)-1 &&
|
|
GetFileVersion (Text2, ®FileVersionMS, ®FileVersionLS))
|
|
{
|
|
tcharsNeeded = min(MAX_PATH, (INT)(WackExeName - RunningInstancePath + 2));
|
|
StringCchCopy(cdFilePath, tcharsNeeded, RunningInstancePath);
|
|
hr = StringCchCat(cdFilePath, MAX_PATH, moduleName);
|
|
|
|
if (FAILED(hr)) {
|
|
ExitProcess(ERROR_BUFFER_OVERFLOW);
|
|
}
|
|
|
|
if (GetFileVersion (cdFilePath, &cdFileVersionMS, &cdFileVersionLS)) {
|
|
if (MAKEULONGLONG(regFileVersionLS, regFileVersionMS) >
|
|
MAKEULONGLONG(cdFileVersionLS, cdFileVersionMS)) {
|
|
//This lstrcpy is ok, since the buffers are the same size
|
|
lstrcpy (winnt32DllPath, Text2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FREE (DynamicUpdatesShare);
|
|
DynamicUpdatesShare = NULL;
|
|
}
|
|
|
|
if (d == ERROR_SUCCESS) {
|
|
|
|
#if defined(_X86_)
|
|
|
|
//
|
|
// before attempting to load the main module, make sure msvcrt.dll is present in the system dir
|
|
//
|
|
if (!GetSystemDirectory (Text1, MAX_PATH)) {
|
|
ExitProcess (GetLastError ());
|
|
}
|
|
|
|
//This is ok, since Text1 is MAX_PATH + 32 TCHARs long
|
|
ConcatenatePaths (Text1, TEXT("msvcrt.dll"));
|
|
|
|
d = GetFileAttributes (Text1);
|
|
if (d == (DWORD)-1) {
|
|
//
|
|
// no local msvcrt.dll; copy the private file from CD
|
|
//
|
|
tcharsNeeded = min(MAX_PATH, (INT)(WackExeName - RunningInstancePath + 2));
|
|
StringCchCopy(cdFilePath, tcharsNeeded, RunningInstancePath);
|
|
|
|
//this is ok, becuase cdFilePath is MAX_PATH + 32 TCHARs long
|
|
ConcatenatePaths (cdFilePath, TEXT("win9xupg\\msvcrt.dll"));
|
|
if (!CopyFile (cdFilePath, Text1, TRUE)) {
|
|
ExitProcess (GetLastError ());
|
|
}
|
|
} else if (d & FILE_ATTRIBUTE_DIRECTORY) {
|
|
ExitProcess (ERROR_DIRECTORY);
|
|
}
|
|
|
|
#endif
|
|
|
|
*WackExeName = 0;
|
|
if (!winnt32DllPath[0]) {
|
|
//The next two string operations are safe, since winnt32DllPath is 2*MAX_PATH TCHARs long
|
|
lstrcpy (winnt32DllPath, RunningInstancePath);
|
|
ConcatenatePaths (winnt32DllPath, moduleName);
|
|
}
|
|
|
|
b = FALSE;
|
|
WinNT32 = LoadLibrary (winnt32DllPath);
|
|
if(WinNT32) {
|
|
winnt32 = (PWINNT32) GetProcAddress(WinNT32, "winnt32");
|
|
if (winnt32) {
|
|
d = (*winnt32) (LocalWinnt32 ? RunningInstancePath : NULL, Dlg, WinNT32Stub, &restartCmdLine);
|
|
b = TRUE;
|
|
}
|
|
FreeLibrary (WinNT32);
|
|
}
|
|
if (!b) {
|
|
d = GetLastError ();
|
|
if (LoadString (GetModuleHandle (NULL), IDS_APPNAME, Text3, sizeof(Text3)/sizeof(TCHAR))
|
|
&& LoadString (GetModuleHandle (NULL), IDS_DLLERROR, Text1, sizeof(Text1)/sizeof(TCHAR)))
|
|
{
|
|
//This is safe, since Text2 is 2*MAX_PATH TCHARs long
|
|
wsprintf (Text2, Text1, winnt32DllPath);
|
|
MessageBox (NULL, Text2, Text3, MB_ICONERROR | MB_OK | MB_SYSTEMMODAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// remove downloaded files
|
|
//
|
|
|
|
#ifdef _X86_
|
|
if (IsWin9x) {
|
|
//
|
|
// check if our local directory exists and if so delete it
|
|
//
|
|
if (LocalWinnt32 && GetFileAttributes (RunningInstancePath) != (DWORD)-1) {
|
|
//
|
|
// copy Winnt32.Exe to temp dir and rerun it from there with /CLEANUP option
|
|
//
|
|
// This is ok, since both buffers are of size MAX_PATH
|
|
lstrcpy (DefSourcesDir, RunningInstancePath);
|
|
|
|
CmdLineLen = GetTempPath (MAX_PATH, DownloadDest);
|
|
if (!CmdLineLen) {
|
|
//
|
|
// an error occured; copy it to %windir% instead
|
|
//
|
|
GetWindowsDirectory (DownloadDest, MAX_PATH);
|
|
}
|
|
|
|
//
|
|
// make sure temp path doesn't end in backslash
|
|
//
|
|
p = FindLastWack (DownloadDest);
|
|
if (p && *(p + 1) == 0) {
|
|
*p = 0;
|
|
}
|
|
|
|
*WackExeName = TEXT('\\');
|
|
if (CopyNode (DefSourcesDir, DownloadDest, WackExeName, FALSE, TRUE)) {
|
|
if (!pReRun (DownloadDest, WackExeName, TEXT("/CLEANUP"), NULL)) {
|
|
StringCbCatA (DownloadDest, sizeof(DownloadDest), WackExeName);
|
|
DeleteNode (DownloadDest);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (d == ERROR_SUCCESS) {
|
|
//
|
|
// check if a restart request was made
|
|
//
|
|
if (restartCmdLine) {
|
|
STARTUPINFOA startupInfo;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
|
startupInfo.cb = sizeof(startupInfo);
|
|
if (!CreateProcessA (
|
|
NULL,
|
|
restartCmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NORMAL_PRIORITY_CLASS,
|
|
NULL,
|
|
NULL,
|
|
&startupInfo,
|
|
&pi
|
|
)) {
|
|
d = GetLastError ();
|
|
}
|
|
|
|
FREE (restartCmdLine);
|
|
}
|
|
}
|
|
|
|
ExitProcess(d);
|
|
}
|