Leaked source code of windows server 2003
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

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