Source code of Windows XP (NT5)
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.

1858 lines
47 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. winnt32.c
  5. Abstract:
  6. Stub loader for WinNT Setup program files.
  7. Author:
  8. Revisions:
  9. Ovidiu Temereanca (ovidiut) 09-Dec-1998
  10. --*/
  11. #include <windows.h>
  12. #include <winver.h>
  13. #include <ntverp.h>
  14. #include <setupbat.h>
  15. #include "winnt32.h"
  16. #include "winnt32p.h"
  17. #ifdef _X86_
  18. #include "i386\download.h"
  19. #define INTERNAL_WINNT32_DIR TEXT("\\WINNT32")
  20. #define MAX_RETRY_INTERVAL_SECONDS 3600L
  21. #endif
  22. #define MAX_UPGCHK_ELAPSED_SECONDS (30 * 60)
  23. #define S_CHKSUM_FILE TEXT("DOSNET.INF")
  24. #define MAKEULONGLONG(low,high) ((ULONGLONG)(((DWORD)(low)) | ((ULONGLONG)((DWORD)(high))) << 32))
  25. #define ALLOC_TEXT(chars) ((PTSTR)HeapAlloc (GetProcessHeap (), 0, ((chars) + 1) * sizeof (TCHAR)))
  26. #define FREE(p) HeapFree (GetProcessHeap (), 0, p)
  27. #define CHARS(string) (sizeof (string) / sizeof ((string)[0]) - 1)
  28. #define pFindEOS(String) pFindChar(String, 0)
  29. PTSTR
  30. FindLastWack (
  31. IN PTSTR String
  32. )
  33. /*++
  34. Routine Description:
  35. FindLastWack returns a pointer to the last backslash character
  36. in the String
  37. Arguments:
  38. String - Specifies the string
  39. Return Value:
  40. The position of the last '\\' in the string or NULL if not found.
  41. --*/
  42. {
  43. PTSTR p;
  44. PTSTR LastChar = NULL;
  45. for(p = String; *p; p = CharNext(p)) {
  46. if(*p == TEXT('\\')) { // the char '\' is never a lead byte
  47. LastChar = p;
  48. }
  49. }
  50. return LastChar;
  51. }
  52. PTSTR
  53. DuplicateText (
  54. IN PCTSTR Text
  55. )
  56. /*++
  57. Routine Description:
  58. DuplicateText allocates memory and then copies a source string into that memory.
  59. Caller is responsible for freeing that memory.
  60. Arguments:
  61. Text - Specifies the source text
  62. Return Value:
  63. A pointer to the duplicate string; NULL if not enough memory.
  64. --*/
  65. {
  66. PTSTR Dup;
  67. Dup = ALLOC_TEXT(lstrlen (Text));
  68. if (Dup) {
  69. lstrcpy (Dup, Text);
  70. }
  71. return Dup;
  72. }
  73. PTSTR
  74. pFindChar (
  75. IN PTSTR String,
  76. IN UINT Char
  77. )
  78. /*++
  79. Routine Description:
  80. pFindChar returns a pointer to the first occurence of the Char
  81. in the String
  82. Arguments:
  83. String - Specifies the string
  84. Char - Specifies the char to look for; can be null
  85. Return Value:
  86. A pointer to the first occurence of the char in this string
  87. or NULL if not found
  88. --*/
  89. {
  90. while (*String) {
  91. if ((UINT)*String == Char) {
  92. return String;
  93. }
  94. String = CharNext (String);
  95. }
  96. return Char ? NULL : String;
  97. }
  98. VOID
  99. ConcatenatePaths (
  100. IN PTSTR LeadingPath,
  101. IN PCTSTR TrailingPath
  102. )
  103. /*++
  104. Routine Description:
  105. ConcatenatePaths concatenates the two given paths, taking care to
  106. insert only one backslash between them. The resulting path is stored
  107. in LeadingPath.
  108. Arguments:
  109. LeadingPath - Specifies the leading path
  110. TrailingPath - Specifies the trailing path
  111. Return Value:
  112. none
  113. --*/
  114. {
  115. PTSTR p;
  116. //
  117. // check for "\" at the end of leading dir
  118. //
  119. p = FindLastWack (LeadingPath);
  120. if (!p) {
  121. p = pFindEOS (LeadingPath);
  122. *p++ = TEXT('\\');
  123. } else {
  124. if (*(p + 1) == 0) {
  125. p++;
  126. } else {
  127. p = pFindEOS (p);
  128. *p++ = TEXT('\\');
  129. }
  130. }
  131. //
  132. // check for "\" at the beginning of trailing dir
  133. //
  134. if (*TrailingPath == TEXT('\\')) {
  135. TrailingPath++;
  136. }
  137. lstrcpy (p, TrailingPath);
  138. }
  139. BOOL
  140. GetFileVersion (
  141. IN PCTSTR FilePath,
  142. OUT PDWORD FileVersionMS, OPTIONAL
  143. OUT PDWORD FileVersionLS OPTIONAL
  144. )
  145. {
  146. DWORD dwLength, dwTemp;
  147. LPVOID lpData;
  148. VS_FIXEDFILEINFO *VsInfo;
  149. UINT DataLength;
  150. BOOL b = FALSE;
  151. if (GetFileAttributes (FilePath) != (DWORD)-1) {
  152. if (dwLength = GetFileVersionInfoSize ((PTSTR)FilePath, &dwTemp)) {
  153. if (lpData = LocalAlloc (LPTR, dwLength)) {
  154. if (GetFileVersionInfo ((PTSTR)FilePath, 0, dwLength, lpData)) {
  155. if (VerQueryValue (lpData, TEXT("\\"), &VsInfo, &DataLength)) {
  156. if (FileVersionMS) {
  157. *FileVersionMS = VsInfo->dwFileVersionMS;
  158. }
  159. if (FileVersionLS) {
  160. *FileVersionLS = VsInfo->dwFileVersionLS;
  161. }
  162. b = TRUE;
  163. }
  164. }
  165. LocalFree (lpData);
  166. }
  167. }
  168. }
  169. return b;
  170. }
  171. #ifdef _X86_
  172. BOOL
  173. pReRun (
  174. IN PCTSTR StartDir,
  175. IN PCTSTR WackExeName,
  176. IN PCTSTR CmdLineArguments,
  177. IN PCTSTR DefSourcesDir OPTIONAL
  178. )
  179. /*++
  180. Routine Description:
  181. pReRun tries to launch a instance of this exe from a local drive,
  182. specifing an additional command line parameter (/S:<Source_Dir>).
  183. Arguments:
  184. StartDir - Specifies the starting directory from where the instance will be launched
  185. WackExeName - Specifies the file name only of the EXE to launch, preceded
  186. by a backslash
  187. CmdLineArguments - Specifies the command line arguments initially supplied
  188. DefSourcesDir - Specifies the default directory containing instalation files
  189. Return Value:
  190. TRUE if the launch was successful
  191. --*/
  192. {
  193. PTSTR CmdLine;
  194. INT Chars;
  195. STARTUPINFO StartupInfo;
  196. PROCESS_INFORMATION pi;
  197. BOOL b = FALSE;
  198. DWORD rc;
  199. Chars = lstrlen (StartDir) + lstrlen (WackExeName) + CHARS(" ") + lstrlen (CmdLineArguments);
  200. if (DefSourcesDir) {
  201. Chars += CHARS(" /S:") + lstrlen (DefSourcesDir);
  202. }
  203. CmdLine = ALLOC_TEXT(Chars);
  204. if (!CmdLine) {
  205. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  206. return FALSE;
  207. }
  208. lstrcpy (CmdLine, StartDir);
  209. lstrcat (CmdLine, WackExeName);
  210. lstrcat (CmdLine, TEXT(" "));
  211. lstrcat (CmdLine, CmdLineArguments);
  212. if (DefSourcesDir) {
  213. lstrcat (CmdLine, TEXT(" /S:"));
  214. lstrcat (CmdLine, DefSourcesDir);
  215. }
  216. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  217. StartupInfo.cb = sizeof(StartupInfo);
  218. b = CreateProcess (
  219. NULL,
  220. CmdLine,
  221. NULL,
  222. NULL,
  223. FALSE,
  224. NORMAL_PRIORITY_CLASS,
  225. NULL,
  226. StartDir,
  227. &StartupInfo,
  228. &pi
  229. );
  230. rc = GetLastError ();
  231. FREE (CmdLine);
  232. SetLastError (rc);
  233. return b;
  234. }
  235. BOOL
  236. pCleanup (
  237. VOID
  238. )
  239. /*++
  240. Routine Description:
  241. pCleanup deletes all locally installed files and marks current running
  242. instance for deletion the next time system will reboot.
  243. Arguments:
  244. none
  245. Return Value:
  246. TRUE if the operation completed successfully; the machine will need to
  247. reboot before actual complete delete will take place.
  248. --*/
  249. {
  250. CHAR RunningInstancePath[MAX_PATH];
  251. CHAR Buffer[MAX_PATH];
  252. BOOL b;
  253. DWORD StartingTime;
  254. PCTSTR p;
  255. if (!GetModuleFileNameA (NULL, RunningInstancePath, MAX_PATH)) {
  256. return FALSE;
  257. }
  258. #if 0
  259. //
  260. // the following code doesn't work on Win9x (unfortunately)
  261. // the system returns a valid handle, but the exe is NOT deleted
  262. //
  263. CreateFile (
  264. RunningInstancePath,
  265. DELETE,
  266. FILE_SHARE_READ | FILE_SHARE_WRITE,
  267. NULL,
  268. OPEN_EXISTING,
  269. FILE_FLAG_DELETE_ON_CLOSE,
  270. NULL
  271. );
  272. #endif
  273. //
  274. // wait until WINNT32\WINNT32.EXE file can be deleted
  275. // or the retry interval of time elapses
  276. //
  277. if (!GetWindowsDirectoryA (Buffer, MAX_PATH)) {
  278. return FALSE;
  279. }
  280. lstrcatA (Buffer, INTERNAL_WINNT32_DIR);
  281. p = FindLastWack ((PTSTR)RunningInstancePath);
  282. if (!p) {
  283. return FALSE;
  284. }
  285. lstrcatA (Buffer, p);
  286. StartingTime = GetTickCount ();
  287. while (GetFileAttributes (Buffer) != (DWORD)-1) {
  288. //
  289. // try to delete it
  290. //
  291. if (DeleteNode (Buffer)) {
  292. break;
  293. }
  294. //
  295. // give up if time elapses
  296. //
  297. if (GetTickCount () - StartingTime > 1000L * MAX_RETRY_INTERVAL_SECONDS) {
  298. break;
  299. }
  300. //
  301. // nothing useful to do; let the other processes run
  302. //
  303. Sleep (0);
  304. }
  305. //
  306. // wait until WINNT32\SETUPLOG.EXE file can be deleted
  307. // or the retry interval of time elapses
  308. //
  309. if (!GetWindowsDirectoryA (Buffer, MAX_PATH)) {
  310. return FALSE;
  311. }
  312. lstrcatA (Buffer, INTERNAL_WINNT32_DIR);
  313. lstrcatA (Buffer, TEXT("\\SETUPLOG.EXE"));
  314. StartingTime = GetTickCount ();
  315. while (GetFileAttributes (Buffer) != (DWORD)-1) {
  316. if (DeleteNode (Buffer)) {
  317. break;
  318. }
  319. if (GetTickCount () - StartingTime > 1000L * MAX_RETRY_INTERVAL_SECONDS) {
  320. break;
  321. }
  322. Sleep (0);
  323. }
  324. if (!GetWindowsDirectoryA (Buffer, MAX_PATH)) {
  325. return FALSE;
  326. }
  327. lstrcatA (Buffer, INTERNAL_WINNT32_DIR);
  328. b = DeleteNode (Buffer);
  329. if (!GetWindowsDirectoryA (Buffer, MAX_PATH)) {
  330. return FALSE;
  331. }
  332. lstrcatA (Buffer, "\\WININIT.INI");
  333. return
  334. WritePrivateProfileString ("rename", "NUL", RunningInstancePath, Buffer) && b;
  335. }
  336. BOOL
  337. pShouldDownloadToLocalDisk (
  338. IN PTSTR Path
  339. )
  340. /*++
  341. Routine Description:
  342. pShouldDownloadToLocalDisk returns TRUE if winnt32 files should be
  343. downloaded to a local disk first (like in the case of sources on
  344. a remote disk or on a CD)
  345. Arguments:
  346. Path - Specifies the path
  347. Return Value:
  348. TRUE if the specified path is on an untrusted media
  349. --*/
  350. {
  351. TCHAR ch;
  352. BOOL Remote = TRUE;
  353. UINT type;
  354. if (Path[1] == TEXT(':') && Path[2] == TEXT('\\')) {
  355. ch = Path[3];
  356. Path[3] = 0;
  357. type = GetDriveType (Path);
  358. Remote = (type == DRIVE_REMOTE) || (type == DRIVE_CDROM);
  359. Path[3] = ch;
  360. }
  361. return Remote;
  362. }
  363. VOID
  364. pCenterWindowOnDesktop (
  365. HWND WndToCenter
  366. )
  367. /*++
  368. Routine Description:
  369. Centers a dialog relative to the 'work area' of the desktop.
  370. Arguments:
  371. WndToCenter - window handle of dialog to center
  372. Return Value:
  373. None.
  374. --*/
  375. {
  376. RECT rcFrame, rcWindow;
  377. LONG x, y, w, h;
  378. POINT point;
  379. HWND Desktop = GetDesktopWindow ();
  380. point.x = point.y = 0;
  381. ClientToScreen(Desktop, &point);
  382. GetWindowRect(WndToCenter, &rcWindow);
  383. GetClientRect(Desktop, &rcFrame);
  384. w = rcWindow.right - rcWindow.left + 1;
  385. h = rcWindow.bottom - rcWindow.top + 1;
  386. x = point.x + ((rcFrame.right - rcFrame.left + 1 - w) / 2);
  387. y = point.y + ((rcFrame.bottom - rcFrame.top + 1 - h) / 2);
  388. //
  389. // Get the work area for the current desktop (i.e., the area that
  390. // the tray doesn't occupy).
  391. //
  392. if(!SystemParametersInfo (SPI_GETWORKAREA, 0, (PVOID)&rcFrame, 0)) {
  393. //
  394. // For some reason SPI failed, so use the full screen.
  395. //
  396. rcFrame.top = rcFrame.left = 0;
  397. rcFrame.right = GetSystemMetrics(SM_CXSCREEN);
  398. rcFrame.bottom = GetSystemMetrics(SM_CYSCREEN);
  399. }
  400. if(x + w > rcFrame.right) {
  401. x = rcFrame.right - w;
  402. } else if(x < rcFrame.left) {
  403. x = rcFrame.left;
  404. }
  405. if(y + h > rcFrame.bottom) {
  406. y = rcFrame.bottom - h;
  407. } else if(y < rcFrame.top) {
  408. y = rcFrame.top;
  409. }
  410. MoveWindow(WndToCenter, x, y, w, h, FALSE);
  411. }
  412. BOOL CALLBACK DlgProc (
  413. HWND Dlg,
  414. UINT Msg,
  415. WPARAM wParam,
  416. LPARAM lParam
  417. )
  418. /*++
  419. Routine Description:
  420. This is the callback procedure for the dialog displayed while
  421. components are copied from the network
  422. Arguments:
  423. Dlg - Specifies the dialog window handle
  424. Msg - Specifies the message
  425. wParam - Specifies the first param
  426. lParam - Specifies the second param
  427. Return Value:
  428. Depends on the specific message.
  429. --*/
  430. {
  431. static HANDLE Bitmap = NULL;
  432. static HCURSOR Cursor = NULL;
  433. RECT rect;
  434. HWND Text;
  435. BITMAP bm;
  436. INT i;
  437. switch (Msg) {
  438. case WM_INITDIALOG:
  439. Cursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
  440. ShowCursor (TRUE);
  441. Bitmap = LoadBitmap (GetModuleHandle (NULL), MAKEINTRESOURCE(IDB_INIT_WIN2000));
  442. if (Bitmap) {
  443. if (GetObject (Bitmap, sizeof (bm), &bm)) {
  444. GetClientRect (Dlg, &rect);
  445. rect.right = bm.bmWidth;
  446. AdjustWindowRect (&rect, GetWindowLong (Dlg, GWL_STYLE), FALSE);
  447. SetWindowPos (
  448. Dlg,
  449. NULL,
  450. 0,
  451. 0,
  452. rect.right - rect.left,
  453. rect.bottom - rect.top,
  454. SWP_NOMOVE | SWP_NOZORDER);
  455. }
  456. SendDlgItemMessage(Dlg, IDC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)Bitmap);
  457. }
  458. GetClientRect (Dlg, &rect);
  459. i = rect.right - rect.left;
  460. Text = GetDlgItem (Dlg, IDC_TEXT);
  461. if (GetWindowRect (Text, &rect)) {
  462. i = (i - (rect.right - rect.left)) / 2;
  463. ScreenToClient (Dlg, (LPPOINT)&rect);
  464. SetWindowPos (
  465. Text,
  466. NULL,
  467. i,
  468. rect.top,
  469. 0,
  470. 0,
  471. SWP_NOSIZE | SWP_NOZORDER);
  472. }
  473. pCenterWindowOnDesktop (Dlg);
  474. return TRUE;
  475. case WM_DESTROY:
  476. ShowCursor (FALSE);
  477. if (Cursor) {
  478. SetCursor (Cursor);
  479. Cursor = NULL;
  480. }
  481. if (Bitmap) {
  482. DeleteObject (Bitmap);
  483. Bitmap = NULL;
  484. }
  485. }
  486. return FALSE;
  487. }
  488. #endif
  489. INT
  490. pStringICompareCharCount (
  491. IN PCTSTR String1,
  492. IN PCTSTR String2,
  493. IN DWORD CharCount
  494. )
  495. /*++
  496. Routine Description:
  497. This routine behaves like _tcsnicmp.
  498. Arguments:
  499. String1 - Specifies the first string
  500. String2 - Specifies the second string
  501. CharCount - Specifies the number of chars to compare at most
  502. Return Value:
  503. 0 if strings are equal; -1 if first string is lesser; 1 if first is greater
  504. --*/
  505. {
  506. TCHAR ch1, ch2;
  507. if (!CharCount) {
  508. return 0;
  509. }
  510. while (*String1) {
  511. ch1 = (TCHAR)CharUpper ((LPTSTR)*String1);
  512. ch2 = (TCHAR)CharUpper ((LPTSTR)*String2);
  513. if (ch1 - ch2) {
  514. return ch1 - ch2;
  515. }
  516. CharCount--;
  517. if (!CharCount) {
  518. return 0;
  519. }
  520. String1 = CharNext (String1);
  521. String2 = CharNext (String2);
  522. }
  523. return -(*String2);
  524. }
  525. VOID
  526. pParseCmdLine (
  527. IN PTSTR CmdStart,
  528. OUT PTSTR* ArgValues,
  529. OUT PTSTR pStr,
  530. OUT INT *NumArgs,
  531. OUT INT *NumBytes
  532. )
  533. /*
  534. Routine Description:
  535. pParseCmdLine parses the command line and sets up the ArgValues array.
  536. On entry, CmdStart should point to the command line,
  537. ArgValues should point to memory for the ArgValues array,
  538. pStr points to memory to place the text of the arguments.
  539. If these are NULL, then no storing (only counting)
  540. is done. On exit, *NumArgs has the number of
  541. arguments (plus one for a final NULL argument),
  542. and *NumBytes has the number of bytes used in the buffer
  543. pointed to by args.
  544. Arguments:
  545. CmdStart - Specifies the command line having the form:
  546. <progname><nul><args><nul>
  547. ArgValues - Receives the arguments array;
  548. NULL means don't build array
  549. pStr - Receives the argument text; NULL means don't store text
  550. NumArgs - Receives the number of ArgValues entries created
  551. NumBytes - Receives the number of bytes used in buffer
  552. Return Value:
  553. none
  554. */
  555. {
  556. PTSTR p;
  557. TCHAR c;
  558. INT inquote; /* 1 = inside quotes */
  559. INT copychar; /* 1 = copy char to *args */
  560. WORD numslash; /* num of backslashes seen */
  561. *NumBytes = 0;
  562. *NumArgs = 1; /* the program name at least */
  563. /* first scan the program name, copy it, and count the bytes */
  564. p = CmdStart;
  565. if (ArgValues)
  566. *ArgValues++ = pStr;
  567. /* A quoted program name is handled here. The handling is much
  568. simpler than for other arguments. Basically, whatever lies
  569. between the leading double-quote and next one, or a terminal null
  570. character is simply accepted. Fancier handling is not required
  571. because the program name must be a legal NTFS/HPFS file name.
  572. Note that the double-quote characters are not copied, nor do they
  573. contribute to NumBytes. */
  574. if (*p == TEXT('\"'))
  575. {
  576. /* scan from just past the first double-quote through the next
  577. double-quote, or up to a null, whichever comes first */
  578. while ((*(++p) != TEXT('\"')) && (*p != TEXT('\0')))
  579. {
  580. *NumBytes += sizeof(TCHAR);
  581. if (pStr)
  582. *pStr++ = *p;
  583. }
  584. /* append the terminating null */
  585. *NumBytes += sizeof(TCHAR);
  586. if (pStr)
  587. *pStr++ = TEXT('\0');
  588. /* if we stopped on a double-quote (usual case), skip over it */
  589. if (*p == TEXT('\"'))
  590. p++;
  591. }
  592. else
  593. {
  594. /* Not a quoted program name */
  595. do {
  596. *NumBytes += sizeof(TCHAR);
  597. if (pStr)
  598. *pStr++ = *p;
  599. c = *p++;
  600. } while (c > TEXT(' '));
  601. if (c == TEXT('\0'))
  602. {
  603. p--;
  604. }
  605. else
  606. {
  607. if (pStr)
  608. *(pStr - 1) = TEXT('\0');
  609. }
  610. }
  611. inquote = 0;
  612. /* loop on each argument */
  613. for ( ; ; )
  614. {
  615. if (*p)
  616. {
  617. while (*p == TEXT(' ') || *p == TEXT('\t'))
  618. ++p;
  619. }
  620. if (*p == TEXT('\0'))
  621. break; /* end of args */
  622. /* scan an argument */
  623. if (ArgValues)
  624. *ArgValues++ = pStr; /* store ptr to arg */
  625. ++*NumArgs;
  626. /* loop through scanning one argument */
  627. for ( ; ; )
  628. {
  629. copychar = 1;
  630. /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
  631. 2N+1 backslashes + " ==> N backslashes + literal "
  632. N backslashes ==> N backslashes */
  633. numslash = 0;
  634. while (*p == TEXT('\\'))
  635. {
  636. /* count number of backslashes for use below */
  637. ++p;
  638. ++numslash;
  639. }
  640. if (*p == TEXT('\"'))
  641. {
  642. /* if 2N backslashes before, start/end quote, otherwise
  643. copy literally */
  644. if (numslash % 2 == 0)
  645. {
  646. if (inquote)
  647. if (p[1] == TEXT('\"'))
  648. p++; /* Double quote inside quoted string */
  649. else /* skip first quote char and copy second */
  650. copychar = 0;
  651. else
  652. copychar = 0; /* don't copy quote */
  653. inquote = !inquote;
  654. }
  655. numslash /= 2; /* divide numslash by two */
  656. }
  657. /* copy slashes */
  658. while (numslash--)
  659. {
  660. if (pStr)
  661. *pStr++ = TEXT('\\');
  662. *NumBytes += sizeof(TCHAR);
  663. }
  664. /* if at end of arg, break loop */
  665. if (*p == TEXT('\0') || (!inquote && (*p == TEXT(' ') || *p == TEXT('\t'))))
  666. break;
  667. /* copy character into argument */
  668. if (copychar)
  669. {
  670. if (pStr)
  671. *pStr++ = *p;
  672. *NumBytes += sizeof(TCHAR);
  673. }
  674. ++p;
  675. }
  676. /* null-terminate the argument */
  677. if (pStr)
  678. *pStr++ = TEXT('\0'); /* terminate string */
  679. *NumBytes += sizeof(TCHAR);
  680. }
  681. }
  682. PTSTR*
  683. pCommandLineToArgv (
  684. OUT INT* NumArgs
  685. )
  686. /*++
  687. Routine Description:
  688. pCommandLineToArgv tokens the command line in an array of arguments
  689. created on the heap. The number of entries in this array of args is
  690. stored in *NumArgs. The caller is responsible for freeing this array.
  691. Arguments:
  692. NumArgs - Receives the number of arguments in the array that is returned
  693. Return Value:
  694. An array of pointer to individual arguments specified on the command line
  695. --*/
  696. {
  697. PTSTR CommandLine;
  698. TCHAR ModuleName[MAX_PATH];
  699. PTSTR Start;
  700. INT Size;
  701. PTSTR* Args;
  702. CommandLine = GetCommandLine();
  703. GetModuleFileName (NULL, ModuleName, MAX_PATH);
  704. //
  705. // If there's no command line at all (won't happen from cmd.exe, but
  706. // possibly another program), then we use pgmname as the command line
  707. // to parse, so that ArgValues[0] is initialized to the program name
  708. //
  709. Start = *CommandLine ? CommandLine : ModuleName;
  710. //
  711. // Find out how much space is needed to store args,
  712. // allocate space for ArgValues[] vector and strings,
  713. // and store args and ArgValues ptrs in block we allocate
  714. //
  715. pParseCmdLine (Start, NULL, NULL, NumArgs, &Size);
  716. Args = (PTSTR*) LocalAlloc (
  717. LMEM_FIXED | LMEM_ZEROINIT,
  718. ((*NumArgs + 1) * sizeof(PTSTR)) + Size
  719. );
  720. if (!Args) {
  721. return NULL;
  722. }
  723. pParseCmdLine (Start, Args, (PTSTR)(Args + *NumArgs), NumArgs, &Size);
  724. return Args;
  725. }
  726. VOID
  727. GetCmdLineArgs (
  728. IN PCTSTR CommandLine,
  729. OUT BOOL* Cleanup,
  730. OUT BOOL* NoDownload,
  731. OUT PCTSTR* UnattendPrefix,
  732. OUT PCTSTR* UnattendFileName,
  733. OUT BOOL* DisableDynamicUpdates,
  734. OUT PCTSTR* DynamicUpdatesShare,
  735. OUT PCTSTR* RestartAnswerFile,
  736. OUT BOOL* LocalWinnt32,
  737. OUT BOOL* CheckUpgradeOnly,
  738. OUT PTSTR RemainingArgs
  739. )
  740. /*++
  741. Routine Description:
  742. GetCmdLineArgs retrieves download-specific commands
  743. from the specified command line and stores them in supplied buffers.
  744. Arguments:
  745. CommandLine - Specifies the command line to interpret
  746. Cleanup - Receives a bool indicating if a cleanup option
  747. was specified
  748. NoDownload - Receives a bool indicating if a no-download option
  749. was specified
  750. UnattendPrefix - Receives a pointer to the unattend command-line option, as
  751. specified by the user (including the terminating column)
  752. or NULL if not specified; caller is responsible
  753. for freeing the memory
  754. UnattendFileName - Receives a pointer to the unattended file name
  755. or NULL if not specified; caller is responsible
  756. for freeing the memory
  757. DisableDynamicUpdates - Receives a bool set if DU is to be disabled
  758. DynamicUpdatesShare - Receives a pointer to the dynamic updates share;
  759. caller is responsible for freeing the memory
  760. RestartAnswerFile - Receives a pointer to the /Restart: answer file
  761. LocalWinnt32 - Receives a bool indicating if a winnt32 runs from a local disk
  762. (after an automatic download)
  763. CheckUpgradeOnly - Receives a bool indicating if winnt32 runs in CheckUpgradeOnly mode
  764. RemainingArgs - Receives all remaining arguments not related
  765. to the download operation
  766. Return Value:
  767. none
  768. --*/
  769. {
  770. INT ArgCount;
  771. PTSTR *ArgValues, *CrtArg;
  772. PTSTR CurrentArg, p;
  773. BOOL PassOn;
  774. *Cleanup = FALSE;
  775. *NoDownload = FALSE;
  776. *UnattendPrefix = NULL;
  777. *UnattendFileName = NULL;
  778. *DisableDynamicUpdates = FALSE;
  779. *DynamicUpdatesShare = NULL;
  780. *RemainingArgs = 0;
  781. *LocalWinnt32 = FALSE;
  782. *CheckUpgradeOnly = FALSE;
  783. *RestartAnswerFile = NULL;
  784. CrtArg = ArgValues = pCommandLineToArgv (&ArgCount);
  785. //
  786. // Skip program name. We should always get back ArgCount as at least 1,
  787. // but be robust anyway.
  788. //
  789. if (ArgCount) {
  790. ArgCount--;
  791. CrtArg++;
  792. }
  793. while (ArgCount--) {
  794. CurrentArg = *CrtArg++;
  795. PassOn = TRUE;
  796. if ((*CurrentArg == TEXT('/')) || (*CurrentArg == TEXT('-'))) {
  797. if (lstrcmpi (CurrentArg + 1, TEXT("LOCAL")) == 0) {
  798. *LocalWinnt32 = TRUE;
  799. PassOn = FALSE;
  800. } else if (lstrcmpi (CurrentArg + 1, TEXT("CLEANUP")) == 0) {
  801. *Cleanup = TRUE;
  802. PassOn = FALSE;
  803. } else if (lstrcmpi (CurrentArg + 1, TEXT("NODOWNLOAD")) == 0) {
  804. *NoDownload = TRUE;
  805. PassOn = FALSE;
  806. } else if (lstrcmpi (CurrentArg + 1, TEXT("CHECKUPGRADEONLY")) == 0) {
  807. *CheckUpgradeOnly = TRUE;
  808. } else if (pStringICompareCharCount (CurrentArg + 1, TEXT("UNATTEND"), 8) == 0) {
  809. p = pFindChar (CurrentArg + 1 + 8, TEXT(':'));
  810. if (p && *(p + 1)) {
  811. p++;
  812. *UnattendFileName = DuplicateText (p);
  813. *p = 0;
  814. *UnattendPrefix = DuplicateText (CurrentArg);
  815. PassOn = FALSE;
  816. }
  817. } else if (pStringICompareCharCount (CurrentArg + 1, TEXT("UNATTENDED"), 10) == 0) {
  818. p = pFindChar (CurrentArg + 1 + 10, TEXT(':'));
  819. if (p && *(p + 1)) {
  820. p++;
  821. *UnattendFileName = DuplicateText (p);
  822. *p = 0;
  823. *UnattendPrefix = DuplicateText (CurrentArg);
  824. PassOn = FALSE;
  825. }
  826. } else if (lstrcmpi (CurrentArg + 1, WINNT_U_DYNAMICUPDATESDISABLE) == 0) {
  827. *DisableDynamicUpdates = TRUE;
  828. } else if (pStringICompareCharCount (CurrentArg + 1, WINNT_U_DYNAMICUPDATESHARE, sizeof (WINNT_U_DYNAMICUPDATESHARE_A) - 1) == 0 &&
  829. CurrentArg[sizeof (WINNT_U_DYNAMICUPDATESHARE_A)] == TEXT(':')) {
  830. *DynamicUpdatesShare = DuplicateText (CurrentArg + 1 + sizeof (WINNT_U_DYNAMICUPDATESHARE_A));
  831. } else if (pStringICompareCharCount (CurrentArg + 1, TEXT("RESTART:"), 8) == 0) {
  832. *RestartAnswerFile = DuplicateText (CurrentArg + 1 + 8);
  833. }
  834. }
  835. if (PassOn) {
  836. if (*RemainingArgs) {
  837. lstrcat(RemainingArgs, TEXT(" "));
  838. }
  839. lstrcat(RemainingArgs, CurrentArg);
  840. }
  841. }
  842. LocalFree ((HLOCAL) ArgValues);
  843. }
  844. BOOL
  845. DoesDirExist (
  846. IN PCTSTR Path
  847. )
  848. {
  849. WIN32_FIND_DATA fd;
  850. TCHAR test[MAX_PATH];
  851. HANDLE h;
  852. BOOL b = FALSE;
  853. if (Path) {
  854. wsprintf (test, TEXT("%s\\*"), Path);
  855. h = FindFirstFile (test, &fd);
  856. if (h != INVALID_HANDLE_VALUE) {
  857. FindClose (h);
  858. b = TRUE;
  859. }
  860. }
  861. return b;
  862. }
  863. ULONGLONG
  864. SystemTimeToFileTime64 (
  865. IN PSYSTEMTIME SystemTime
  866. )
  867. {
  868. FILETIME ft;
  869. ULARGE_INTEGER result;
  870. SystemTimeToFileTime (SystemTime, &ft);
  871. result.LowPart = ft.dwLowDateTime;
  872. result.HighPart = ft.dwHighDateTime;
  873. return result.QuadPart;
  874. }
  875. BOOL
  876. pComputeChecksum (
  877. IN PCTSTR FileName,
  878. OUT PDWORD Chksum
  879. )
  880. {
  881. DWORD chksum, size, dwords, bytes;
  882. HANDLE hFile, hMap = NULL;
  883. PVOID viewBase = NULL;
  884. PDWORD base, limit;
  885. PBYTE base2;
  886. DWORD rc;
  887. BOOL b = FALSE;
  888. hFile = CreateFile(
  889. FileName,
  890. GENERIC_READ,
  891. FILE_SHARE_READ,
  892. NULL,
  893. OPEN_EXISTING,
  894. FILE_FLAG_SEQUENTIAL_SCAN,
  895. NULL
  896. );
  897. if(hFile == INVALID_HANDLE_VALUE) {
  898. return FALSE;
  899. }
  900. __try {
  901. size = GetFileSize (hFile, NULL);
  902. if (size == (DWORD)-1) {
  903. __leave;
  904. }
  905. hMap = CreateFileMapping (
  906. hFile,
  907. NULL,
  908. PAGE_READONLY,
  909. 0,
  910. size,
  911. NULL
  912. );
  913. if (!hMap) {
  914. __leave;
  915. }
  916. viewBase = MapViewOfFile (hMap, FILE_MAP_READ, 0, 0, size);
  917. if (!viewBase) {
  918. __leave;
  919. }
  920. dwords = size / sizeof (DWORD);
  921. base = (PDWORD)viewBase;
  922. limit = base + dwords;
  923. chksum = 0;
  924. while (base < limit) {
  925. chksum += *base;
  926. base++;
  927. }
  928. bytes = size % sizeof (DWORD);
  929. base2 = (PBYTE)base;
  930. while (bytes) {
  931. chksum += *base2;
  932. base2++;
  933. bytes--;
  934. }
  935. b = TRUE;
  936. }
  937. __finally {
  938. if (!b) {
  939. rc = GetLastError ();
  940. }
  941. if (viewBase) {
  942. UnmapViewOfFile (viewBase);
  943. }
  944. if (hMap) {
  945. CloseHandle (hMap);
  946. }
  947. CloseHandle (hFile);
  948. if (!b) {
  949. SetLastError (rc);
  950. }
  951. }
  952. if (b) {
  953. *Chksum = chksum;
  954. }
  955. return b;
  956. }
  957. BOOL
  958. pGetFiletimeStamps (
  959. IN PCTSTR FileName,
  960. OUT PFILETIME CreationTime,
  961. OUT PFILETIME LastWriteTime
  962. )
  963. {
  964. WIN32_FIND_DATA fd;
  965. HANDLE h;
  966. h = FindFirstFile (FileName, &fd);
  967. if (h == INVALID_HANDLE_VALUE) {
  968. return FALSE;
  969. }
  970. FindClose (h);
  971. *CreationTime = fd.ftCreationTime;
  972. *LastWriteTime = fd.ftLastWriteTime;
  973. return TRUE;
  974. }
  975. PTSTR
  976. pGetRecentDUShare (
  977. IN DWORD MaxElapsedSeconds
  978. )
  979. {
  980. SYSTEMTIME lastDownload, currentTime;
  981. ULONGLONG lastDownloadIn100Ns, currentTimeIn100Ns;
  982. ULONGLONG difference;
  983. DWORD rc, size, type;
  984. HKEY key = NULL;
  985. BOOL b = FALSE;
  986. PTSTR duShare = NULL;
  987. TCHAR filePath[MAX_PATH];
  988. PTSTR p;
  989. FILETIME ftCreationTime;
  990. FILETIME ftLastWriteTime;
  991. ULONGLONG data[2], storedData[2];
  992. DWORD chksum, storedChksum;
  993. if (!GetModuleFileName (NULL, filePath, MAX_PATH)) {
  994. return NULL;
  995. }
  996. p = FindLastWack (filePath);
  997. if (!p) {
  998. return NULL;
  999. }
  1000. lstrcpy (p + 1, S_CHKSUM_FILE);
  1001. rc = RegOpenKeyEx (
  1002. HKEY_LOCAL_MACHINE,
  1003. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\Winnt32\\5.1\\DUShare"),
  1004. 0,
  1005. KEY_READ,
  1006. &key
  1007. );
  1008. if (rc == ERROR_SUCCESS) {
  1009. size = sizeof (lastDownload);
  1010. rc = RegQueryValueEx (
  1011. key,
  1012. TEXT("LastDownloadTime"),
  1013. NULL,
  1014. &type,
  1015. (PBYTE) (&lastDownload),
  1016. &size
  1017. );
  1018. }
  1019. if (rc == ERROR_SUCCESS && type == REG_BINARY && size == sizeof (lastDownload)) {
  1020. //
  1021. // Compare current time to report time
  1022. //
  1023. GetSystemTime (&currentTime);
  1024. lastDownloadIn100Ns = SystemTimeToFileTime64 (&lastDownload);
  1025. currentTimeIn100Ns = SystemTimeToFileTime64 (&currentTime);
  1026. if (currentTimeIn100Ns > lastDownloadIn100Ns) {
  1027. //
  1028. // Compute difference in seconds
  1029. //
  1030. difference = currentTimeIn100Ns - lastDownloadIn100Ns;
  1031. difference /= (10 * 1000 * 1000);
  1032. if (difference < MaxElapsedSeconds) {
  1033. b = TRUE;
  1034. }
  1035. }
  1036. }
  1037. if (b) {
  1038. rc = RegQueryValueEx (
  1039. key,
  1040. TEXT(""),
  1041. NULL,
  1042. &type,
  1043. NULL,
  1044. &size
  1045. );
  1046. if (rc == ERROR_SUCCESS && type == REG_SZ && size > 0) {
  1047. duShare = ALLOC_TEXT (size / sizeof (TCHAR));
  1048. if (duShare) {
  1049. rc = RegQueryValueEx (
  1050. key,
  1051. TEXT(""),
  1052. NULL,
  1053. NULL,
  1054. (LPBYTE)duShare,
  1055. &size
  1056. );
  1057. if (rc != ERROR_SUCCESS || !DoesDirExist (duShare)) {
  1058. FREE (duShare);
  1059. duShare = NULL;
  1060. }
  1061. }
  1062. }
  1063. }
  1064. if (duShare) {
  1065. b = FALSE;
  1066. if (pGetFiletimeStamps (filePath, &ftCreationTime, &ftLastWriteTime)) {
  1067. rc = RegQueryValueEx (
  1068. key,
  1069. TEXT("TimeStamp"),
  1070. 0,
  1071. &type,
  1072. (LPBYTE)storedData,
  1073. &size
  1074. );
  1075. if (rc == ERROR_SUCCESS && type == REG_BINARY) {
  1076. data[0] = ((ULONGLONG)ftCreationTime.dwHighDateTime << 32) | (ULONGLONG)ftCreationTime.dwLowDateTime;
  1077. data[1] = ((ULONGLONG)ftLastWriteTime.dwHighDateTime << 32 ) | (ULONGLONG)ftLastWriteTime.dwLowDateTime;
  1078. if (data[0] == storedData[0] && data[1] == storedData[1]) {
  1079. b = TRUE;
  1080. }
  1081. }
  1082. }
  1083. if (b) {
  1084. b = FALSE;
  1085. if (pComputeChecksum (filePath, &chksum)) {
  1086. rc = RegQueryValueEx (
  1087. key,
  1088. TEXT("Checksum"),
  1089. NULL,
  1090. &type,
  1091. (LPBYTE)&storedChksum,
  1092. &size
  1093. );
  1094. if (rc == ERROR_SUCCESS && type == REG_DWORD && storedChksum == chksum) {
  1095. b = TRUE;
  1096. }
  1097. }
  1098. }
  1099. if (!b) {
  1100. FREE (duShare);
  1101. duShare = NULL;
  1102. }
  1103. }
  1104. if (key) {
  1105. RegCloseKey (key);
  1106. }
  1107. return duShare;
  1108. }
  1109. void
  1110. _stdcall
  1111. ModuleEntry(
  1112. VOID
  1113. )
  1114. /*++
  1115. Routine Description:
  1116. ModuleEntry is the stub program that loads Windows 2000 Setup DLLs.
  1117. Arguments:
  1118. none
  1119. Return Value:
  1120. none. ExitProcess will set the process' exit code.
  1121. --*/
  1122. {
  1123. TCHAR RunningInstancePath[MAX_PATH];
  1124. TCHAR Temp[MAX_PATH];
  1125. TCHAR Text1[MAX_PATH];
  1126. TCHAR Text2[MAX_PATH+MAX_PATH];
  1127. TCHAR Text3[MAX_PATH];
  1128. TCHAR *WackExeName, *p;
  1129. TCHAR winnt32DllPath[MAX_PATH];
  1130. HMODULE WinNT32;
  1131. BOOL Downloaded;
  1132. DWORD d;
  1133. BOOL b;
  1134. HWND Dlg = NULL;
  1135. HANDLE WinNT32Stub = NULL;
  1136. PWINNT32 winnt32;
  1137. HKEY key;
  1138. DWORD type;
  1139. PCTSTR moduleName;
  1140. PSTR restartCmdLine = NULL;
  1141. PTSTR RemainingArgs, NewCmdLine, UnattendPrefix, UnattendFileName;
  1142. PTSTR DynamicUpdatesShare;
  1143. BOOL Cleanup, NoDownload, DisableDynamicUpdates, LocalWinnt32, CheckUpgradeOnly;
  1144. PTSTR RestartAnswerFile;
  1145. UINT CmdLineLen;
  1146. PTSTR FileName;
  1147. PCTSTR ExtraFiles[2];
  1148. TCHAR cdFilePath[MAX_PATH];
  1149. PTSTR duShare = NULL;
  1150. #ifdef _X86_
  1151. TCHAR DownloadDest[MAX_PATH] = TEXT("");
  1152. TCHAR DefSourcesDir[MAX_PATH];
  1153. BOOL IsWin9x;
  1154. //
  1155. // Check OS version. Disallow Win32s and NT < 4.00
  1156. //
  1157. d = GetVersion();
  1158. if((d & 0xff) < 4) {
  1159. LoadString (GetModuleHandle (NULL), IDS_VERERROR, Text1, sizeof(Text1)/sizeof(TCHAR));
  1160. LoadString (GetModuleHandle (NULL), IDS_APPNAME, Text2, sizeof(Text2)/sizeof(TCHAR));
  1161. MessageBox (NULL, Text1, Text2, MB_ICONERROR | MB_OK | MB_SYSTEMMODAL);
  1162. ExitProcess (ERROR_OLD_WIN_VERSION);
  1163. }
  1164. IsWin9x = (d & 0x80000000) != 0;
  1165. #else
  1166. #define IsWin9x ((BOOL)FALSE)
  1167. #endif
  1168. //
  1169. // get this instance's path
  1170. //
  1171. if (!GetModuleFileName(NULL, RunningInstancePath, MAX_PATH)) {
  1172. ExitProcess (GetLastError ());
  1173. }
  1174. WackExeName = FindLastWack (RunningInstancePath);
  1175. if (!WackExeName) { // shut up PREfix. This case should never happen.
  1176. ExitProcess (ERROR_BAD_PATHNAME);
  1177. }
  1178. //
  1179. // Ansi version on Win95. Unicode on NT.
  1180. //
  1181. moduleName = IsWin9x ? TEXT("WINNT32A.DLL") : TEXT("WINNT32U.DLL");
  1182. winnt32DllPath[0] = 0;
  1183. //
  1184. // get command line options
  1185. // allocate a bigger buffer for safety
  1186. //
  1187. RemainingArgs = ALLOC_TEXT(lstrlen(GetCommandLine()) * 2);
  1188. if (!RemainingArgs) {
  1189. ExitProcess (GetLastError ());
  1190. }
  1191. GetCmdLineArgs (
  1192. GetCommandLine (),
  1193. &Cleanup,
  1194. &NoDownload,
  1195. &UnattendPrefix,
  1196. &UnattendFileName,
  1197. &DisableDynamicUpdates,
  1198. &DynamicUpdatesShare,
  1199. &RestartAnswerFile,
  1200. &LocalWinnt32,
  1201. &CheckUpgradeOnly,
  1202. RemainingArgs
  1203. );
  1204. #ifdef _X86_
  1205. if (Cleanup) {
  1206. pCleanup ();
  1207. ExitProcess (0);
  1208. }
  1209. if (IsWin9x) {
  1210. WinNT32Stub = CreateEvent (NULL, FALSE, FALSE, TEXT("_WinNT32_Stub_"));
  1211. if (!WinNT32Stub) {
  1212. ExitProcess (GetLastError ());
  1213. }
  1214. b = (GetLastError() == ERROR_SUCCESS);
  1215. if (!NoDownload && !DynamicUpdatesShare && pShouldDownloadToLocalDisk (RunningInstancePath)) {
  1216. Dlg = CreateDialog (GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_SETUPINIT), NULL, DlgProc);
  1217. GetWindowsDirectory (DownloadDest, MAX_PATH);
  1218. lstrcat (DownloadDest, INTERNAL_WINNT32_DIR);
  1219. *WackExeName = 0;
  1220. if (UnattendFileName &&
  1221. GetFullPathName (UnattendFileName, MAX_PATH, Temp, &FileName) &&
  1222. lstrcmpi (UnattendFileName, Temp)
  1223. ) {
  1224. ExtraFiles[0] = Temp;
  1225. ExtraFiles[1] = NULL;
  1226. } else {
  1227. ExtraFiles[0] = NULL;
  1228. FileName = UnattendFileName;
  1229. }
  1230. Downloaded = DownloadProgramFiles (
  1231. RunningInstancePath,
  1232. DownloadDest,
  1233. ExtraFiles
  1234. );
  1235. *WackExeName = TEXT('\\');
  1236. if (Downloaded) {
  1237. //
  1238. // get default sources dir
  1239. //
  1240. lstrcpy (DefSourcesDir, RunningInstancePath);
  1241. *FindLastWack (DefSourcesDir) = 0;
  1242. p = FindLastWack (DefSourcesDir);
  1243. if (p && lstrcmpi(p, INTERNAL_WINNT32_DIR) == 0) {
  1244. *p = 0;
  1245. }
  1246. if (FileName) {
  1247. CmdLineLen = lstrlen (RemainingArgs);
  1248. if (CmdLineLen > 0) {
  1249. //
  1250. // count the space between args
  1251. //
  1252. CmdLineLen += CHARS(" ");
  1253. }
  1254. CmdLineLen += lstrlen (UnattendPrefix);
  1255. CmdLineLen += lstrlen (FileName);
  1256. NewCmdLine = ALLOC_TEXT(CmdLineLen);
  1257. if (NewCmdLine) {
  1258. if (*RemainingArgs) {
  1259. lstrcpy (NewCmdLine, RemainingArgs);
  1260. lstrcat (NewCmdLine, TEXT(" "));
  1261. } else {
  1262. *NewCmdLine = 0;
  1263. }
  1264. lstrcat (NewCmdLine, UnattendPrefix);
  1265. lstrcat (NewCmdLine, FileName);
  1266. FREE (RemainingArgs);
  1267. RemainingArgs = NewCmdLine;
  1268. NewCmdLine = NULL;
  1269. }
  1270. }
  1271. //
  1272. // append /LOCAL to the new processes command line
  1273. // to let it know it's running from a local share
  1274. //
  1275. NewCmdLine = ALLOC_TEXT(lstrlen (RemainingArgs) + sizeof (" /local") - 1);
  1276. if (NewCmdLine) {
  1277. wsprintf (NewCmdLine, TEXT("%s /%s"), RemainingArgs, TEXT("LOCAL"));
  1278. if (pReRun (DownloadDest, WackExeName, NewCmdLine, DefSourcesDir)) {
  1279. //
  1280. // the new process will do it; this one will just die
  1281. // but after the signal that the Setup Wizard is on
  1282. // anyway, if something goes very wrong,
  1283. // don't wait more than 10 sec.
  1284. // this should be enough for the wizard to appear
  1285. // (or any error message box) on any machine that installs W2K
  1286. //
  1287. WaitForSingleObject (WinNT32Stub, 10000);
  1288. CloseHandle (WinNT32Stub);
  1289. if (Dlg) {
  1290. DestroyWindow (Dlg);
  1291. }
  1292. d = 0;
  1293. } else {
  1294. d = GetLastError ();
  1295. }
  1296. } else {
  1297. d = ERROR_NOT_ENOUGH_MEMORY;
  1298. }
  1299. ExitProcess (d);
  1300. }
  1301. }
  1302. if (!Dlg && WinNT32Stub) {
  1303. CloseHandle (WinNT32Stub);
  1304. WinNT32Stub = NULL;
  1305. }
  1306. }
  1307. #endif
  1308. if (RemainingArgs) {
  1309. FREE(RemainingArgs);
  1310. RemainingArgs = NULL;
  1311. }
  1312. if (UnattendPrefix) {
  1313. FREE(UnattendPrefix);
  1314. UnattendPrefix = NULL;
  1315. }
  1316. if (!DisableDynamicUpdates && !DynamicUpdatesShare) {
  1317. PCTSTR af = NULL;
  1318. if (RestartAnswerFile) {
  1319. af = RestartAnswerFile;
  1320. } else if (UnattendFileName) {
  1321. if (GetFullPathName (UnattendFileName, MAX_PATH, Temp, &FileName)) {
  1322. af = Temp;
  1323. }
  1324. }
  1325. //
  1326. // get the path from this answer file
  1327. //
  1328. if (af) {
  1329. GetPrivateProfileString (
  1330. WINNT_UNATTENDED,
  1331. WINNT_U_DYNAMICUPDATESDISABLE,
  1332. TEXT(""),
  1333. Text2,
  1334. MAX_PATH,
  1335. af
  1336. );
  1337. DisableDynamicUpdates = !lstrcmpi (Text2, WINNT_A_YES);
  1338. if (!DisableDynamicUpdates) {
  1339. if (GetPrivateProfileString (
  1340. WINNT_UNATTENDED,
  1341. WINNT_U_DYNAMICUPDATESHARE,
  1342. TEXT(""),
  1343. Text2,
  1344. MAX_PATH,
  1345. af
  1346. )) {
  1347. DynamicUpdatesShare = DuplicateText (Text2);
  1348. }
  1349. }
  1350. }
  1351. }
  1352. if (UnattendFileName) {
  1353. FREE(UnattendFileName);
  1354. UnattendFileName = NULL;
  1355. }
  1356. b = FALSE;
  1357. if (!CheckUpgradeOnly && !DisableDynamicUpdates && !DynamicUpdatesShare) {
  1358. DynamicUpdatesShare = pGetRecentDUShare (MAX_UPGCHK_ELAPSED_SECONDS);
  1359. if (DynamicUpdatesShare) {
  1360. b = TRUE;
  1361. }
  1362. }
  1363. d = ERROR_SUCCESS;
  1364. if (!DisableDynamicUpdates && DynamicUpdatesShare) {
  1365. DWORD regFileVersionMS, regFileVersionLS;
  1366. DWORD cdFileVersionMS, cdFileVersionLS;
  1367. //
  1368. // check if there is a replacement module newer than the CD version
  1369. //
  1370. if (GetFileAttributes (DynamicUpdatesShare) == (DWORD)-1) {
  1371. if (!b) {
  1372. d = GetLastError ();
  1373. LoadString (GetModuleHandle (NULL), IDS_APPNAME, Text3, sizeof(Text3)/sizeof(TCHAR));
  1374. LoadString (GetModuleHandle (NULL), IDS_PATHERROR, Text1, sizeof(Text1)/sizeof(TCHAR));
  1375. wsprintf (Text2, Text1, DynamicUpdatesShare);
  1376. MessageBox (NULL, Text2, Text3, MB_ICONERROR | MB_OK | MB_SYSTEMMODAL);
  1377. }
  1378. } else {
  1379. wsprintf (Text2, TEXT("%s%s\\%s"), DynamicUpdatesShare, TEXT("\\WINNT32"), moduleName);
  1380. if (GetFileAttributes (Text2) != (DWORD)-1 &&
  1381. GetFileVersion (Text2, &regFileVersionMS, &regFileVersionLS)) {
  1382. lstrcpyn (cdFilePath, RunningInstancePath, (INT)(WackExeName - RunningInstancePath + 2));
  1383. lstrcat (cdFilePath, moduleName);
  1384. if (GetFileVersion (cdFilePath, &cdFileVersionMS, &cdFileVersionLS)) {
  1385. if (MAKEULONGLONG(regFileVersionLS, regFileVersionMS) >
  1386. MAKEULONGLONG(cdFileVersionLS, cdFileVersionMS)) {
  1387. lstrcpy (winnt32DllPath, Text2);
  1388. }
  1389. }
  1390. }
  1391. }
  1392. FREE (DynamicUpdatesShare);
  1393. DynamicUpdatesShare = NULL;
  1394. }
  1395. if (d == ERROR_SUCCESS) {
  1396. #ifdef _X86_
  1397. //
  1398. // before attempting to load the main module, make sure msvcrt.dll is present in the system dir
  1399. //
  1400. if (!GetSystemDirectory (Text1, MAX_PATH)) {
  1401. ExitProcess (GetLastError ());
  1402. }
  1403. ConcatenatePaths (Text1, TEXT("msvcrt.dll"));
  1404. d = GetFileAttributes (Text1);
  1405. if (d == (DWORD)-1) {
  1406. //
  1407. // no local msvcrt.dll; copy the private file from CD
  1408. //
  1409. lstrcpyn (cdFilePath, RunningInstancePath, WackExeName - RunningInstancePath + 2);
  1410. ConcatenatePaths (cdFilePath, TEXT("win9xupg\\msvcrt.dll"));
  1411. if (!CopyFile (cdFilePath, Text1, TRUE)) {
  1412. ExitProcess (GetLastError ());
  1413. }
  1414. } else if (d & FILE_ATTRIBUTE_DIRECTORY) {
  1415. ExitProcess (ERROR_DIRECTORY);
  1416. }
  1417. #endif
  1418. *WackExeName = 0;
  1419. if (!winnt32DllPath[0]) {
  1420. lstrcpy (winnt32DllPath, RunningInstancePath);
  1421. ConcatenatePaths (winnt32DllPath, moduleName);
  1422. }
  1423. b = FALSE;
  1424. WinNT32 = LoadLibrary (winnt32DllPath);
  1425. if(WinNT32) {
  1426. winnt32 = (PWINNT32) GetProcAddress(WinNT32, "winnt32");
  1427. if (winnt32) {
  1428. d = (*winnt32) (LocalWinnt32 ? RunningInstancePath : NULL, Dlg, WinNT32Stub, &restartCmdLine);
  1429. b = TRUE;
  1430. }
  1431. FreeLibrary (WinNT32);
  1432. }
  1433. if (!b) {
  1434. d = GetLastError ();
  1435. LoadString (GetModuleHandle (NULL), IDS_APPNAME, Text3, sizeof(Text3)/sizeof(TCHAR));
  1436. LoadString (GetModuleHandle (NULL), IDS_DLLERROR, Text1, sizeof(Text1)/sizeof(TCHAR));
  1437. wsprintf (Text2, Text1, winnt32DllPath);
  1438. MessageBox (NULL, Text2, Text3, MB_ICONERROR | MB_OK | MB_SYSTEMMODAL);
  1439. }
  1440. }
  1441. //
  1442. // remove downloaded files
  1443. //
  1444. #ifdef _X86_
  1445. if (IsWin9x) {
  1446. //
  1447. // check if our local directory exists and if so delete it
  1448. //
  1449. if (LocalWinnt32 && GetFileAttributes (RunningInstancePath) != (DWORD)-1) {
  1450. //
  1451. // copy Winnt32.Exe to temp dir and rerun it from there with /CLEANUP option
  1452. //
  1453. lstrcpy (DefSourcesDir, RunningInstancePath);
  1454. CmdLineLen = GetTempPath (MAX_PATH, DownloadDest);
  1455. if (!CmdLineLen) {
  1456. //
  1457. // an error occured; copy it to %windir% instead
  1458. //
  1459. GetWindowsDirectory (DownloadDest, MAX_PATH);
  1460. }
  1461. //
  1462. // make sure temp path doesn't end in backslash
  1463. //
  1464. p = FindLastWack (DownloadDest);
  1465. if (p && *(p + 1) == 0) {
  1466. *p = 0;
  1467. }
  1468. *WackExeName = TEXT('\\');
  1469. if (CopyNode (DefSourcesDir, DownloadDest, WackExeName, FALSE)) {
  1470. if (!pReRun (DownloadDest, WackExeName, TEXT("/CLEANUP"), NULL)) {
  1471. lstrcatA (DownloadDest, WackExeName);
  1472. DeleteNode (DownloadDest);
  1473. }
  1474. }
  1475. }
  1476. }
  1477. #endif
  1478. if (d == ERROR_SUCCESS) {
  1479. //
  1480. // check if a restart request was made
  1481. //
  1482. if (restartCmdLine) {
  1483. STARTUPINFOA startupInfo;
  1484. PROCESS_INFORMATION pi;
  1485. ZeroMemory(&startupInfo, sizeof(startupInfo));
  1486. startupInfo.cb = sizeof(startupInfo);
  1487. if (!CreateProcessA (
  1488. NULL,
  1489. restartCmdLine,
  1490. NULL,
  1491. NULL,
  1492. FALSE,
  1493. NORMAL_PRIORITY_CLASS,
  1494. NULL,
  1495. NULL,
  1496. &startupInfo,
  1497. &pi
  1498. )) {
  1499. d = GetLastError ();
  1500. }
  1501. FREE (restartCmdLine);
  1502. }
  1503. }
  1504. ExitProcess(d);
  1505. }