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.
1095 lines
27 KiB
1095 lines
27 KiB
// Copyright (c) 1997-1999 Microsoft Corporation
|
|
//
|
|
// domain controller promotion wizard, Mark II
|
|
//
|
|
// 12-12-97 sburns
|
|
|
|
|
|
|
|
#include "headers.hxx"
|
|
#include "common.hpp"
|
|
#include "adpass.hpp"
|
|
#include "ApplicationPartitionPage.hpp"
|
|
#include "ApplicationPartitionConfirmationPage.hpp"
|
|
#include "BadComputerNameDialog.hpp"
|
|
#include "CheckDomainUpgradedPage.hpp"
|
|
#include "CheckPortAvailability.hpp"
|
|
#include "ChildPage.hpp"
|
|
#include "CredentialsPage.hpp"
|
|
#include "ConfirmationPage.hpp"
|
|
#include "DemotePage.hpp"
|
|
#include "DynamicDnsPage.hpp"
|
|
#include "ConfigureDnsClientPage.hpp"
|
|
#include "DnsOnNetPage.hpp"
|
|
#include "FailurePage.hpp"
|
|
#include "finish.hpp"
|
|
#include "ForcedDemotionPage.hpp"
|
|
#include "ForestPage.hpp"
|
|
#include "ForestVersionPage.hpp"
|
|
#include "GcConfirmationPage.hpp"
|
|
#include "NetbiosNamePage.hpp"
|
|
#include "NewDomainPage.hpp"
|
|
#include "NewSitePage.hpp"
|
|
#include "NonDomainNc.hpp"
|
|
#include "NonRfcComputerNameDialog.hpp"
|
|
#include "PathsPage.hpp"
|
|
#include "Paths2Page.hpp"
|
|
#include "PickSitePage.hpp"
|
|
#include "rasfixup.hpp"
|
|
// #include "ReadmePage.hpp"
|
|
#include "RebootDialog.hpp"
|
|
#include "ReplicaOrNewDomainPage.hpp"
|
|
#include "ReplicateFromMediaPage.hpp"
|
|
#include "ReplicaPage.hpp"
|
|
#include "ReplicaOrMemberPage.hpp"
|
|
#include "resource.h"
|
|
#include "safemode.hpp"
|
|
#include "SecureCommWarningPage.hpp"
|
|
#include "state.hpp"
|
|
#include "InstallTcpIpPage.hpp"
|
|
#include "TreePage.hpp"
|
|
#include "WelcomePage.hpp"
|
|
#include <ntverp.h>
|
|
|
|
|
|
|
|
HINSTANCE hResourceModuleHandle = 0;
|
|
const wchar_t* HELPFILE_NAME = 0; // no context help available
|
|
|
|
|
|
|
|
// don't change this: it is also the name of a mutex that the net id ui
|
|
// uses to determine if dcpromo is running.
|
|
|
|
const wchar_t* RUNTIME_NAME = L"dcpromoui";
|
|
|
|
DWORD DEFAULT_LOGGING_OPTIONS =
|
|
Log::OUTPUT_TO_FILE
|
|
| Log::OUTPUT_FUNCCALLS
|
|
| Log::OUTPUT_LOGS
|
|
| Log::OUTPUT_ERRORS
|
|
| Log::OUTPUT_HEADER;
|
|
|
|
|
|
// a system modal popup thingy
|
|
Popup popup(IDS_WIZARD_TITLE, true);
|
|
|
|
// this is the mutex that indicates the dcpromo is running.
|
|
|
|
HANDLE dcpromoRunningMutex = INVALID_HANDLE_VALUE;
|
|
|
|
|
|
|
|
// these are the valid exit codes returned from the dcpromo.exe process
|
|
|
|
enum ExitCode
|
|
{
|
|
// the operation failed.
|
|
|
|
EXIT_CODE_UNSUCCESSFUL = 0,
|
|
|
|
// the operation succeeded
|
|
|
|
EXIT_CODE_SUCCESSFUL = 1,
|
|
|
|
// the operation succeeded, and the user opted not to have the wizard
|
|
// restart the machine, either manually or by specifying
|
|
// RebootOnSuccess=NoAndNoPromptEither in the answerfile
|
|
|
|
EXIT_CODE_SUCCESSFUL_NO_REBOOT = 2,
|
|
|
|
// the operation failed, but the machine needs to be rebooted anyway
|
|
|
|
EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT = 3
|
|
};
|
|
|
|
|
|
|
|
// Checks the platform and os version number. Returns the resource ID of the
|
|
// string to present to the user if platform/os ver requirements are not met,
|
|
// or 0 if they are met.
|
|
|
|
unsigned
|
|
CheckPlatform()
|
|
{
|
|
LOG_FUNCTION(CheckPlatform);
|
|
|
|
unsigned retval = 0;
|
|
|
|
Computer::Role role = State::GetInstance().GetComputer().GetRole();
|
|
switch (role)
|
|
{
|
|
case Computer::STANDALONE_WORKSTATION:
|
|
case Computer::MEMBER_WORKSTATION:
|
|
{
|
|
retval = IDS_WORKSTATION_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
case Computer::STANDALONE_SERVER:
|
|
case Computer::MEMBER_SERVER:
|
|
case Computer::PRIMARY_CONTROLLER:
|
|
case Computer::BACKUP_CONTROLLER:
|
|
{
|
|
// check OS version
|
|
|
|
OSVERSIONINFOEX info;
|
|
HRESULT hr = Win::GetVersionEx(info);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
if (
|
|
|
|
// require the same version for which we were built.
|
|
// NTRAID#NTBUG9-591686-2002/04/12-sburns
|
|
|
|
info.dwPlatformId != VER_PLATFORM_WIN32_NT
|
|
|| !( info.dwMajorVersion == VER_PRODUCTMAJORVERSION
|
|
&& info.dwMinorVersion == VER_PRODUCTMINORVERSION))
|
|
{
|
|
retval = IDS_NT51_REQUIRED;
|
|
break;
|
|
}
|
|
|
|
if (
|
|
// if a web blade ...
|
|
|
|
info.wSuiteMask & VER_SUITE_BLADE
|
|
|
|
// or, an appliance ...
|
|
|
|
|| (info.wSuiteMask & VER_SUITE_EMBEDDED_RESTRICTED
|
|
|
|
// that is not advanced or data center server ...
|
|
|
|
&& !(info.wSuiteMask & (VER_SUITE_ENTERPRISE | VER_SUITE_DATACENTER)) ) )
|
|
{
|
|
// .. then don't allow promotion.
|
|
|
|
// NTRAID#NTBUG9-195265-2001/04/03-sburns
|
|
// NTRAID#NTBUG9-590937-2002/04/15-sburns
|
|
|
|
retval = IDS_WEB_BLADE_NOT_SUPPORTED;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
// Checks the role change state of the machine. Returns the resource ID of
|
|
// the string to present to the user if the state is such that another role
|
|
// change cannot be attempted, or 0 if a role change attempt may proceed.
|
|
|
|
unsigned
|
|
CheckRoleChangeState()
|
|
{
|
|
LOG_FUNCTION(CheckRoleChangeState);
|
|
|
|
unsigned retval = 0;
|
|
|
|
// check to see if a role change has taken place, or is in progress.
|
|
|
|
DSROLE_OPERATION_STATE_INFO* info = 0;
|
|
HRESULT hr = MyDsRoleGetPrimaryDomainInformation(0, info);
|
|
if (SUCCEEDED(hr) && info)
|
|
{
|
|
switch (info->OperationState)
|
|
{
|
|
case DsRoleOperationIdle:
|
|
{
|
|
// do nothing
|
|
break;
|
|
}
|
|
case DsRoleOperationActive:
|
|
{
|
|
// a role change operation is underway
|
|
retval = IDS_ROLE_CHANGE_IN_PROGRESS;
|
|
break;
|
|
}
|
|
case DsRoleOperationNeedReboot:
|
|
{
|
|
// a role change has already taken place, need to reboot before
|
|
// attempting another.
|
|
retval = IDS_ROLE_CHANGE_NEEDS_REBOOT;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
::DsRoleFreeMemory(info);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
// Checks for the presence of at least 1 logical drive formatted with NTFS5.
|
|
// Returns the resource ID of the string to present to the user if no such
|
|
// drive is found (which implies that the user will not be able to pick a
|
|
// sysvol path that is formatted w/ NTFS5, which implies that proceeding would
|
|
// be a waste of time.
|
|
|
|
unsigned
|
|
CheckForNtfs5()
|
|
{
|
|
LOG_FUNCTION(CheckForNtfs5);
|
|
|
|
if (GetFirstNtfs5HardDrive().empty())
|
|
{
|
|
return IDS_NO_NTFS5_DRIVES;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// Checks if the machine is running in safeboot mode. You can't run dcpromo
|
|
// while in safeboot mode.
|
|
|
|
unsigned
|
|
CheckSafeBootMode()
|
|
{
|
|
LOG_FUNCTION(CheckSafeBootMode);
|
|
|
|
static const String
|
|
SAFEBOOT_KEY(L"System\\CurrentControlSet\\Control\\Safeboot\\Option");
|
|
|
|
do
|
|
{
|
|
RegistryKey key;
|
|
|
|
HRESULT hr = key.Open(HKEY_LOCAL_MACHINE, SAFEBOOT_KEY);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
DWORD mode = 0;
|
|
hr = key.GetValue(L"OptionValue", mode);
|
|
|
|
if (mode)
|
|
{
|
|
return IDS_SAFEBOOT_MODE;
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
unsigned
|
|
CheckCertService()
|
|
{
|
|
LOG_FUNCTION(CheckCertService);
|
|
|
|
// If not a downlevel DC upgrade, then refuse to run until cert service
|
|
// is removed. 356399
|
|
|
|
State::RunContext context = State::GetInstance().GetRunContext();
|
|
if (context != State::BDC_UPGRADE && context != State::PDC_UPGRADE)
|
|
{
|
|
if (NTService(L"CertSvc").IsInstalled())
|
|
{
|
|
return IDS_CERT_SERVICE_IS_INSTALLED;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
unsigned
|
|
CheckWindirSpace()
|
|
{
|
|
LOG_FUNCTION(CheckWindirSpace);
|
|
|
|
// if you change this, change the error message resource too.
|
|
|
|
static const unsigned WINDIR_MIN_SPACE_MB = 20;
|
|
|
|
String windir = Win::GetSystemWindowsDirectory();
|
|
|
|
if (!CheckDiskSpace(windir, 20))
|
|
{
|
|
return IDS_WINDIR_LOW_SPACE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// NTRAID#NTBUG9-199759-2000/10/27-sburns
|
|
|
|
unsigned
|
|
CheckComputerWasRenamedAndNeedsReboot()
|
|
{
|
|
LOG_FUNCTION(CheckComputerWasRenamedAndNeedsReboot);
|
|
|
|
if (ComputerWasRenamedAndNeedsReboot())
|
|
{
|
|
return IDS_NAME_CHANGE_NEEDS_REBOOT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// Start the Network Identification UI (aka Computer Name UI). After calling
|
|
// this function, the app must not initiate a role change. It should
|
|
// terminate.
|
|
|
|
void
|
|
LaunchNetId()
|
|
{
|
|
LOG_FUNCTION(LaunchNetId);
|
|
ASSERT(dcpromoRunningMutex != INVALID_HANDLE_VALUE);
|
|
|
|
// net id ui attempts acquisition of our mutex to determine if we are
|
|
// running. So, before starting the net id ui, we need to close the
|
|
// mutex. Otherwise, we would create a race condition between the start of
|
|
// the net id ui and the closure of this app. (And yes, cynical reader, I
|
|
// did think of that before actually encountering a problem.)
|
|
|
|
do
|
|
{
|
|
Win::CloseHandle(dcpromoRunningMutex);
|
|
|
|
// It would be extraordinarily unlikely, but in case the mutex close
|
|
// fails, we're gonna start the net id ui anyway and risk the race
|
|
// condition.
|
|
|
|
String sys32Folder = Win::GetSystemDirectory();
|
|
|
|
PROCESS_INFORMATION procInfo;
|
|
|
|
// REVIEWED-2002/02/25-sburns correct byte count passed
|
|
|
|
::ZeroMemory(&procInfo, sizeof procInfo);
|
|
|
|
STARTUPINFO startup;
|
|
|
|
// REVIEWED-2002/02/25-sburns correct byte count passed
|
|
|
|
::ZeroMemory(&startup, sizeof startup);
|
|
|
|
LOG(L"Calling CreateProcess");
|
|
|
|
String commandLine(L"shell32.dll,Control_RunDLL sysdm.cpl,,1");
|
|
|
|
// REVIEWED-2002/02/26-sburns wrapper requires full path to app
|
|
|
|
HRESULT hr =
|
|
Win::CreateProcess(
|
|
sys32Folder + L"\\rundll32.exe",
|
|
commandLine,
|
|
0,
|
|
String(),
|
|
startup,
|
|
procInfo);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
Win::CloseHandle(procInfo.hThread);
|
|
Win::CloseHandle(procInfo.hProcess);
|
|
}
|
|
while (0);
|
|
}
|
|
|
|
|
|
|
|
// Return false if the local machine's dns hostname is bad, also pop up an
|
|
// error dialog. Return true if the name is OK. A bad name is one we believe
|
|
// will have problems being registered in DNS after a promotion. The user
|
|
// must fix a bad name before proceeding.
|
|
|
|
bool
|
|
IsComputerNameOk()
|
|
{
|
|
LOG_FUNCTION(IsComputerNameOk);
|
|
|
|
bool result = true;
|
|
|
|
do
|
|
{
|
|
State& state = State::GetInstance();
|
|
State::RunContext context = state.GetRunContext();
|
|
if (
|
|
context == State::BDC_UPGRADE
|
|
|| context == State::PDC_UPGRADE
|
|
|| context == State::NT5_DC
|
|
|| !IsTcpIpInstalled() )
|
|
{
|
|
// If the machine is already a DC, then we don't worry about the name.
|
|
//
|
|
// If the machine is a downlevel DC undergoing upgrade, then the name
|
|
// can't be changed until dcpromo is complete. So, we say nothing now,
|
|
// but remind the user to rename the machine in the Finish Page.
|
|
//
|
|
// If TCP/IP is not installed, then the machine has no hostname
|
|
// to check. In this case, we will check for that with the
|
|
// InstallTcpIpPage
|
|
|
|
ASSERT(result == true);
|
|
|
|
break;
|
|
}
|
|
|
|
// Then check the computer name to ensure that it can be registered in
|
|
// DNS.
|
|
|
|
String hostname =
|
|
Win::GetComputerNameEx(::ComputerNamePhysicalDnsHostname);
|
|
|
|
DNS_STATUS status =
|
|
MyDnsValidateName(hostname, ::DnsNameHostnameLabel);
|
|
|
|
switch (status)
|
|
{
|
|
case DNS_ERROR_NON_RFC_NAME:
|
|
{
|
|
// Don't pester the user if we're running unattended
|
|
// NTRAID#NTBUG9-538475-2002/04/19-sburns
|
|
|
|
if (state.RunHiddenUnattended())
|
|
{
|
|
LOG(L"skipping non-RFC computer name warning");
|
|
|
|
// continue on with the non-rfc name
|
|
|
|
ASSERT(result == true);
|
|
break;
|
|
}
|
|
|
|
INT_PTR dlgResult =
|
|
NonRfcComputerNameDialog(hostname).ModalExecute(0);
|
|
|
|
switch (dlgResult)
|
|
{
|
|
case NonRfcComputerNameDialog::CONTINUE:
|
|
{
|
|
// continue on with the non-rfc name
|
|
|
|
ASSERT(result == true);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// close the wizard and rename.
|
|
|
|
result = false;
|
|
|
|
// after calling this, we must not allow any promote
|
|
// operation. We will fall out of this function, then
|
|
// end the app.
|
|
|
|
LaunchNetId();
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DNS_ERROR_NUMERIC_NAME:
|
|
{
|
|
result = false;
|
|
|
|
String message =
|
|
String::format(
|
|
IDS_COMPUTER_NAME_IS_NUMERIC,
|
|
hostname.c_str());
|
|
|
|
BadComputerNameDialog(message).ModalExecute(0);
|
|
|
|
break;
|
|
}
|
|
case DNS_ERROR_INVALID_NAME_CHAR:
|
|
case ERROR_INVALID_NAME:
|
|
{
|
|
result = false;
|
|
|
|
String message =
|
|
String::format(
|
|
IDS_COMPUTER_NAME_HAS_BAD_CHARS,
|
|
hostname.c_str());
|
|
|
|
BadComputerNameDialog(message).ModalExecute(0);
|
|
|
|
break;
|
|
}
|
|
case ERROR_SUCCESS:
|
|
default:
|
|
{
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
LOG(result ? L"true" : L"false");
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// Reboots the machine with the special dcpromo reason codes
|
|
// NTRAID#NTBUG9-689581-2002/08/19-sburns
|
|
|
|
HRESULT
|
|
DcpromoReboot()
|
|
{
|
|
LOG_FUNCTION(DcpromoReboot);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
AutoTokenPrivileges privs(SE_SHUTDOWN_NAME);
|
|
hr = privs.Enable();
|
|
|
|
DWORD minorReason = SHTDN_REASON_MINOR_DC_PROMOTION;
|
|
switch (State::GetInstance().GetOperation())
|
|
{
|
|
case State::REPLICA:
|
|
case State::FOREST:
|
|
case State::TREE:
|
|
case State::CHILD:
|
|
{
|
|
// do nothing
|
|
|
|
break;
|
|
}
|
|
case State::DEMOTE:
|
|
case State::ABORT_BDC_UPGRADE:
|
|
{
|
|
// we treat abort bdc upgrade as a demotion because it is one,
|
|
// in the sense that the machine was once a DC and now it is not.
|
|
|
|
minorReason = SHTDN_REASON_MINOR_DC_DEMOTION;
|
|
break;
|
|
}
|
|
case State::NONE:
|
|
default:
|
|
{
|
|
// we're insane.
|
|
|
|
ASSERT(false);
|
|
LOG(L"unknown operation!");
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
BOOL succeeded =
|
|
::InitiateSystemShutdownEx(
|
|
0,
|
|
const_cast<PWSTR>(String::load(IDS_REBOOT_MESSAGE).c_str()),
|
|
|
|
// zero timeout -- BAM! you're dead! -- this to avoid a winlogon
|
|
// race condition.
|
|
// NTRAID#NTBUG9-727439-2002/10/24-sburns
|
|
0, // 15,
|
|
|
|
FALSE,
|
|
TRUE,
|
|
SHTDN_REASON_FLAG_PLANNED
|
|
| SHTDN_REASON_MAJOR_OPERATINGSYSTEM
|
|
| minorReason);
|
|
if (!succeeded)
|
|
{
|
|
hr = Win::GetLastErrorAsHresult();
|
|
}
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
popup.Error(
|
|
Win::GetDesktopWindow(),
|
|
hr,
|
|
IDS_CANT_REBOOT);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// NTRAID#NTBUG9-346120-2001/04/04-sburns
|
|
|
|
ExitCode
|
|
HandleRebootCases()
|
|
{
|
|
LOG_FUNCTION(HandleRebootCases);
|
|
|
|
// There are two possible reasons for needing to reboot the machine:
|
|
// the operation was successful, or the operation failed, but in the
|
|
// attempt the machine's joined state changed.
|
|
|
|
State& state = State::GetInstance();
|
|
|
|
ExitCode exitCode =
|
|
(state.GetOperationResultsCode() == State::SUCCESS)
|
|
? EXIT_CODE_SUCCESSFUL
|
|
: EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT;
|
|
|
|
switch (exitCode)
|
|
{
|
|
case EXIT_CODE_SUCCESSFUL:
|
|
{
|
|
if (state.RunHiddenUnattended())
|
|
{
|
|
String option =
|
|
state.GetAnswerFileOption(AnswerFile::OPTION_REBOOT);
|
|
if (option.icompare(AnswerFile::VALUE_YES) == 0)
|
|
{
|
|
ASSERT(exitCode == EXIT_CODE_SUCCESSFUL);
|
|
HRESULT hr = DcpromoReboot();
|
|
if (FAILED(hr))
|
|
{
|
|
exitCode = EXIT_CODE_SUCCESSFUL_NO_REBOOT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if (option.icompare(AnswerFile::VALUE_NO_DONT_PROMPT) == 0)
|
|
{
|
|
// user opted not to reboot the machine via answerfile
|
|
|
|
LOG(L"Not rebooting, and not prompting either");
|
|
|
|
exitCode = EXIT_CODE_SUCCESSFUL_NO_REBOOT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RebootDialog dlg(false);
|
|
if (dlg.ModalExecute(0))
|
|
{
|
|
// user opted to reboot the machine
|
|
|
|
HRESULT hr = DcpromoReboot();
|
|
if (FAILED(hr))
|
|
{
|
|
exitCode = EXIT_CODE_SUCCESSFUL_NO_REBOOT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// user opted not to reboot the machine
|
|
|
|
exitCode = EXIT_CODE_SUCCESSFUL_NO_REBOOT;
|
|
}
|
|
break;
|
|
}
|
|
case EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT:
|
|
{
|
|
// If the operation failed, then the wizard has gone into interactive
|
|
// mode.
|
|
|
|
RebootDialog dlg(true);
|
|
if (dlg.ModalExecute(0))
|
|
{
|
|
// user opted to reboot the machine
|
|
|
|
exitCode = EXIT_CODE_UNSUCCESSFUL;
|
|
HRESULT hr = DcpromoReboot();
|
|
if (FAILED(hr))
|
|
{
|
|
exitCode = EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// user opted not to reboot the machine
|
|
|
|
ASSERT(exitCode == EXIT_CODE_UNSUCCESSFUL_NEEDS_REBOOT);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return exitCode;
|
|
}
|
|
|
|
|
|
|
|
ExitCode
|
|
RunWizard()
|
|
{
|
|
LOG_FUNCTION(RunWizard);
|
|
|
|
Wizard wiz(
|
|
IDS_WIZARD_TITLE,
|
|
IDB_BANNER16,
|
|
IDB_BANNER256,
|
|
IDB_WATERMARK16,
|
|
IDB_WATERMARK256);
|
|
|
|
// Welcome must be first
|
|
|
|
wiz.AddPage(new WelcomePage());
|
|
|
|
// These are not in any particular order...
|
|
// CODEWORK: Someday it might be useful to split this into two separate
|
|
// sets of pages for promote and demote.
|
|
|
|
wiz.AddPage(new AdminPasswordPage());
|
|
wiz.AddPage(new ApplicationPartitionPage());
|
|
wiz.AddPage(new ApplicationPartitionConfirmationPage());
|
|
wiz.AddPage(new CheckDomainUpgradedPage());
|
|
wiz.AddPage(new ChildPage());
|
|
wiz.AddPage(new ConfigureDnsClientPage());
|
|
wiz.AddPage(new ConfirmationPage());
|
|
wiz.AddPage(new CredentialsPage());
|
|
wiz.AddPage(new DemotePage());
|
|
wiz.AddPage(new DnsOnNetPage());
|
|
wiz.AddPage(new DynamicDnsPage());
|
|
wiz.AddPage(new FailurePage());
|
|
wiz.AddPage(new FinishPage());
|
|
wiz.AddPage(new ForcedDemotionPage());
|
|
wiz.AddPage(new ForestPage());
|
|
wiz.AddPage(new ForestVersionPage());
|
|
wiz.AddPage(new GcConfirmationPage());
|
|
wiz.AddPage(new InstallTcpIpPage());
|
|
wiz.AddPage(new NetbiosNamePage());
|
|
wiz.AddPage(new NewDomainPage());
|
|
wiz.AddPage(new NewSitePage());
|
|
wiz.AddPage(new Paths2Page());
|
|
wiz.AddPage(new PathsPage());
|
|
wiz.AddPage(new PickSitePage());
|
|
wiz.AddPage(new RASFixupPage());
|
|
// wiz.AddPage(new ReadmePage());
|
|
wiz.AddPage(new ReplicaOrMemberPage());
|
|
wiz.AddPage(new ReplicaOrNewDomainPage());
|
|
wiz.AddPage(new ReplicaPage());
|
|
wiz.AddPage(new ReplicateFromMediaPage());
|
|
wiz.AddPage(new SafeModePasswordPage());
|
|
wiz.AddPage(new SecureCommWarningPage());
|
|
wiz.AddPage(new TreePage());
|
|
|
|
ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL;
|
|
|
|
switch (wiz.ModalExecute(Win::GetDesktopWindow()))
|
|
{
|
|
case -1:
|
|
{
|
|
popup.Error(
|
|
Win::GetDesktopWindow(),
|
|
E_FAIL,
|
|
IDS_PROP_SHEET_FAILED);
|
|
break;
|
|
}
|
|
case ID_PSREBOOTSYSTEM:
|
|
{
|
|
exitCode = HandleRebootCases();
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// do nothing.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return exitCode;
|
|
}
|
|
|
|
|
|
|
|
// NTRAID#NTBUG9-350777-2001/04/24-sburns
|
|
|
|
bool
|
|
ShouldCancelBecauseMachineIsAppServer()
|
|
{
|
|
LOG_FUNCTION(ShouldCancelBecauseMachineIsAppServer);
|
|
|
|
bool result = false;
|
|
|
|
do
|
|
{
|
|
State& state = State::GetInstance();
|
|
State::RunContext context = state.GetRunContext();
|
|
if (context == State::NT5_DC)
|
|
{
|
|
// already a DC: nothing to gripe about.
|
|
|
|
break;
|
|
}
|
|
|
|
OSVERSIONINFOEX info;
|
|
HRESULT hr = Win::GetVersionEx(info);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
// you're running app server if you're running terminal server and
|
|
// not single user terminal server.
|
|
|
|
bool isAppServer =
|
|
(info.wSuiteMask & VER_SUITE_TERMINAL)
|
|
&& !(info.wSuiteMask & VER_SUITE_SINGLEUSERTS);
|
|
|
|
if (isAppServer)
|
|
{
|
|
// warn the user that promotion will whack the ts policy settings
|
|
|
|
LOG(L"machine has app server installed");
|
|
|
|
if (!state.RunHiddenUnattended())
|
|
{
|
|
if (
|
|
popup.MessageBox(
|
|
Win::GetDesktopWindow(),
|
|
IDS_APP_SERVER_WARNING,
|
|
MB_OKCANCEL) == IDCANCEL)
|
|
{
|
|
// user wishes to bail out.
|
|
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (0);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
ExitCode
|
|
Start()
|
|
{
|
|
LOG_FUNCTION(Start);
|
|
|
|
ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL;
|
|
unsigned id = 0;
|
|
do
|
|
{
|
|
// do the admin check first of all, cause others may fail for non-admin
|
|
// 292749
|
|
|
|
id = IsCurrentUserAdministrator() ? 0 : IDS_NOT_ADMIN;
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If cert service is installed, we will probably break it on promote
|
|
// or demote.
|
|
// 324653
|
|
|
|
id = CheckCertService();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
id = CheckSafeBootMode();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// do the role change check before the platform check, as the platform
|
|
// check may be unreliable after a demote.
|
|
|
|
id = CheckRoleChangeState();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
id = CheckPlatform();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
id = CheckForNtfs5();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
id = CheckWindirSpace();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
|
|
id = CheckComputerWasRenamedAndNeedsReboot();
|
|
if (id)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while(0);
|
|
|
|
do
|
|
{
|
|
if (id)
|
|
{
|
|
popup.Error(
|
|
Win::GetDesktopWindow(),
|
|
String::load(id));
|
|
break;
|
|
}
|
|
|
|
if (!IsComputerNameOk())
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ShouldCancelBecauseMachineIsAppServer())
|
|
{
|
|
break;
|
|
}
|
|
|
|
// NTRAID#NTBUG9-129955-2000/11/02-sburns left commented out until
|
|
// PM decides what the real fix to this bug is.
|
|
|
|
// if (!AreRequiredPortsAvailable())
|
|
// {
|
|
// break;
|
|
// }
|
|
|
|
exitCode = RunWizard();
|
|
}
|
|
while (0);
|
|
|
|
LOG(String::format(L"exitCode = %1!d!", static_cast<int>(exitCode)));
|
|
|
|
return exitCode;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ShowCommandLineHelp()
|
|
{
|
|
// CODEWORK: replace this with WinHelp, someday
|
|
|
|
popup.MessageBox(Win::GetDesktopWindow(), IDS_COMMAND_LINE_HELP, MB_OK);
|
|
}
|
|
|
|
|
|
|
|
int WINAPI
|
|
WinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE /* hPrevInstance */ ,
|
|
PSTR /* lpszCmdLine */ ,
|
|
int /* nCmdShow */)
|
|
{
|
|
hResourceModuleHandle = hInstance;
|
|
|
|
ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL;
|
|
|
|
try
|
|
{
|
|
HRESULT hr =
|
|
|
|
// ISSUE-2002/02/25-sburns This is a global named object. See
|
|
// NTRAID#NTBUG9-525195-2002/02/25-sburns
|
|
|
|
Win::CreateMutex(
|
|
0,
|
|
true,
|
|
|
|
// The mutex name has the "Global" prefix so ts users will see it.
|
|
// NTRAID#NTBUG9-404808-2001/05/29-sburns
|
|
|
|
// If you ever change this, change IsDcpromoRunning in
|
|
// burnslib\src\dsutil.cpp too.
|
|
// NTRAID#NTBUG9-498351-2001/11/21-sburns
|
|
|
|
String(L"Global\\") + RUNTIME_NAME,
|
|
dcpromoRunningMutex);
|
|
if (hr == Win32ToHresult(ERROR_ALREADY_EXISTS))
|
|
{
|
|
// Close the mutex so that the truly clueless admin won't get confused
|
|
// that the popup we're about to raise should be closed before he tries
|
|
// to launch the wizard again. Sheesh!
|
|
// NTRAID#NTBUG9-404808-2001/05/29-sburns (bis)
|
|
|
|
Win::CloseHandle(dcpromoRunningMutex);
|
|
popup.Error(Win::GetDesktopWindow(), IDS_ALREADY_RUNNING);
|
|
}
|
|
else
|
|
{
|
|
AutoCoInitialize coInit;
|
|
hr = coInit.Result();
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
// change structure instance name so as not to accidentally offend
|
|
// the sensibilities of the delicate reader.
|
|
// NTRAID#NTBUG9-382719-2001/05/01-sburns
|
|
|
|
INITCOMMONCONTROLSEX init_structure_not_to_contain_a_naughty_word;
|
|
init_structure_not_to_contain_a_naughty_word.dwSize =
|
|
sizeof(init_structure_not_to_contain_a_naughty_word);
|
|
init_structure_not_to_contain_a_naughty_word.dwICC =
|
|
ICC_ANIMATE_CLASS | ICC_USEREX_CLASSES;
|
|
|
|
BOOL init =
|
|
::InitCommonControlsEx(&init_structure_not_to_contain_a_naughty_word);
|
|
ASSERT(init);
|
|
|
|
State::Init();
|
|
|
|
if (State::GetInstance().NeedsCommandLineHelp())
|
|
{
|
|
ShowCommandLineHelp();
|
|
}
|
|
else
|
|
{
|
|
exitCode = Start();
|
|
}
|
|
|
|
State::Destroy();
|
|
}
|
|
}
|
|
catch (Error& err)
|
|
{
|
|
popup.Error(Win::GetDesktopWindow(), err.GetMessage());
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG(L"unhandled exception caught");
|
|
|
|
popup.Error(Win::GetDesktopWindow(), IDS_UNHANDLED_EXCEPTION);
|
|
}
|
|
|
|
return static_cast<int>(exitCode);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|