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.

966 lines
24 KiB

  1. #include "precomp.h"
  2. #pragma hdrstop
  3. #include <tchar.h>
  4. #include <stdlib.h>
  5. #include <CRTDBG.H>
  6. #include <winuserp.h>
  7. #if DBG
  8. VOID
  9. AssertFail(
  10. IN PSTR FileName,
  11. IN UINT LineNumber,
  12. IN PSTR Condition
  13. )
  14. {
  15. int i;
  16. CHAR Name[MAX_PATH];
  17. PCHAR p;
  18. CHAR Msg[4096];
  19. //
  20. // Use dll name as caption
  21. //
  22. GetModuleFileNameA(NULL,Name,MAX_PATH);
  23. if(p = strrchr(Name,'\\')) {
  24. p++;
  25. } else {
  26. p = Name;
  27. }
  28. wsprintfA(
  29. Msg,
  30. "Assertion failure at line %u in file %s: %s\n\nCall DebugBreak()?",
  31. LineNumber,
  32. FileName,
  33. Condition
  34. );
  35. OutputDebugStringA(Msg);
  36. i = MessageBoxA(
  37. NULL,
  38. Msg,
  39. p,
  40. MB_YESNO | MB_TASKMODAL | MB_ICONSTOP | MB_SETFOREGROUND
  41. );
  42. if(i == IDYES) {
  43. DebugBreak();
  44. }
  45. }
  46. #define MYASSERT(x) if(!(x)) { AssertFail(__FILE__,__LINE__,#x); }
  47. #else
  48. #define MYASSERT( exp )
  49. #endif // DBG
  50. //
  51. // App instance.
  52. //
  53. HINSTANCE hInst;
  54. //
  55. // Global version information structure.
  56. //
  57. OSVERSIONINFO OsVersionInfo;
  58. //
  59. // Specification of master inf, from command line.
  60. //
  61. TCHAR InfPath[MAX_PATH];
  62. TCHAR InfDir[MAX_PATH];
  63. //
  64. // Source path for installation files, etc.
  65. //
  66. TCHAR SourcePath[MAX_PATH];
  67. TCHAR UnattendPath[MAX_PATH];
  68. // If Unattened
  69. BOOL bUnattendInstall;
  70. //
  71. // Whether to force the specified master inf to be treated as new
  72. // (from command line)
  73. //
  74. BOOL ForceNewInf;
  75. //
  76. // whether we need to pass language callback to components
  77. //
  78. BOOL LanguageAware;
  79. //
  80. // Whether to run without UI
  81. //
  82. BOOL QuietMode;
  83. //
  84. // Whether to delete all subcomponent entries listed in the master inf
  85. // (from command line)
  86. //
  87. BOOL KillSubcomponentEntries;
  88. // If set and /U then reboot is suppressed
  89. BOOL bNoReboot;
  90. // if this is set and we're running /unattend, then warn on reboot
  91. BOOL bWarnOnReboot;
  92. // if this is set then we want sysocmgr.exe to enforce the admin check.
  93. BOOL bDoAdminCheck = FALSE;
  94. // Flag for Derminineing Starting or Ending message
  95. BOOL bStarting;
  96. //
  97. // OC Manager context 'handle'
  98. //
  99. PVOID OcManagerContext;
  100. //
  101. // Generic app title string id.
  102. //
  103. UINT AppTitleStringId;
  104. BOOL NeedToReboot;
  105. BOOL SkipBillboard;
  106. BOOL ForceExternalProgressIndicator;
  107. BOOL AllowCancel = TRUE;
  108. VOID
  109. OcSetReboot(
  110. VOID
  111. );
  112. //
  113. // Callback routines.
  114. //
  115. OCM_CLIENT_CALLBACKS CallbackRoutines = {
  116. OcFillInSetupDataA,
  117. OcLogError,
  118. OcSetReboot
  119. #ifdef UNICODE
  120. ,OcFillInSetupDataW
  121. #endif
  122. ,NULL, // No callback for show,hide wizard
  123. NULL, // No callback for progress feedback, they are only needed for setup
  124. NULL, // No callback to set the progress text
  125. NULL // No logging callback.
  126. };
  127. BOOL
  128. DoIt(
  129. VOID
  130. );
  131. BOOL
  132. ParseArgs(
  133. IN int argc,
  134. IN TCHAR *argv[]
  135. );
  136. DWORD
  137. ExpandPath(
  138. IN LPCTSTR lpFileName,
  139. OUT LPTSTR lpBuffer,
  140. OUT LPTSTR *lpFilePart
  141. );
  142. void
  143. ShutDown()
  144. {
  145. extern void RestartDialogEx(VOID *, VOID *, DWORD, DWORD);
  146. if (!bNoReboot) {
  147. if ( bUnattendInstall && !bWarnOnReboot ) {
  148. //
  149. // NT is always UNICODE and W9x is alwasy Ansii
  150. //
  151. #ifdef UNICODE
  152. HANDLE hToken; TOKEN_PRIVILEGES tkp; // Get a token for this process.
  153. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken))
  154. sapiAssert("OpenProcessToken"); // Get the LUID for the shutdown privilege.
  155. LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
  156. tkp.PrivilegeCount = 1; // one privilege to set
  157. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  158. // Get the shutdown privilege for this process.
  159. AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
  160. // Cannot test the return value of AdjustTokenPrivileges.
  161. if (GetLastError() == ERROR_SUCCESS) {
  162. sapiAssert("AdjustTokenPrivileges");
  163. }
  164. #endif
  165. //
  166. // Shut down the system and force all applications to close.
  167. //
  168. if (! ExitWindowsEx(EWX_REBOOT|EWX_FORCE , 0) ) {
  169. _RPT0(_CRT_WARN,"Sysocmgr:Failed to ExitWindows");
  170. sapiAssert(FALSE);
  171. }
  172. } else {
  173. RestartDialogEx(NULL,NULL,EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG \
  174. | SHTDN_REASON_FLAG_PLANNED );
  175. }
  176. }
  177. }
  178. VOID
  179. __cdecl
  180. #ifdef UNICODE
  181. wmain(
  182. #else
  183. main(
  184. #endif
  185. IN int argc,
  186. IN TCHAR *argv[]
  187. )
  188. {
  189. INITCOMMONCONTROLSEX ControlInit;
  190. //
  191. // Preliminaries
  192. //
  193. ControlInit.dwSize = sizeof(INITCOMMONCONTROLSEX);
  194. ControlInit.dwICC = ICC_LISTVIEW_CLASSES |
  195. ICC_TREEVIEW_CLASSES |
  196. ICC_BAR_CLASSES |
  197. ICC_TAB_CLASSES |
  198. ICC_UPDOWN_CLASS |
  199. ICC_PROGRESS_CLASS |
  200. ICC_HOTKEY_CLASS |
  201. ICC_ANIMATE_CLASS |
  202. ICC_WIN95_CLASSES |
  203. ICC_DATE_CLASSES |
  204. ICC_USEREX_CLASSES |
  205. ICC_COOL_CLASSES;
  206. #if (_WIN32_IE >= 0x0400)
  207. ControlInit.dwICC = ControlInit.dwICC |
  208. ICC_INTERNET_CLASSES |
  209. ICC_PAGESCROLLER_CLASS;
  210. #endif
  211. InitCommonControlsEx( &ControlInit );
  212. hInst = GetModuleHandle(NULL);
  213. OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  214. GetVersionEx(&OsVersionInfo);
  215. AppTitleStringId = IS_NT() ? IDS_WINNT_SETUP : IDS_WIN9X_SETUP;
  216. //
  217. // Parse arguments and do it.
  218. //
  219. if (ParseArgs(argc,argv)) {
  220. DoIt();
  221. }
  222. //
  223. // If we need to reboot, do that now.
  224. //
  225. if (NeedToReboot) {
  226. ShutDown();
  227. }
  228. }
  229. BOOL
  230. ParseArgs(
  231. IN int argc,
  232. IN TCHAR *argv[]
  233. )
  234. /*++
  235. Routine Description:
  236. Parse and syntactically validate arguments specified on the comment line.
  237. The following arguments are valid:
  238. /a forces the external progress indicator on the setup page
  239. /c disallow cancel during final installation phase
  240. /i:<master_oc_inf> specifies the master OC inf (required).
  241. /n forces specified master_oc_inf to be treated as new.
  242. /s:<master_oc_inf> specifies the source path (required).
  243. /u:<unattend_spec> specifies unattended operation parameters.
  244. /x supresses the 'initializing' banner
  245. /q run wizard invisibly
  246. /r supress reboot if need on unattended operation
  247. /w warn on reboot on unattended operation
  248. Arguments:
  249. Standard main argc/argv.
  250. Return Value:
  251. Boolean value indicating whether the arguments specified are valid.
  252. If successful, various global variables will have been filled in.
  253. If not, the user will have been informed.
  254. --*/
  255. {
  256. BOOL Valid;
  257. LPCTSTR SourcePathSpec = NULL;
  258. LPCTSTR InfSpec = NULL;
  259. LPCTSTR UnattendSpec = NULL;
  260. LPTSTR FilePart;
  261. DWORD u;
  262. //
  263. // Skip program name.
  264. //
  265. if (argc) {
  266. argc--;
  267. }
  268. Valid = TRUE;
  269. ForceNewInf = FALSE;
  270. QuietMode = FALSE;
  271. KillSubcomponentEntries = FALSE;
  272. while (Valid && argc--) {
  273. argv++;
  274. if ((argv[0][0] == TEXT('-')) || (argv[0][0] == TEXT('/'))) {
  275. switch (argv[0][1]) {
  276. case TEXT('a'):
  277. case TEXT('A'):
  278. if (!ForceExternalProgressIndicator && !argv[0][2]) {
  279. ForceExternalProgressIndicator = TRUE;
  280. } else {
  281. Valid = FALSE;
  282. }
  283. break;
  284. case TEXT('c'):
  285. case TEXT('C'):
  286. if (AllowCancel && !argv[0][2]) {
  287. AllowCancel = FALSE;
  288. } else {
  289. Valid = FALSE;
  290. }
  291. break;
  292. case TEXT('f'):
  293. case TEXT('F'):
  294. ForceNewInf = TRUE;
  295. KillSubcomponentEntries = TRUE;
  296. break;
  297. case TEXT('i'):
  298. case TEXT('I'):
  299. if (!InfSpec && (argv[0][2] == TEXT(':')) && argv[0][3]) {
  300. InfSpec = &(argv[0][3]);
  301. } else {
  302. Valid = FALSE;
  303. }
  304. break;
  305. case TEXT('l'):
  306. case TEXT('L'):
  307. LanguageAware = TRUE;
  308. break;
  309. case TEXT('n'):
  310. case TEXT('N'):
  311. ForceNewInf = TRUE;
  312. break;
  313. case TEXT('q'):
  314. case TEXT('Q'):
  315. if (!QuietMode && !argv[0][2]) {
  316. QuietMode = TRUE;
  317. SkipBillboard = TRUE;
  318. } else {
  319. Valid = FALSE;
  320. }
  321. break;
  322. case TEXT('r'):
  323. case TEXT('R'):
  324. bNoReboot = TRUE;
  325. break;
  326. case TEXT('s'):
  327. case TEXT('S'):
  328. if (!SourcePathSpec && (argv[0][2] == TEXT(':')) && argv[0][3]) {
  329. SourcePathSpec = &argv[0][3];
  330. } else {
  331. Valid = FALSE;
  332. }
  333. break;
  334. case TEXT('u'):
  335. case TEXT('U'):
  336. //
  337. // accept unattend, unattended, u all as the same
  338. //
  339. if(!_tcsnicmp(&argv[0][1],TEXT("unattended"),10)) {
  340. u = 11;
  341. } else if(!_tcsnicmp(&argv[0][1],TEXT("unattend"),8)) {
  342. u = 9;
  343. } else if(!_tcsnicmp(&argv[0][1],TEXT("u"),1)) {
  344. u = 2;
  345. } else {
  346. Valid = FALSE;
  347. u = 0;
  348. }
  349. if (!UnattendSpec ) {
  350. bUnattendInstall = TRUE;
  351. // If you have the : then you must also have the arg
  352. if (argv[0][u] == TEXT(':')) {
  353. if ( argv[0][u+1]) {
  354. UnattendSpec = &argv[0][u+1];
  355. } else {
  356. Valid = FALSE;
  357. }
  358. } else {
  359. Valid = FALSE;
  360. }
  361. } else {
  362. Valid = FALSE;
  363. }
  364. break;
  365. case TEXT('w'):
  366. case TEXT('W'):
  367. bWarnOnReboot = TRUE;
  368. break;
  369. case TEXT('x'):
  370. case TEXT('X'):
  371. if (!SkipBillboard && !argv[0][2]) {
  372. SkipBillboard = TRUE;
  373. } else {
  374. Valid = FALSE;
  375. }
  376. break;
  377. // For ISSUE NTBUG9:295052 (389583): We want to do a top level admin check so we get a more friendly message.
  378. // It is possible for people to have been using sysocmgr.exe with their own custom master oc.inf
  379. // (the one passed in with the /i: switch) and they may not need this admin check. So, we did
  380. // not want to do this admin check unconditionally. We will have the control panel applet that
  381. // is launching sysocmgr.exe to pass in this /y switch.
  382. //
  383. case TEXT('y'):
  384. case TEXT('Y'):
  385. bDoAdminCheck = TRUE;
  386. break;
  387. case TEXT('z'):
  388. case TEXT('Z'):
  389. // Stop parsing Arguments All other args past this point are
  390. // Component Arguments
  391. argc = 0;
  392. break;
  393. default:
  394. Valid = FALSE;
  395. break;
  396. }
  397. } else {
  398. Valid = FALSE;
  399. }
  400. }
  401. if (Valid && !InfSpec) {
  402. Valid = FALSE;
  403. }
  404. if (Valid) {
  405. //
  406. // Expand the inf spec to a full path.
  407. //
  408. ExpandPath(InfSpec,InfPath,&FilePart);
  409. _tcscpy(InfDir, InfSpec);
  410. if (_tcsrchr(InfDir, TEXT('\\')))
  411. *_tcsrchr(InfDir,TEXT('\\')) = 0;
  412. else
  413. GetCurrentDirectory(MAX_PATH, InfDir);
  414. // If the user specified /s then expand it too, otherwise
  415. // use the dir in the /i as the /s arg.
  416. if (SourcePathSpec) {
  417. ExpandPath(SourcePathSpec,SourcePath,&FilePart);
  418. } else {
  419. lstrcpy(SourcePath,InfPath);
  420. if (_tcsrchr(SourcePath,TEXT('\\'))) {
  421. *_tcsrchr(SourcePath,TEXT('\\')) = 0;
  422. }
  423. }
  424. SetCurrentDirectory(InfDir);
  425. if (UnattendSpec) {
  426. ExpandPath(UnattendSpec,UnattendPath,&FilePart);
  427. }else{
  428. // Allow /Q only if /U was specified
  429. QuietMode = FALSE;
  430. SkipBillboard = FALSE;
  431. }
  432. } else {
  433. MessageBoxFromMessage(
  434. NULL,
  435. MSG_ARGS,
  436. FALSE,
  437. MAKEINTRESOURCE(AppTitleStringId),
  438. MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL
  439. );
  440. }
  441. return (Valid);
  442. }
  443. INT_PTR
  444. BillboardDlgProc(
  445. IN HWND hdlg,
  446. IN UINT msg,
  447. IN WPARAM wParam,
  448. IN LPARAM lParam
  449. )
  450. {
  451. BOOL b;
  452. RECT rect1,rect2;
  453. static HCURSOR hOldCursor;
  454. switch (msg) {
  455. case WM_INITDIALOG:
  456. //
  457. // Center on-screen.
  458. //
  459. GetWindowRect(hdlg,&rect1);
  460. SystemParametersInfo(SPI_GETWORKAREA,0,&rect2,0);
  461. MoveWindow(
  462. hdlg,
  463. rect2.left + (((rect2.right - rect2.left) - (rect1.right - rect1.left)) / 2),
  464. rect2.top + (((rect2.bottom - rect2.top) - (rect1.bottom - rect1.top)) / 2),
  465. rect1.right - rect1.left,
  466. rect1.bottom - rect1.top,
  467. FALSE
  468. );
  469. *(HWND *)lParam = hdlg;
  470. b = TRUE;
  471. hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  472. break;
  473. case WM_APP:
  474. EndDialog(hdlg,0);
  475. SetCursor( hOldCursor );
  476. b = TRUE;
  477. break;
  478. default:
  479. b = FALSE;
  480. break;
  481. }
  482. return (b);
  483. }
  484. DWORD
  485. DisplayMessage(
  486. IN LPVOID ThreadParameter
  487. )
  488. {
  489. int i;
  490. i = (int)DialogBoxParam(
  491. hInst,
  492. MAKEINTRESOURCE(bStarting?IDD_STARTING:IDD_FINISHING),
  493. NULL,
  494. BillboardDlgProc,
  495. (LPARAM)ThreadParameter
  496. );
  497. if (i == -1) {
  498. //
  499. // Force caller out of wait loop
  500. //
  501. *(HWND *)ThreadParameter = (HWND)(-1);
  502. }
  503. return (0);
  504. }
  505. /*---------------------------------------------------------------------------*\
  506. Function: RunningAsAdministrator()
  507. |*---------------------------------------------------------------------------*|
  508. Description: Checks whether we are running as administrator on the machine
  509. or not.
  510. Code taken from ntoc.dll
  511. \*---------------------------------------------------------------------------*/
  512. BOOL
  513. RunningAsAdministrator(
  514. VOID
  515. )
  516. {
  517. BOOL fAdmin;
  518. HANDLE hThread;
  519. TOKEN_GROUPS *ptg = NULL;
  520. DWORD cbTokenGroups;
  521. DWORD dwGroup;
  522. PSID psidAdmin;
  523. SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
  524. // First we must open a handle to the access token for this thread.
  525. if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread))
  526. {
  527. if ( GetLastError() == ERROR_NO_TOKEN)
  528. {
  529. // If the thread does not have an access token, we'll examine the
  530. // access token associated with the process.
  531. if (! OpenProcessToken ( GetCurrentProcess(), TOKEN_QUERY,
  532. &hThread))
  533. return ( FALSE);
  534. }
  535. else
  536. return ( FALSE);
  537. }
  538. // Then we must query the size of the group information associated with
  539. // the token. Note that we expect a FALSE result from GetTokenInformation
  540. // because we've given it a NULL buffer. On exit cbTokenGroups will tell
  541. // the size of the group information.
  542. if ( GetTokenInformation ( hThread, TokenGroups, NULL, 0, &cbTokenGroups))
  543. return ( FALSE);
  544. // Here we verify that GetTokenInformation failed for lack of a large
  545. // enough buffer.
  546. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  547. return ( FALSE);
  548. // Now we allocate a buffer for the group information.
  549. // Since _alloca allocates on the stack, we don't have
  550. // to explicitly deallocate it. That happens automatically
  551. // when we exit this function.
  552. if ( ! ( ptg= (TOKEN_GROUPS *)malloc ( cbTokenGroups)))
  553. return ( FALSE);
  554. // Now we ask for the group information again.
  555. // This may fail if an administrator has added this account
  556. // to an additional group between our first call to
  557. // GetTokenInformation and this one.
  558. if ( !GetTokenInformation ( hThread, TokenGroups, ptg, cbTokenGroups,
  559. &cbTokenGroups) )
  560. {
  561. free(ptg);
  562. return ( FALSE);
  563. }
  564. // Now we must create a System Identifier for the Admin group.
  565. if ( ! AllocateAndInitializeSid ( &SystemSidAuthority, 2,
  566. SECURITY_BUILTIN_DOMAIN_RID,
  567. DOMAIN_ALIAS_RID_ADMINS,
  568. 0, 0, 0, 0, 0, 0, &psidAdmin) )
  569. {
  570. free(ptg);
  571. return ( FALSE);
  572. }
  573. // Finally we'll iterate through the list of groups for this access
  574. // token looking for a match against the SID we created above.
  575. fAdmin= FALSE;
  576. for ( dwGroup= 0; dwGroup < ptg->GroupCount; dwGroup++)
  577. {
  578. if ( EqualSid ( ptg->Groups[dwGroup].Sid, psidAdmin))
  579. {
  580. fAdmin = TRUE;
  581. break;
  582. }
  583. }
  584. // Before we exit we must explicity deallocate the SID we created.
  585. FreeSid ( psidAdmin);
  586. free(ptg);
  587. return ( fAdmin);
  588. }
  589. BOOL
  590. DoIt(
  591. VOID
  592. )
  593. {
  594. BOOL ShowErr;
  595. HANDLE hThread;
  596. DWORD ThreadId;
  597. HANDLE hMutex;
  598. TCHAR Fname[MAX_PATH];
  599. TCHAR MutexName[MAX_PATH];
  600. DWORD Flags;
  601. HWND StartingMsgWindow = NULL;
  602. HCURSOR hOldCursor;
  603. if (bDoAdminCheck && !RunningAsAdministrator()) {
  604. MessageBoxFromMessage(
  605. StartingMsgWindow,
  606. MSG_NOT_ADMIN,
  607. FALSE,
  608. MAKEINTRESOURCE(AppTitleStringId),
  609. MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL
  610. );
  611. return FALSE;
  612. }
  613. //
  614. // Create a Mutex from the Base Name of the Inf file
  615. // This will prevent OCM from running on the same inf file
  616. // in two or more instances
  617. //
  618. _tsplitpath( InfPath, NULL, NULL, Fname, NULL );
  619. lstrcpy( MutexName, TEXT("Global\\"));
  620. lstrcat( MutexName, Fname );
  621. hMutex = CreateMutex( NULL, TRUE, MutexName );
  622. if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError()) {
  623. MessageBoxFromMessage(
  624. NULL,
  625. MSG_ONLY_ONE_INST,
  626. FALSE,
  627. MAKEINTRESOURCE(AppTitleStringId),
  628. MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_SYSTEMMODAL
  629. );
  630. ReleaseMutex(hMutex);
  631. return FALSE;
  632. }
  633. //
  634. // Initialize the OC Manager. Show the user an "initializing setup"
  635. // dialog while this is happening, as it can take a while.
  636. //
  637. if (!SkipBillboard) {
  638. bStarting = TRUE;
  639. StartingMsgWindow = NULL;
  640. hThread = CreateThread(
  641. NULL,
  642. 0,
  643. DisplayMessage,
  644. &StartingMsgWindow,
  645. 0,
  646. &ThreadId
  647. );
  648. if (hThread) {
  649. CloseHandle(hThread);
  650. Sleep(50);
  651. } else {
  652. DisplayMessage(0);
  653. }
  654. }
  655. //
  656. // Make sure the window has actually been created,
  657. // or we could have a timing window where the PostMessage fails
  658. // and the billboard shows up on top of the wizard.
  659. //
  660. if (!SkipBillboard) {
  661. while (!StartingMsgWindow) {
  662. Sleep(50);
  663. }
  664. }
  665. Flags = ForceNewInf ? OCINIT_FORCENEWINF : 0;
  666. Flags |= KillSubcomponentEntries ? OCINIT_KILLSUBCOMPS : 0;
  667. Flags |= QuietMode ? OCINIT_RUNQUIET : 0;
  668. Flags |= LanguageAware ? OCINIT_LANGUAGEAWARE : 0 ;
  669. hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  670. OcManagerContext = OcInitialize(
  671. &CallbackRoutines,
  672. InfPath,
  673. Flags,
  674. &ShowErr,
  675. NULL
  676. );
  677. if (!OcManagerContext) {
  678. SetCursor( hOldCursor );
  679. if (ShowErr) {
  680. MessageBoxFromMessage(
  681. StartingMsgWindow,
  682. MSG_CANT_INIT,
  683. FALSE,
  684. MAKEINTRESOURCE(AppTitleStringId),
  685. MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL
  686. );
  687. }
  688. ReleaseMutex(hMutex);
  689. return (FALSE);
  690. }
  691. //
  692. // Do the wizard.
  693. //
  694. DoWizard(OcManagerContext,StartingMsgWindow, hOldCursor);
  695. SetCursor( hOldCursor );
  696. hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  697. // the Terminate can take a while too..
  698. if (!SkipBillboard) {
  699. bStarting = FALSE;
  700. StartingMsgWindow = NULL;
  701. hThread = CreateThread(
  702. NULL,
  703. 0,
  704. DisplayMessage,
  705. &StartingMsgWindow,
  706. 0,
  707. &ThreadId
  708. );
  709. if (hThread) {
  710. CloseHandle(hThread);
  711. Sleep(50);
  712. } else {
  713. DisplayMessage(0);
  714. }
  715. }
  716. //
  717. // Clean up, we're done.
  718. //
  719. OcTerminate(&OcManagerContext);
  720. if (!SkipBillboard) {
  721. //
  722. // Make sure the window has actually been created,
  723. // or we could have a timing window where the PostMessage fails
  724. // and the billboard shows up on top of the wizard.
  725. //
  726. while (!StartingMsgWindow) {
  727. Sleep(50);
  728. }
  729. SendMessage(StartingMsgWindow,WM_APP,0,0);
  730. }
  731. ReleaseMutex(hMutex);
  732. SetCursor( hOldCursor );
  733. return (TRUE);
  734. }
  735. VOID
  736. OcSetReboot(
  737. VOID
  738. )
  739. {
  740. NeedToReboot = TRUE;
  741. }
  742. DWORD
  743. ExpandPath(
  744. IN LPCTSTR lpFileName,
  745. OUT LPTSTR lpBuffer,
  746. OUT LPTSTR *lpFilePart
  747. )
  748. {
  749. TCHAR buf[MAX_PATH];
  750. ExpandEnvironmentStrings(lpFileName, buf, MAX_PATH);
  751. return GetFullPathName(buf, MAX_PATH, lpBuffer, lpFilePart);
  752. }