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.

967 lines
25 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,
  169. SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG) ) {
  170. _RPT0(_CRT_WARN,"Sysocmgr:Failed to ExitWindows");
  171. sapiAssert(FALSE);
  172. }
  173. } else {
  174. RestartDialogEx(NULL,NULL,EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG \
  175. | SHTDN_REASON_FLAG_PLANNED );
  176. }
  177. }
  178. }
  179. VOID
  180. __cdecl
  181. #ifdef UNICODE
  182. wmain(
  183. #else
  184. main(
  185. #endif
  186. IN int argc,
  187. IN TCHAR *argv[]
  188. )
  189. {
  190. INITCOMMONCONTROLSEX ControlInit;
  191. //
  192. // Preliminaries
  193. //
  194. ControlInit.dwSize = sizeof(INITCOMMONCONTROLSEX);
  195. ControlInit.dwICC = ICC_LISTVIEW_CLASSES |
  196. ICC_TREEVIEW_CLASSES |
  197. ICC_BAR_CLASSES |
  198. ICC_TAB_CLASSES |
  199. ICC_UPDOWN_CLASS |
  200. ICC_PROGRESS_CLASS |
  201. ICC_HOTKEY_CLASS |
  202. ICC_ANIMATE_CLASS |
  203. ICC_WIN95_CLASSES |
  204. ICC_DATE_CLASSES |
  205. ICC_USEREX_CLASSES |
  206. ICC_COOL_CLASSES;
  207. #if (_WIN32_IE >= 0x0400)
  208. ControlInit.dwICC = ControlInit.dwICC |
  209. ICC_INTERNET_CLASSES |
  210. ICC_PAGESCROLLER_CLASS;
  211. #endif
  212. InitCommonControlsEx( &ControlInit );
  213. hInst = GetModuleHandle(NULL);
  214. OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  215. GetVersionEx(&OsVersionInfo);
  216. AppTitleStringId = IDS_WINNT_SETUP;
  217. //
  218. // Parse arguments and do it.
  219. //
  220. if (ParseArgs(argc,argv)) {
  221. DoIt();
  222. }
  223. //
  224. // If we need to reboot, do that now.
  225. //
  226. if (NeedToReboot) {
  227. ShutDown();
  228. }
  229. }
  230. BOOL
  231. ParseArgs(
  232. IN int argc,
  233. IN TCHAR *argv[]
  234. )
  235. /*++
  236. Routine Description:
  237. Parse and syntactically validate arguments specified on the comment line.
  238. The following arguments are valid:
  239. /a forces the external progress indicator on the setup page
  240. /c disallow cancel during final installation phase
  241. /i:<master_oc_inf> specifies the master OC inf (required).
  242. /n forces specified master_oc_inf to be treated as new.
  243. /s:<master_oc_inf> specifies the source path (required).
  244. /u:<unattend_spec> specifies unattended operation parameters.
  245. /x supresses the 'initializing' banner
  246. /q run wizard invisibly
  247. /r supress reboot if need on unattended operation
  248. /w warn on reboot on unattended operation
  249. Arguments:
  250. Standard main argc/argv.
  251. Return Value:
  252. Boolean value indicating whether the arguments specified are valid.
  253. If successful, various global variables will have been filled in.
  254. If not, the user will have been informed.
  255. --*/
  256. {
  257. BOOL Valid;
  258. LPCTSTR SourcePathSpec = NULL;
  259. LPCTSTR InfSpec = NULL;
  260. LPCTSTR UnattendSpec = NULL;
  261. LPTSTR FilePart;
  262. DWORD u;
  263. //
  264. // Skip program name.
  265. //
  266. if (argc) {
  267. argc--;
  268. }
  269. Valid = TRUE;
  270. ForceNewInf = FALSE;
  271. QuietMode = FALSE;
  272. KillSubcomponentEntries = FALSE;
  273. while (Valid && argc--) {
  274. argv++;
  275. if ((argv[0][0] == TEXT('-')) || (argv[0][0] == TEXT('/'))) {
  276. switch (argv[0][1]) {
  277. case TEXT('a'):
  278. case TEXT('A'):
  279. if (!ForceExternalProgressIndicator && !argv[0][2]) {
  280. ForceExternalProgressIndicator = TRUE;
  281. } else {
  282. Valid = FALSE;
  283. }
  284. break;
  285. case TEXT('c'):
  286. case TEXT('C'):
  287. if (AllowCancel && !argv[0][2]) {
  288. AllowCancel = FALSE;
  289. } else {
  290. Valid = FALSE;
  291. }
  292. break;
  293. case TEXT('f'):
  294. case TEXT('F'):
  295. ForceNewInf = TRUE;
  296. KillSubcomponentEntries = TRUE;
  297. break;
  298. case TEXT('i'):
  299. case TEXT('I'):
  300. if (!InfSpec && (argv[0][2] == TEXT(':')) && argv[0][3]) {
  301. InfSpec = &(argv[0][3]);
  302. } else {
  303. Valid = FALSE;
  304. }
  305. break;
  306. case TEXT('l'):
  307. case TEXT('L'):
  308. LanguageAware = TRUE;
  309. break;
  310. case TEXT('n'):
  311. case TEXT('N'):
  312. ForceNewInf = TRUE;
  313. break;
  314. case TEXT('q'):
  315. case TEXT('Q'):
  316. if (!QuietMode && !argv[0][2]) {
  317. QuietMode = TRUE;
  318. SkipBillboard = TRUE;
  319. } else {
  320. Valid = FALSE;
  321. }
  322. break;
  323. case TEXT('r'):
  324. case TEXT('R'):
  325. bNoReboot = TRUE;
  326. break;
  327. case TEXT('s'):
  328. case TEXT('S'):
  329. if (!SourcePathSpec && (argv[0][2] == TEXT(':')) && argv[0][3]) {
  330. SourcePathSpec = &argv[0][3];
  331. } else {
  332. Valid = FALSE;
  333. }
  334. break;
  335. case TEXT('u'):
  336. case TEXT('U'):
  337. //
  338. // accept unattend, unattended, u all as the same
  339. //
  340. if(!_tcsnicmp(&argv[0][1],TEXT("unattended"),10)) {
  341. u = 11;
  342. } else if(!_tcsnicmp(&argv[0][1],TEXT("unattend"),8)) {
  343. u = 9;
  344. } else if(!_tcsnicmp(&argv[0][1],TEXT("u"),1)) {
  345. u = 2;
  346. } else {
  347. Valid = FALSE;
  348. u = 0;
  349. }
  350. if (!UnattendSpec ) {
  351. bUnattendInstall = TRUE;
  352. // If you have the : then you must also have the arg
  353. if (argv[0][u] == TEXT(':')) {
  354. if ( argv[0][u+1]) {
  355. UnattendSpec = &argv[0][u+1];
  356. } else {
  357. Valid = FALSE;
  358. }
  359. } else {
  360. Valid = FALSE;
  361. }
  362. } else {
  363. Valid = FALSE;
  364. }
  365. break;
  366. case TEXT('w'):
  367. case TEXT('W'):
  368. bWarnOnReboot = TRUE;
  369. break;
  370. case TEXT('x'):
  371. case TEXT('X'):
  372. if (!SkipBillboard && !argv[0][2]) {
  373. SkipBillboard = TRUE;
  374. } else {
  375. Valid = FALSE;
  376. }
  377. break;
  378. // For ISSUE NTBUG9:295052 (389583): We want to do a top level admin check so we get a more friendly message.
  379. // It is possible for people to have been using sysocmgr.exe with their own custom master oc.inf
  380. // (the one passed in with the /i: switch) and they may not need this admin check. So, we did
  381. // not want to do this admin check unconditionally. We will have the control panel applet that
  382. // is launching sysocmgr.exe to pass in this /y switch.
  383. //
  384. case TEXT('y'):
  385. case TEXT('Y'):
  386. bDoAdminCheck = TRUE;
  387. break;
  388. case TEXT('z'):
  389. case TEXT('Z'):
  390. // Stop parsing Arguments All other args past this point are
  391. // Component Arguments
  392. argc = 0;
  393. break;
  394. default:
  395. Valid = FALSE;
  396. break;
  397. }
  398. } else {
  399. Valid = FALSE;
  400. }
  401. }
  402. if (Valid && !InfSpec) {
  403. Valid = FALSE;
  404. }
  405. if (Valid) {
  406. //
  407. // Expand the inf spec to a full path.
  408. //
  409. ExpandPath(InfSpec,InfPath,&FilePart);
  410. _tcscpy(InfDir, InfSpec);
  411. if (_tcsrchr(InfDir, TEXT('\\')))
  412. *_tcsrchr(InfDir,TEXT('\\')) = 0;
  413. else
  414. GetCurrentDirectory(MAX_PATH, InfDir);
  415. // If the user specified /s then expand it too, otherwise
  416. // use the dir in the /i as the /s arg.
  417. if (SourcePathSpec) {
  418. ExpandPath(SourcePathSpec,SourcePath,&FilePart);
  419. } else {
  420. lstrcpy(SourcePath,InfPath);
  421. if (_tcsrchr(SourcePath,TEXT('\\'))) {
  422. *_tcsrchr(SourcePath,TEXT('\\')) = 0;
  423. }
  424. }
  425. SetCurrentDirectory(InfDir);
  426. if (UnattendSpec) {
  427. ExpandPath(UnattendSpec,UnattendPath,&FilePart);
  428. }else{
  429. // Allow /Q only if /U was specified
  430. QuietMode = FALSE;
  431. SkipBillboard = FALSE;
  432. }
  433. } else {
  434. MessageBoxFromMessage(
  435. NULL,
  436. MSG_ARGS,
  437. FALSE,
  438. MAKEINTRESOURCE(AppTitleStringId),
  439. MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL
  440. );
  441. }
  442. return (Valid);
  443. }
  444. INT_PTR
  445. BillboardDlgProc(
  446. IN HWND hdlg,
  447. IN UINT msg,
  448. IN WPARAM wParam,
  449. IN LPARAM lParam
  450. )
  451. {
  452. BOOL b;
  453. RECT rect1,rect2;
  454. static HCURSOR hOldCursor;
  455. switch (msg) {
  456. case WM_INITDIALOG:
  457. //
  458. // Center on-screen.
  459. //
  460. GetWindowRect(hdlg,&rect1);
  461. SystemParametersInfo(SPI_GETWORKAREA,0,&rect2,0);
  462. MoveWindow(
  463. hdlg,
  464. rect2.left + (((rect2.right - rect2.left) - (rect1.right - rect1.left)) / 2),
  465. rect2.top + (((rect2.bottom - rect2.top) - (rect1.bottom - rect1.top)) / 2),
  466. rect1.right - rect1.left,
  467. rect1.bottom - rect1.top,
  468. FALSE
  469. );
  470. *(HWND *)lParam = hdlg;
  471. b = TRUE;
  472. hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  473. break;
  474. case WM_APP:
  475. EndDialog(hdlg,0);
  476. SetCursor( hOldCursor );
  477. b = TRUE;
  478. break;
  479. default:
  480. b = FALSE;
  481. break;
  482. }
  483. return (b);
  484. }
  485. DWORD
  486. DisplayMessage(
  487. IN LPVOID ThreadParameter
  488. )
  489. {
  490. int i;
  491. i = (int)DialogBoxParam(
  492. hInst,
  493. MAKEINTRESOURCE(bStarting?IDD_STARTING:IDD_FINISHING),
  494. NULL,
  495. BillboardDlgProc,
  496. (LPARAM)ThreadParameter
  497. );
  498. if (i == -1) {
  499. //
  500. // Force caller out of wait loop
  501. //
  502. *(HWND *)ThreadParameter = (HWND)(-1);
  503. }
  504. return (0);
  505. }
  506. /*---------------------------------------------------------------------------*\
  507. Function: RunningAsAdministrator()
  508. |*---------------------------------------------------------------------------*|
  509. Description: Checks whether we are running as administrator on the machine
  510. or not.
  511. Code taken from ntoc.dll
  512. \*---------------------------------------------------------------------------*/
  513. BOOL
  514. RunningAsAdministrator(
  515. VOID
  516. )
  517. {
  518. BOOL fAdmin;
  519. HANDLE hThread;
  520. TOKEN_GROUPS *ptg = NULL;
  521. DWORD cbTokenGroups;
  522. DWORD dwGroup;
  523. PSID psidAdmin;
  524. SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
  525. // First we must open a handle to the access token for this thread.
  526. if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread))
  527. {
  528. if ( GetLastError() == ERROR_NO_TOKEN)
  529. {
  530. // If the thread does not have an access token, we'll examine the
  531. // access token associated with the process.
  532. if (! OpenProcessToken ( GetCurrentProcess(), TOKEN_QUERY,
  533. &hThread))
  534. return ( FALSE);
  535. }
  536. else
  537. return ( FALSE);
  538. }
  539. // Then we must query the size of the group information associated with
  540. // the token. Note that we expect a FALSE result from GetTokenInformation
  541. // because we've given it a NULL buffer. On exit cbTokenGroups will tell
  542. // the size of the group information.
  543. if ( GetTokenInformation ( hThread, TokenGroups, NULL, 0, &cbTokenGroups))
  544. return ( FALSE);
  545. // Here we verify that GetTokenInformation failed for lack of a large
  546. // enough buffer.
  547. if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  548. return ( FALSE);
  549. // Now we allocate a buffer for the group information.
  550. // Since _alloca allocates on the stack, we don't have
  551. // to explicitly deallocate it. That happens automatically
  552. // when we exit this function.
  553. if ( ! ( ptg= (TOKEN_GROUPS *)malloc ( cbTokenGroups)))
  554. return ( FALSE);
  555. // Now we ask for the group information again.
  556. // This may fail if an administrator has added this account
  557. // to an additional group between our first call to
  558. // GetTokenInformation and this one.
  559. if ( !GetTokenInformation ( hThread, TokenGroups, ptg, cbTokenGroups,
  560. &cbTokenGroups) )
  561. {
  562. free(ptg);
  563. return ( FALSE);
  564. }
  565. // Now we must create a System Identifier for the Admin group.
  566. if ( ! AllocateAndInitializeSid ( &SystemSidAuthority, 2,
  567. SECURITY_BUILTIN_DOMAIN_RID,
  568. DOMAIN_ALIAS_RID_ADMINS,
  569. 0, 0, 0, 0, 0, 0, &psidAdmin) )
  570. {
  571. free(ptg);
  572. return ( FALSE);
  573. }
  574. // Finally we'll iterate through the list of groups for this access
  575. // token looking for a match against the SID we created above.
  576. fAdmin= FALSE;
  577. for ( dwGroup= 0; dwGroup < ptg->GroupCount; dwGroup++)
  578. {
  579. if ( EqualSid ( ptg->Groups[dwGroup].Sid, psidAdmin))
  580. {
  581. fAdmin = TRUE;
  582. break;
  583. }
  584. }
  585. // Before we exit we must explicity deallocate the SID we created.
  586. FreeSid ( psidAdmin);
  587. free(ptg);
  588. return ( fAdmin);
  589. }
  590. BOOL
  591. DoIt(
  592. VOID
  593. )
  594. {
  595. BOOL ShowErr;
  596. HANDLE hThread;
  597. DWORD ThreadId;
  598. HANDLE hMutex;
  599. TCHAR Fname[MAX_PATH];
  600. TCHAR MutexName[MAX_PATH];
  601. DWORD Flags;
  602. HWND StartingMsgWindow = NULL;
  603. HCURSOR hOldCursor;
  604. if (bDoAdminCheck && !RunningAsAdministrator()) {
  605. MessageBoxFromMessage(
  606. StartingMsgWindow,
  607. MSG_NOT_ADMIN,
  608. FALSE,
  609. MAKEINTRESOURCE(AppTitleStringId),
  610. MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL
  611. );
  612. return FALSE;
  613. }
  614. //
  615. // Create a Mutex from the Base Name of the Inf file
  616. // This will prevent OCM from running on the same inf file
  617. // in two or more instances
  618. //
  619. _tsplitpath( InfPath, NULL, NULL, Fname, NULL );
  620. lstrcpy( MutexName, TEXT("Global\\"));
  621. lstrcat( MutexName, Fname );
  622. hMutex = CreateMutex( NULL, TRUE, MutexName );
  623. if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError()) {
  624. MessageBoxFromMessage(
  625. NULL,
  626. MSG_ONLY_ONE_INST,
  627. FALSE,
  628. MAKEINTRESOURCE(AppTitleStringId),
  629. MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_SYSTEMMODAL
  630. );
  631. ReleaseMutex(hMutex);
  632. return FALSE;
  633. }
  634. //
  635. // Initialize the OC Manager. Show the user an "initializing setup"
  636. // dialog while this is happening, as it can take a while.
  637. //
  638. if (!SkipBillboard) {
  639. bStarting = TRUE;
  640. StartingMsgWindow = NULL;
  641. hThread = CreateThread(
  642. NULL,
  643. 0,
  644. DisplayMessage,
  645. &StartingMsgWindow,
  646. 0,
  647. &ThreadId
  648. );
  649. if (hThread) {
  650. CloseHandle(hThread);
  651. Sleep(50);
  652. } else {
  653. DisplayMessage(0);
  654. }
  655. }
  656. //
  657. // Make sure the window has actually been created,
  658. // or we could have a timing window where the PostMessage fails
  659. // and the billboard shows up on top of the wizard.
  660. //
  661. if (!SkipBillboard) {
  662. while (!StartingMsgWindow) {
  663. Sleep(50);
  664. }
  665. }
  666. Flags = ForceNewInf ? OCINIT_FORCENEWINF : 0;
  667. Flags |= KillSubcomponentEntries ? OCINIT_KILLSUBCOMPS : 0;
  668. Flags |= QuietMode ? OCINIT_RUNQUIET : 0;
  669. Flags |= LanguageAware ? OCINIT_LANGUAGEAWARE : 0 ;
  670. hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  671. OcManagerContext = OcInitialize(
  672. &CallbackRoutines,
  673. InfPath,
  674. Flags,
  675. &ShowErr,
  676. NULL
  677. );
  678. if (!OcManagerContext) {
  679. SetCursor( hOldCursor );
  680. if (ShowErr) {
  681. MessageBoxFromMessage(
  682. StartingMsgWindow,
  683. MSG_CANT_INIT,
  684. FALSE,
  685. MAKEINTRESOURCE(AppTitleStringId),
  686. MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_SYSTEMMODAL
  687. );
  688. }
  689. ReleaseMutex(hMutex);
  690. return (FALSE);
  691. }
  692. //
  693. // Do the wizard.
  694. //
  695. DoWizard(OcManagerContext,StartingMsgWindow, hOldCursor);
  696. SetCursor( hOldCursor );
  697. hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
  698. // the Terminate can take a while too..
  699. if (!SkipBillboard) {
  700. bStarting = FALSE;
  701. StartingMsgWindow = NULL;
  702. hThread = CreateThread(
  703. NULL,
  704. 0,
  705. DisplayMessage,
  706. &StartingMsgWindow,
  707. 0,
  708. &ThreadId
  709. );
  710. if (hThread) {
  711. CloseHandle(hThread);
  712. Sleep(50);
  713. } else {
  714. DisplayMessage(0);
  715. }
  716. }
  717. //
  718. // Clean up, we're done.
  719. //
  720. OcTerminate(&OcManagerContext);
  721. if (!SkipBillboard) {
  722. //
  723. // Make sure the window has actually been created,
  724. // or we could have a timing window where the PostMessage fails
  725. // and the billboard shows up on top of the wizard.
  726. //
  727. while (!StartingMsgWindow) {
  728. Sleep(50);
  729. }
  730. SendMessage(StartingMsgWindow,WM_APP,0,0);
  731. }
  732. ReleaseMutex(hMutex);
  733. SetCursor( hOldCursor );
  734. return (TRUE);
  735. }
  736. VOID
  737. OcSetReboot(
  738. VOID
  739. )
  740. {
  741. NeedToReboot = TRUE;
  742. }
  743. DWORD
  744. ExpandPath(
  745. IN LPCTSTR lpFileName,
  746. OUT LPTSTR lpBuffer,
  747. OUT LPTSTR *lpFilePart
  748. )
  749. {
  750. TCHAR buf[MAX_PATH];
  751. ExpandEnvironmentStrings(lpFileName, buf, MAX_PATH);
  752. return GetFullPathName(buf, MAX_PATH, lpBuffer, lpFilePart);
  753. }