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.

949 lines
21 KiB

  1. // Copyright (c) 1997-1999 Microsoft Corporation
  2. //
  3. // domain controller promotion wizard, Mark II
  4. //
  5. // 12-12-97 sburns
  6. #include "headers.hxx"
  7. #include "common.hpp"
  8. #include "adpass.hpp"
  9. #include "BadComputerNameDialog.hpp"
  10. #include "CheckPortAvailability.hpp"
  11. #include "ChildPage.hpp"
  12. #include "CredentialsPage.hpp"
  13. #include "ConfirmationPage.hpp"
  14. #include "DemotePage.hpp"
  15. #include "DynamicDnsPage.hpp"
  16. #include "ConfigureDnsClientPage.hpp"
  17. #include "DnsOnNetPage.hpp"
  18. #include "FailurePage.hpp"
  19. #include "finish.hpp"
  20. #include "ForestPage.hpp"
  21. #include "ForestVersionPage.hpp"
  22. #include "GcConfirmationPage.hpp"
  23. #include "NetbiosNamePage.hpp"
  24. #include "NewDomainPage.hpp"
  25. #include "NewSitePage.hpp"
  26. #include "NonDomainNc.hpp"
  27. #include "NonRfcComputerNameDialog.hpp"
  28. #include "PathsPage.hpp"
  29. #include "Paths2Page.hpp"
  30. #include "PickSitePage.hpp"
  31. #include "rasfixup.hpp"
  32. #include "RebootDialog.hpp"
  33. #include "ReplicaOrNewDomainPage.hpp"
  34. #include "ReplicateFromMediaPage.hpp"
  35. #include "ReplicaPage.hpp"
  36. #include "ReplicaOrMemberPage.hpp"
  37. #include "resource.h"
  38. #include "safemode.hpp"
  39. #include "state.hpp"
  40. #include "InstallTcpIpPage.hpp"
  41. #include "TreePage.hpp"
  42. #include "WelcomePage.hpp"
  43. HINSTANCE hResourceModuleHandle = 0;
  44. const wchar_t* HELPFILE_NAME = 0; // no context help available
  45. // don't change this: it is also the name of a mutex that the net id ui
  46. // uses to determine if dcpromo is running.
  47. const wchar_t* RUNTIME_NAME = L"dcpromoui";
  48. DWORD DEFAULT_LOGGING_OPTIONS =
  49. Log::OUTPUT_TO_FILE
  50. | Log::OUTPUT_FUNCCALLS
  51. | Log::OUTPUT_LOGS
  52. | Log::OUTPUT_ERRORS
  53. | Log::OUTPUT_HEADER;
  54. // a system modal popup thingy
  55. Popup popup(IDS_WIZARD_TITLE, true);
  56. // this is the mutex that indicates the dcpromo is running.
  57. HANDLE dcpromoRunningMutex = INVALID_HANDLE_VALUE;
  58. // these are the valid exit codes returned from the dcpromo.exe process
  59. enum ExitCode
  60. {
  61. // the operation failed.
  62. EXIT_CODE_UNSUCCESSFUL = 0,
  63. // the operation succeeded
  64. EXIT_CODE_SUCCESSFUL = 1,
  65. // the operation succeeded, and the user opted not to have the wizard
  66. // restart the machine, either manually or by specifying
  67. // RebootOnSuccess=NoAndNoPromptEither in the answerfile
  68. EXIT_CODE_SUCCESSFUL_NO_REBOOT = 2,
  69. // the operation failed, but the machine needs to be rebooted anyway
  70. EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT = 3
  71. };
  72. // Checks the platform and os version number. Returns the resource ID of the
  73. // string to present to the user if platform/os ver requirements are not met,
  74. // or 0 if they are met.
  75. unsigned
  76. CheckPlatform()
  77. {
  78. LOG_FUNCTION(CheckPlatform);
  79. unsigned retval = 0;
  80. Computer::Role role = State::GetInstance().GetComputer().GetRole();
  81. switch (role)
  82. {
  83. case Computer::STANDALONE_WORKSTATION:
  84. case Computer::MEMBER_WORKSTATION:
  85. {
  86. retval = IDS_WORKSTATION_NOT_SUPPORTED;
  87. break;
  88. }
  89. case Computer::STANDALONE_SERVER:
  90. case Computer::MEMBER_SERVER:
  91. case Computer::PRIMARY_CONTROLLER:
  92. case Computer::BACKUP_CONTROLLER:
  93. {
  94. // check OS version
  95. OSVERSIONINFOEX info;
  96. memset(&info, 0, sizeof(info));
  97. HRESULT hr = Win::GetVersionEx(info);
  98. BREAK_ON_FAILED_HRESULT(hr);
  99. if (
  100. // require an NT-based OS
  101. info.dwPlatformId != VER_PLATFORM_WIN32_NT
  102. // require Whistler (version 5.1)
  103. || !(info.dwMajorVersion == 5 && info.dwMinorVersion == 1))
  104. {
  105. retval = IDS_NT51_REQUIRED;
  106. break;
  107. }
  108. if (info.wSuiteMask & VER_SUITE_BLADE)
  109. {
  110. // machine is a web blade. Don't allow promotion.
  111. // NTRAID#NTBUG9-195265-2001/04/03-sburns
  112. retval = IDS_WEB_BLADE_NOT_SUPPORTED;
  113. break;
  114. }
  115. break;
  116. }
  117. default:
  118. {
  119. ASSERT(false);
  120. break;
  121. }
  122. }
  123. return retval;
  124. }
  125. // Checks the role change state of the machine. Returns the resource ID of
  126. // the string to present to the user if the state is such that another role
  127. // change cannot be attempted, or 0 if a role change attempt may proceed.
  128. unsigned
  129. CheckRoleChangeState()
  130. {
  131. LOG_FUNCTION(CheckRoleChangeState);
  132. unsigned retval = 0;
  133. // check to see if a role change has taken place, or is in progress.
  134. DSROLE_OPERATION_STATE_INFO* info = 0;
  135. HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
  136. if (SUCCEEDED(hr) && info)
  137. {
  138. switch (info->OperationState)
  139. {
  140. case DsRoleOperationIdle:
  141. {
  142. // do nothing
  143. break;
  144. }
  145. case DsRoleOperationActive:
  146. {
  147. // a role change operation is underway
  148. retval = IDS_ROLE_CHANGE_IN_PROGRESS;
  149. break;
  150. }
  151. case DsRoleOperationNeedReboot:
  152. {
  153. // a role change has already taken place, need to reboot before
  154. // attempting another.
  155. retval = IDS_ROLE_CHANGE_NEEDS_REBOOT;
  156. break;
  157. }
  158. default:
  159. {
  160. ASSERT(false);
  161. break;
  162. }
  163. }
  164. ::DsRoleFreeMemory(info);
  165. }
  166. return retval;
  167. }
  168. // Checks for the presence of at least 1 logical drive formatted with NTFS5.
  169. // Returns the resource ID of the string to present to the user if no such
  170. // drive is found (which implies that the user will not be able to pick a
  171. // sysvol path that is formatted w/ NTFS5, which implies that proceeding would
  172. // be a waste of time.
  173. unsigned
  174. CheckForNtfs5()
  175. {
  176. LOG_FUNCTION(CheckForNtfs5);
  177. if (GetFirstNtfs5HardDrive().empty())
  178. {
  179. return IDS_NO_NTFS5_DRIVES;
  180. }
  181. return 0;
  182. }
  183. // Checks if the machine is running in safeboot mode. You can run dcpromo
  184. // while in safeboot mode.
  185. unsigned
  186. CheckSafeBootMode()
  187. {
  188. LOG_FUNCTION(CheckSafeBootMode);
  189. static const String
  190. SAFEBOOT_KEY(L"System\\CurrentControlSet\\Control\\Safeboot\\Option");
  191. do
  192. {
  193. RegistryKey key;
  194. HRESULT hr = key.Open(HKEY_LOCAL_MACHINE, SAFEBOOT_KEY);
  195. BREAK_ON_FAILED_HRESULT(hr);
  196. DWORD mode = 0;
  197. hr = key.GetValue(L"OptionValue", mode);
  198. if (mode)
  199. {
  200. return IDS_SAFEBOOT_MODE;
  201. }
  202. }
  203. while (0);
  204. return 0;
  205. }
  206. unsigned
  207. CheckCertService()
  208. {
  209. LOG_FUNCTION(CheckCertService);
  210. // If not a downlevel DC upgrade, then refuse to run until cert service
  211. // is removed. 356399
  212. State::RunContext context = State::GetInstance().GetRunContext();
  213. if (context != State::BDC_UPGRADE && context != State::PDC_UPGRADE)
  214. {
  215. if (NTService(L"CertSvc").IsInstalled())
  216. {
  217. return IDS_CERT_SERVICE_IS_INSTALLED;
  218. }
  219. }
  220. return 0;
  221. }
  222. unsigned
  223. CheckWindirSpace()
  224. {
  225. LOG_FUNCTION(CheckWindirSpace);
  226. // if you change this, change the error message resource too.
  227. static const unsigned WINDIR_MIN_SPACE_MB = 20;
  228. String windir = Win::GetSystemWindowsDirectory();
  229. if (!CheckDiskSpace(windir, 20))
  230. {
  231. return IDS_WINDIR_LOW_SPACE;
  232. }
  233. return 0;
  234. }
  235. // NTRAID#NTBUG9-199759-2000/10/27-sburns
  236. unsigned
  237. CheckComputerWasRenamedAndNeedsReboot()
  238. {
  239. LOG_FUNCTION(CheckComputerWasRenamedAndNeedsReboot);
  240. if (ComputerWasRenamedAndNeedsReboot())
  241. {
  242. return IDS_NAME_CHANGE_NEEDS_REBOOT;
  243. }
  244. return 0;
  245. }
  246. // Start the Network Identification UI (aka Computer Name UI). After calling
  247. // this function, the app must not initiate a role change. It should
  248. // terminate.
  249. void
  250. LaunchNetId()
  251. {
  252. LOG_FUNCTION(LaunchNetId);
  253. ASSERT(dcpromoRunningMutex != INVALID_HANDLE_VALUE);
  254. // net id ui attempts acquisition of our mutex to determine if we are
  255. // running. So, before starting the net id ui, we need to close the
  256. // mutex. Otherwise, we would create a race condition between the start of
  257. // the net id ui and the closure of this app. (And yes, cynical reader, I
  258. // did think of that before actually encountering a problem.)
  259. do
  260. {
  261. Win::CloseHandle(dcpromoRunningMutex);
  262. // It would be extraordinarily unlikely, but in case the mutex close
  263. // fails, we're gonna start the net id ui anyway and risk the race
  264. // condition.
  265. String sys32Folder = Win::GetSystemDirectory();
  266. String commandLine =
  267. String::format(
  268. IDS_LAUNCH_NETID_COMMAND_LINE,
  269. sys32Folder.c_str());
  270. PROCESS_INFORMATION procInfo;
  271. ::ZeroMemory(&procInfo, sizeof(procInfo));
  272. STARTUPINFO startup;
  273. ::ZeroMemory(&startup, sizeof(startup));
  274. LOG(L"Calling CreateProcess");
  275. LOG(commandLine);
  276. HRESULT hr =
  277. Win::CreateProcess(
  278. commandLine,
  279. 0,
  280. 0,
  281. false,
  282. 0,
  283. 0,
  284. String(),
  285. startup,
  286. procInfo);
  287. BREAK_ON_FAILED_HRESULT(hr);
  288. Win::CloseHandle(procInfo.hThread);
  289. Win::CloseHandle(procInfo.hProcess);
  290. }
  291. while (0);
  292. }
  293. // Return false if the local machine's dns hostname is bad, also pop up an
  294. // error dialog. Return true if the name is OK. A bad name is one we believe
  295. // will have problems being registered in DNS after a promotion. The user
  296. // must fix a bad name before proceeding.
  297. bool
  298. IsComputerNameOk()
  299. {
  300. LOG_FUNCTION(IsComputerNameOk);
  301. bool result = true;
  302. do
  303. {
  304. State::RunContext context = State::GetInstance().GetRunContext();
  305. if (
  306. context == State::BDC_UPGRADE
  307. || context == State::PDC_UPGRADE
  308. || context == State::NT5_DC
  309. || !IsTcpIpInstalled() )
  310. {
  311. // If the machine is already a DC, then we don't worry about the name.
  312. //
  313. // If the machine is a downlevel DC undergoing upgrade, then the name
  314. // can't be changed until dcpromo is complete. So, we say nothing now,
  315. // but remind the user to rename the machine in the Finish Page.
  316. //
  317. // If TCP/IP is not installed, then the machine has no hostname
  318. // to check. In this case, we will check for that with the
  319. // InstallTcpIpPage
  320. ASSERT(result == true);
  321. break;
  322. }
  323. // Then check the computer name to ensure that it can be registered in
  324. // DNS.
  325. String hostname =
  326. Win::GetComputerNameEx(::ComputerNamePhysicalDnsHostname);
  327. DNS_STATUS status =
  328. MyDnsValidateName(hostname, ::DnsNameHostnameLabel);
  329. switch (status)
  330. {
  331. case DNS_ERROR_NON_RFC_NAME:
  332. {
  333. INT_PTR dlgResult =
  334. NonRfcComputerNameDialog(hostname).ModalExecute(0);
  335. switch (dlgResult)
  336. {
  337. case NonRfcComputerNameDialog::CONTINUE:
  338. {
  339. // continue on with the non-rfc name
  340. ASSERT(result == true);
  341. break;
  342. }
  343. default:
  344. {
  345. // close the wizard and rename.
  346. result = false;
  347. // after calling this, we must not allow any promote
  348. // operation. We will fall out of this function, then
  349. // end the app.
  350. LaunchNetId();
  351. break;
  352. }
  353. }
  354. break;
  355. }
  356. case DNS_ERROR_NUMERIC_NAME:
  357. {
  358. result = false;
  359. String message =
  360. String::format(
  361. IDS_COMPUTER_NAME_IS_NUMERIC,
  362. hostname.c_str());
  363. BadComputerNameDialog(message).ModalExecute(0);
  364. break;
  365. }
  366. case DNS_ERROR_INVALID_NAME_CHAR:
  367. case ERROR_INVALID_NAME:
  368. {
  369. result = false;
  370. String message =
  371. String::format(
  372. IDS_COMPUTER_NAME_HAS_BAD_CHARS,
  373. hostname.c_str());
  374. BadComputerNameDialog(message).ModalExecute(0);
  375. break;
  376. }
  377. case ERROR_SUCCESS:
  378. default:
  379. {
  380. break;
  381. }
  382. }
  383. }
  384. while (0);
  385. LOG(result ? L"true" : L"false");
  386. return result;
  387. }
  388. // NTRAID#NTBUG9-346120-2001/04/04-sburns
  389. ExitCode
  390. HandleRebootCases()
  391. {
  392. LOG_FUNCTION(HandleRebootCases);
  393. // There are two possible reasons for needing to reboot the machine:
  394. // the operation was successful, or the operation failed, but in the
  395. // attempt the machine's joined state changed.
  396. State& state = State::GetInstance();
  397. ExitCode exitCode =
  398. (state.GetOperationResultsCode() == State::SUCCESS)
  399. ? EXIT_CODE_SUCCESSFUL
  400. : EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT;
  401. switch (exitCode)
  402. {
  403. case EXIT_CODE_SUCCESSFUL:
  404. {
  405. if (state.RunHiddenUnattended())
  406. {
  407. String option =
  408. state.GetAnswerFileOption(State::OPTION_REBOOT);
  409. if (option.icompare(State::VALUE_YES) == 0)
  410. {
  411. ASSERT(exitCode == EXIT_CODE_SUCCESSFUL);
  412. HRESULT hr = Reboot();
  413. if (FAILED(hr))
  414. {
  415. popup.Error(
  416. Win::GetDesktopWindow(),
  417. hr,
  418. IDS_CANT_REBOOT);
  419. }
  420. break;
  421. }
  422. else if (option.icompare(State::VALUE_NO_DONT_PROMPT) == 0)
  423. {
  424. // user opted not to reboot the machine via answerfile
  425. LOG(L"Not rebooting, and not prompting either");
  426. exitCode = EXIT_CODE_SUCCESSFUL_NO_REBOOT;
  427. break;
  428. }
  429. }
  430. RebootDialog dlg(false);
  431. if (dlg.ModalExecute(0))
  432. {
  433. // user opted to reboot the machine
  434. HRESULT hr = Reboot();
  435. ASSERT(SUCCEEDED(hr));
  436. ASSERT(exitCode == EXIT_CODE_SUCCESSFUL);
  437. }
  438. else
  439. {
  440. // user opted not to reboot the machine
  441. exitCode = EXIT_CODE_SUCCESSFUL_NO_REBOOT;
  442. }
  443. break;
  444. }
  445. case EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT:
  446. {
  447. // If the operation failed, then the wizard has gone into interactive
  448. // mode.
  449. RebootDialog dlg(true);
  450. if (dlg.ModalExecute(0))
  451. {
  452. // user opted to reboot the machine
  453. exitCode = EXIT_CODE_UNSUCCESSFUL;
  454. HRESULT hr = Reboot();
  455. ASSERT(SUCCEEDED(hr));
  456. }
  457. else
  458. {
  459. // user opted not to reboot the machine
  460. ASSERT(exitCode == EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT);
  461. }
  462. break;
  463. }
  464. default:
  465. {
  466. ASSERT(false);
  467. break;
  468. }
  469. }
  470. return exitCode;
  471. }
  472. ExitCode
  473. RunWizard()
  474. {
  475. LOG_FUNCTION(RunWizard);
  476. // this is necessary to use the hotlink-style static text control.
  477. // //
  478. // // BOOL b = LinkWindow_RegisterClass();
  479. // // ASSERT(b);
  480. Wizard wiz(
  481. IDS_WIZARD_TITLE,
  482. IDB_BANNER16,
  483. IDB_BANNER256,
  484. IDB_WATERMARK16,
  485. IDB_WATERMARK256);
  486. // Welcome must be first
  487. wiz.AddPage(new WelcomePage());
  488. // These are not in any particular order...
  489. wiz.AddPage(new AdminPasswordPage());
  490. wiz.AddPage(new ChildPage());
  491. wiz.AddPage(new ConfigureDnsClientPage());
  492. wiz.AddPage(new ConfirmationPage());
  493. wiz.AddPage(new CredentialsPage());
  494. wiz.AddPage(new DemotePage());
  495. wiz.AddPage(new DnsOnNetPage());
  496. wiz.AddPage(new DynamicDnsPage());
  497. wiz.AddPage(new FailurePage());
  498. wiz.AddPage(new FinishPage());
  499. wiz.AddPage(new ForestPage());
  500. wiz.AddPage(new ForestVersionPage());
  501. wiz.AddPage(new GcConfirmationPage());
  502. wiz.AddPage(new InstallTcpIpPage());
  503. wiz.AddPage(new NetbiosNamePage());
  504. wiz.AddPage(new NewDomainPage());
  505. wiz.AddPage(new NewSitePage());
  506. wiz.AddPage(new Paths2Page());
  507. wiz.AddPage(new PathsPage());
  508. wiz.AddPage(new PickSitePage());
  509. wiz.AddPage(new RASFixupPage());
  510. wiz.AddPage(new ReplicaOrMemberPage());
  511. wiz.AddPage(new ReplicaOrNewDomainPage());
  512. wiz.AddPage(new ReplicaPage());
  513. wiz.AddPage(new ReplicateFromMediaPage());
  514. wiz.AddPage(new SafeModePasswordPage());
  515. wiz.AddPage(new TreePage());
  516. ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL;
  517. switch (wiz.ModalExecute(Win::GetDesktopWindow()))
  518. {
  519. case -1:
  520. {
  521. popup.Error(
  522. Win::GetDesktopWindow(),
  523. E_FAIL,
  524. IDS_PROP_SHEET_FAILED);
  525. break;
  526. }
  527. case ID_PSREBOOTSYSTEM:
  528. {
  529. exitCode = HandleRebootCases();
  530. break;
  531. }
  532. default:
  533. {
  534. // do nothing.
  535. break;
  536. }
  537. }
  538. return exitCode;
  539. }
  540. // NTRAID#NTBUG9-350777-2001/04/24-sburns
  541. bool
  542. ShouldCancelBecauseMachineIsAppServer()
  543. {
  544. LOG_FUNCTION(ShouldCancelBecauseMachineIsAppServer);
  545. bool result = false;
  546. do
  547. {
  548. State& state = State::GetInstance();
  549. State::RunContext context = state.GetRunContext();
  550. if (context == State::NT5_DC)
  551. {
  552. // already a DC: nothing to gripe about.
  553. break;
  554. }
  555. OSVERSIONINFOEX info;
  556. ::ZeroMemory(&info, sizeof(info));
  557. HRESULT hr = Win::GetVersionEx(info);
  558. BREAK_ON_FAILED_HRESULT(hr);
  559. // you're running app server if you're running terminal server and
  560. // not single user terminal server.
  561. bool isAppServer =
  562. (info.wSuiteMask & VER_SUITE_TERMINAL)
  563. && !(info.wSuiteMask & VER_SUITE_SINGLEUSERTS);
  564. if (isAppServer)
  565. {
  566. // warn the user that promotion will whack the ts policy settings
  567. LOG(L"machine has app server installed");
  568. if (!state.RunHiddenUnattended())
  569. {
  570. if (
  571. popup.MessageBox(
  572. Win::GetDesktopWindow(),
  573. IDS_APP_SERVER_WARNING,
  574. MB_OKCANCEL) == IDCANCEL)
  575. {
  576. // user wishes to bail out.
  577. result = true;
  578. break;
  579. }
  580. }
  581. }
  582. }
  583. while (0);
  584. LOG_BOOL(result);
  585. return result;
  586. }
  587. ExitCode
  588. Start()
  589. {
  590. LOG_FUNCTION(Start);
  591. ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL;
  592. unsigned id = 0;
  593. do
  594. {
  595. // do the admin check first of all, cause others may fail for non-admin
  596. // 292749
  597. id = IsCurrentUserAdministrator() ? 0 : IDS_NOT_ADMIN;
  598. if (id)
  599. {
  600. break;
  601. }
  602. // If cert service is installed, we will probably break it on promote
  603. // or demote.
  604. // 324653
  605. id = CheckCertService();
  606. if (id)
  607. {
  608. break;
  609. }
  610. id = CheckSafeBootMode();
  611. if (id)
  612. {
  613. break;
  614. }
  615. // do the role change check before the platform check, as the platform
  616. // check may be unreliable after a demote.
  617. id = CheckRoleChangeState();
  618. if (id)
  619. {
  620. break;
  621. }
  622. id = CheckPlatform();
  623. if (id)
  624. {
  625. break;
  626. }
  627. id = CheckForNtfs5();
  628. if (id)
  629. {
  630. break;
  631. }
  632. id = CheckWindirSpace();
  633. if (id)
  634. {
  635. break;
  636. }
  637. id = CheckComputerWasRenamedAndNeedsReboot();
  638. if (id)
  639. {
  640. break;
  641. }
  642. }
  643. while(0);
  644. do
  645. {
  646. if (id)
  647. {
  648. popup.Error(
  649. Win::GetDesktopWindow(),
  650. String::load(id));
  651. break;
  652. }
  653. if (!IsComputerNameOk())
  654. {
  655. break;
  656. }
  657. if (ShouldCancelBecauseMachineIsAppServer())
  658. {
  659. break;
  660. }
  661. // NTRAID#NTBUG9-129955-2000/11/02-sburns left commented out until
  662. // PM decides what the real fix to this bug is.
  663. // if (!AreRequiredPortsAvailable())
  664. // {
  665. // break;
  666. // }
  667. // there's always one more thing to check. 120143
  668. if (IsLastReplicaOfNonDomainNamingContexts())
  669. {
  670. break;
  671. }
  672. exitCode = RunWizard();
  673. }
  674. while (0);
  675. LOG(String::format(L"exitCode = %1!d!", static_cast<int>(exitCode)));
  676. return exitCode;
  677. }
  678. void
  679. ShowCommandLineHelp()
  680. {
  681. // CODEWORK: replace this with WinHelp, someday
  682. popup.MessageBox(Win::GetDesktopWindow(), IDS_COMMAND_LINE_HELP, MB_OK);
  683. }
  684. int WINAPI
  685. WinMain(
  686. HINSTANCE hInstance,
  687. HINSTANCE /* hPrevInstance */ ,
  688. PSTR /* lpszCmdLine */ ,
  689. int /* nCmdShow */)
  690. {
  691. hResourceModuleHandle = hInstance;
  692. ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL;
  693. HRESULT hr = Win::CreateMutex(0, true, RUNTIME_NAME, dcpromoRunningMutex);
  694. if (hr == Win32ToHresult(ERROR_ALREADY_EXISTS))
  695. {
  696. popup.Error(Win::GetDesktopWindow(), IDS_ALREADY_RUNNING);
  697. }
  698. else
  699. {
  700. hr = ::CoInitialize(0);
  701. ASSERT(SUCCEEDED(hr));
  702. // change structure instance name so as not to accidentally offend
  703. // the sensibilities of the delicate reader.
  704. // NTRAID#NTBUG9-382719-2001/05/01-sburns
  705. INITCOMMONCONTROLSEX init_structure_not_to_contain_a_naughty_word;
  706. init_structure_not_to_contain_a_naughty_word.dwSize =
  707. sizeof(init_structure_not_to_contain_a_naughty_word);
  708. init_structure_not_to_contain_a_naughty_word.dwICC =
  709. ICC_ANIMATE_CLASS | ICC_USEREX_CLASSES;
  710. BOOL init =
  711. ::InitCommonControlsEx(&init_structure_not_to_contain_a_naughty_word);
  712. ASSERT(init);
  713. State::Init();
  714. if (State::GetInstance().NeedsCommandLineHelp())
  715. {
  716. ShowCommandLineHelp();
  717. }
  718. else
  719. {
  720. exitCode = Start();
  721. }
  722. State::Destroy();
  723. }
  724. return static_cast<int>(exitCode);
  725. }