// Copyright (c) 2001 Microsoft Corporation // // File: cys.cpp // // Synopsis: Configure Your Server Wizard main // // History: 02/02/2001 JeffJon Created #include "pch.h" #include "resource.h" #include "InstallationUnitProvider.h" // include the wizard pages #include "BeforeBeginPage.h" #include "CustomServerPage.h" #include "DecisionPage.h" #include "DnsForwarderPage.h" #include "DomainPage.h" #include "ExpressDHCPPage.h" #include "ExpressDNSPage.h" #include "ExpressRebootPage.h" #include "FileServerPage.h" #include "FinishPage.h" #include "IndexingPage.h" #include "InstallationProgressPage.h" #include "MilestonePage.h" #include "NetbiosPage.h" #include "POP3Page.h" #include "PrintServerPage.h" #include "RemoteDesktopPage.h" #include "UninstallMilestonePage.h" #include "UninstallProgressPage.h" #include "WebApplicationPage.h" #include "WelcomePage.h" #include "ExpressRebootPage.h" HINSTANCE hResourceModuleHandle = 0; const wchar_t* HELPFILE_NAME = 0; // no context help available // This is the name of a mutex that is used to see if CYS is running const wchar_t* RUNTIME_NAME = L"cysui"; DWORD DEFAULT_LOGGING_OPTIONS = Log::OUTPUT_TO_FILE | Log::OUTPUT_FUNCCALLS | Log::OUTPUT_LOGS | Log::OUTPUT_ERRORS | Log::OUTPUT_HEADER | Log::OUTPUT_RUN_TIME; // a system modal popup thingy Popup popup(IDS_WIZARD_TITLE, true); // this is the mutex that indicates that CYS is running. HANDLE cysRunningMutex = INVALID_HANDLE_VALUE; // This is a brush that is used to paint all the backgrounds. It // needs to be created and deleted from inside main HBRUSH brush = 0; // these are the valid exit codes returned from the cys.exe process enum ExitCode { // the operation failed. EXIT_CODE_UNSUCCESSFUL = 0, // the operation succeeded EXIT_CODE_SUCCESSFUL = 1, // other exit codes can be added here... }; enum StartPages { CYS_WELCOME_PAGE = 0, CYS_BEFORE_BEGIN_PAGE, CYS_EXPRESS_REBOOT_PAGE, CYS_FINISH_PAGE }; UINT TerminalServerPostBoot() { LOG_FUNCTION(TerminalServerPostBoot); UINT startPage = CYS_WELCOME_PAGE; InstallationUnitProvider::GetInstance(). SetCurrentInstallationUnit(TERMINALSERVER_SERVER); // Create the log file bool logFileAvailable = false; String logName; HANDLE logfileHandle = AppendLogFile( CYS_LOGFILE_NAME, logName); if (logfileHandle && logfileHandle != INVALID_HANDLE_VALUE) { LOG(String::format(L"New log file was created: %1", logName.c_str())); logFileAvailable = true; } else { LOG(L"Unable to create the log file!!!"); logFileAvailable = false; } // Prepare the finish dialog TerminalServerInstallationUnit& tsInstallationUnit = InstallationUnitProvider::GetInstance().GetTerminalServerInstallationUnit(); // Make sure the installation unit knows we are doing an install tsInstallationUnit.SetInstalling(true); if (tsInstallationUnit.GetApplicationMode() == 1) { CYS_APPEND_LOG(String::load(IDS_LOG_TERMINAL_SERVER_REBOOT_SUCCESS)); tsInstallationUnit.SetInstallResult(INSTALL_SUCCESS); startPage = CYS_FINISH_PAGE; } else { // Failed to install Terminal Server CYS_APPEND_LOG(String::load(IDS_LOG_TERMINAL_SERVER_REBOOT_FAILED)); tsInstallationUnit.SetInstallResult(INSTALL_FAILURE); startPage = CYS_FINISH_PAGE; } CYS_APPEND_LOG(L"\r\n"); // Close the log file Win::CloseHandle(logfileHandle); LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT TerminalServerUninstallPostBoot() { LOG_FUNCTION(TerminalServerUninstallPostBoot); UINT startPage = CYS_WELCOME_PAGE; InstallationUnitProvider::GetInstance(). SetCurrentInstallationUnit(TERMINALSERVER_SERVER); // Create the log file bool logFileAvailable = false; String logName; HANDLE logfileHandle = AppendLogFile( CYS_LOGFILE_NAME, logName); if (logfileHandle && logfileHandle != INVALID_HANDLE_VALUE) { LOG(String::format(L"New log file was created: %1", logName.c_str())); logFileAvailable = true; } else { LOG(L"Unable to create the log file!!!"); logFileAvailable = false; } // Prepare the finish dialog TerminalServerInstallationUnit& tsInstallationUnit = InstallationUnitProvider::GetInstance().GetTerminalServerInstallationUnit(); // Make sure the installation unit knows we are doing an uninstall tsInstallationUnit.SetInstalling(false); if (tsInstallationUnit.GetApplicationMode() == 0) { CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_TERMINAL_SERVER_SUCCESS)); tsInstallationUnit.SetUninstallResult(UNINSTALL_SUCCESS); startPage = CYS_FINISH_PAGE; } else { // Failed to uninstall Terminal Server CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_TERMINAL_SERVER_FAILED)); tsInstallationUnit.SetUninstallResult(UNINSTALL_FAILURE); startPage = CYS_FINISH_PAGE; } CYS_APPEND_LOG(L"\r\n"); // Close the log file Win::CloseHandle(logfileHandle); LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT FirstServerPostBoot() { LOG_FUNCTION(FirstServerPostBoot); UINT startPage = CYS_EXPRESS_REBOOT_PAGE; InstallationUnitProvider::GetInstance(). SetCurrentInstallationUnit(EXPRESS_SERVER); LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT DCPromoPostBoot() { LOG_FUNCTION(DCPromoPostBoot); UINT startPage = CYS_WELCOME_PAGE; InstallationUnitProvider::GetInstance(). SetCurrentInstallationUnit(DC_SERVER); // Create the log file bool logFileAvailable = false; String logName; HANDLE logfileHandle = AppendLogFile( CYS_LOGFILE_NAME, logName); if (logfileHandle && logfileHandle != INVALID_HANDLE_VALUE) { LOG(String::format(L"New log file was created: %1", logName.c_str())); logFileAvailable = true; } else { LOG(L"Unable to create the log file!!!"); logFileAvailable = false; } // Make sure the installation unit knows we are doing an install InstallationUnitProvider::GetInstance(). GetCurrentInstallationUnit().SetInstalling(true); // Prepare the finish page if (State::GetInstance().IsDC()) { CYS_APPEND_LOG(String::load(IDS_LOG_DOMAIN_CONTROLLER_SUCCESS)); InstallationUnitProvider::GetInstance(). GetADInstallationUnit().SetInstallResult(INSTALL_SUCCESS); startPage = CYS_FINISH_PAGE; } else { CYS_APPEND_LOG(String::load(IDS_LOG_DOMAIN_CONTROLLER_FAILED)); InstallationUnitProvider::GetInstance(). GetADInstallationUnit().SetInstallResult(INSTALL_FAILURE); startPage = CYS_FINISH_PAGE; } CYS_APPEND_LOG(L"\r\n"); // Close the log file Win::CloseHandle(logfileHandle); LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT DCDemotePostBoot() { LOG_FUNCTION(DCDemotePostBoot); UINT startPage = CYS_WELCOME_PAGE; InstallationUnitProvider::GetInstance(). SetCurrentInstallationUnit(DC_SERVER); // Create the log file bool logFileAvailable = false; String logName; HANDLE logfileHandle = AppendLogFile( CYS_LOGFILE_NAME, logName); if (logfileHandle && logfileHandle != INVALID_HANDLE_VALUE) { LOG(String::format(L"New log file was created: %1", logName.c_str())); logFileAvailable = true; } else { LOG(L"Unable to create the log file!!!"); logFileAvailable = false; } // Make sure the installation unit knows we are doing an uninstall InstallationUnitProvider::GetInstance(). GetCurrentInstallationUnit().SetInstalling(false); // Prepare the finish page if (!State::GetInstance().IsDC()) { CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_DOMAIN_CONTROLLER_SUCCESS)); InstallationUnitProvider::GetInstance(). GetADInstallationUnit().SetUninstallResult(UNINSTALL_SUCCESS); startPage = CYS_FINISH_PAGE; } else { CYS_APPEND_LOG(String::load(IDS_LOG_UNINSTALL_DOMAIN_CONTROLLER_FAILED)); InstallationUnitProvider::GetInstance(). GetADInstallationUnit().SetUninstallResult(UNINSTALL_FAILURE); startPage = CYS_FINISH_PAGE; } CYS_APPEND_LOG(L"\r\n"); // Close the log file Win::CloseHandle(logfileHandle); LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT DoRebootOperations() { LOG_FUNCTION(DoRebootOperations); UINT startPage = 0; // Check to see if we are in a reboot scenario String homeKeyValue; if (State::GetInstance().GetHomeRegkey(homeKeyValue)) { // Now set the home regkey back to "home" so that we won't run // through these again. This has to be done before doing the // operation because the user could leave this dialog up // and cause a reboot (like demoting a DC) and then the // post reboot operations would run again if (homeKeyValue.icompare(CYS_HOME_REGKEY_DEFAULT_VALUE) != 0) { bool result = State::GetInstance().SetHomeRegkey(CYS_HOME_REGKEY_DEFAULT_VALUE); ASSERT(result); } // Reset the must run key now that we have done the reboot stuff bool regkeyResult = SetRegKeyValue( CYS_HOME_REGKEY, CYS_HOME_REGKEY_MUST_RUN, CYS_HOME_RUN_KEY_DONT_RUN, HKEY_LOCAL_MACHINE, true); ASSERT(regkeyResult); // Set the reboot scenario in the state object so that we know we // are running in that context State::GetInstance().SetRebootScenario(true); // Now run the post reboot operations if necessary if (homeKeyValue.icompare(CYS_HOME_REGKEY_TERMINAL_SERVER_VALUE) == 0) { startPage = TerminalServerPostBoot(); } else if (homeKeyValue.icompare(CYS_HOME_REGKEY_UNINSTALL_TERMINAL_SERVER_VALUE) == 0) { startPage = TerminalServerUninstallPostBoot(); } else if (homeKeyValue.icompare(CYS_HOME_REGKEY_FIRST_SERVER_VALUE) == 0) { startPage = FirstServerPostBoot(); } else if (homeKeyValue.icompare(CYS_HOME_REGKEY_DCPROMO_VALUE) == 0) { startPage = DCPromoPostBoot(); } else if (homeKeyValue.icompare(CYS_HOME_REGKEY_DCDEMOTE_VALUE) == 0) { startPage = DCDemotePostBoot(); } else { // We are NOT running a reboot scenario State::GetInstance().SetRebootScenario(false); } } LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT GetStartPageFromCommandLine() { LOG_FUNCTION(GetStartPageFromCommandLine); UINT startPage = 0; StringVector args; int argc = Win::GetCommandLineArgs(std::back_inserter(args)); if (argc > 1) { const String skipWelcome(L"/skipWelcome"); for ( StringVector::iterator itr = args.begin(); itr != args.end(); ++itr) { if (itr && (*itr).icompare(skipWelcome) == 0) { startPage = 1; break; } } } LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } UINT GetStartPage() { LOG_FUNCTION(GetStartPage); UINT startPage = 0; // First check for the reboot scenarios startPage = DoRebootOperations(); if (startPage == 0) { // Now look at the commandline to see if any // switches were provided startPage = GetStartPageFromCommandLine(); } LOG(String::format( L"startPage = %1!d!", startPage)); return startPage; } // This is the DlgProc of the property sheet that we are subclassing. I need // to hold on to it so I can call it if we don't handle the message in our // replacement DlgProc. static WNDPROC replacedSheetWndProc = 0; // This is the DlgProc that we will use to replace the property sheet // DlgProc. It handles the WM_CTLCOLORDLG message to paint the background // color LRESULT ReplacementWndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { switch (message) { case WM_CTLCOLORDLG: case WM_CTLCOLORSTATIC: case WM_CTLCOLOREDIT: case WM_CTLCOLORLISTBOX: case WM_CTLCOLORSCROLLBAR: { HDC deviceContext = reinterpret_cast(wparam); ASSERT(deviceContext); if (deviceContext) { SetTextColor(deviceContext, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(deviceContext, GetSysColor(COLOR_WINDOW)); } return reinterpret_cast( Win::GetSysColorBrush(COLOR_WINDOW)); } default: if (replacedSheetWndProc) { return ::CallWindowProc( replacedSheetWndProc, hwnd, message, wparam, lparam); } break; } return 0; } // This callback function is called by the property sheet. During initialization // I use this to subclass the property sheet, replacing their DlgProc with my // own so that I can change the background color. int CALLBACK SheetCallbackProc( HWND hwnd, UINT message, LPARAM /*lparam*/) { LOG_FUNCTION(SheetCallbackProc); if (message == PSCB_INITIALIZED) { LONG_PTR ptr = 0; HRESULT hr = Win::GetWindowLongPtr( hwnd, GWLP_WNDPROC, ptr); if (SUCCEEDED(hr)) { replacedSheetWndProc = reinterpret_cast(ptr); hr = Win::SetWindowLongPtr( hwnd, GWLP_WNDPROC, reinterpret_cast(ReplacementWndProc)); ASSERT(SUCCEEDED(hr)); } } return 0; } ExitCode RunWizard() { LOG_FUNCTION(RunWizard); ExitCode exitCode = EXIT_CODE_SUCCESSFUL; UINT startPage = GetStartPage(); State::GetInstance().SetStartPage(startPage); // Create the wizard and add all the pages Wizard wiz( IDS_WIZARD_TITLE, IDB_BANNER16, IDB_BANNER256, IDB_WATERMARK16, IDB_WATERMARK256); // NOTE: Do not change the order of the following // page additions. They are important for being able // to start the wizard at one of these pages directly. // The order of these pages cooresponds directly to // the order of the StartPages enum above wiz.AddPage(new WelcomePage()); // CYS_WELCOME_PAGE wiz.AddPage(new BeforeBeginPage()); // CYS_BEFORE_BEGIN_PAGE wiz.AddPage(new ExpressRebootPage()); // CYS_EXPRESS_REBOOT_PAGE wiz.AddPage(new FinishPage()); // CYS_FINISH_PAGE // // // wiz.AddPage(new DecisionPage()); wiz.AddPage(new CustomServerPage()); wiz.AddPage(new ADDomainPage()); wiz.AddPage(new NetbiosDomainPage()); wiz.AddPage(new DNSForwarderPage()); wiz.AddPage(new ExpressDNSPage()); wiz.AddPage(new ExpressDHCPPage()); wiz.AddPage(new PrintServerPage()); wiz.AddPage(new FileServerPage()); wiz.AddPage(new IndexingPage()); wiz.AddPage(new MilestonePage()); wiz.AddPage(new UninstallMilestonePage()); wiz.AddPage(new InstallationProgressPage()); wiz.AddPage(new UninstallProgressPage()); wiz.AddPage(new WebApplicationPage()); wiz.AddPage(new POP3Page()); // Run the wizard switch (wiz.ModalExecute( 0, startPage, SheetCallbackProc)) { case -1: { /* popup.Error( Win::GetDesktopWindow(), E_FAIL, IDS_PROP_SHEET_FAILED); */ exitCode = EXIT_CODE_UNSUCCESSFUL; break; } case ID_PSREBOOTSYSTEM: { // we can infer that if we are supposed to reboot, then the // operation was successful. exitCode = EXIT_CODE_SUCCESSFUL; break; } default: { // do nothing. break; } } return exitCode; } ExitCode Start() { LOG_FUNCTION(Start); ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL; do { // Put any checks that should stop the wizard from running here... // User must be an Administrator bool isAdmin = ::IsCurrentUserAdministrator(); if (!isAdmin) { LOG(L"Current user is not an Administrator"); // Since the user is not an administrator // close the mutex so that a non-admin can't // leave this message box up and prevent // an administrator from running CYS Win::CloseHandle(cysRunningMutex); popup.MessageBox( Win::GetDesktopWindow(), IDS_NOT_ADMIN, MB_OK); // State::GetInstance().SetRerunWizard(false); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } // The Sys OC Manager cannot be running if (State::GetInstance().IsWindowsSetupRunning()) { LOG(L"Windows setup is running"); popup.MessageBox( Win::GetDesktopWindow(), IDS_WINDOWS_SETUP_RUNNING_DURING_CYS_STARTUP, MB_OK); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } // Machine cannot be in the middle of a DC upgrade if (State::GetInstance().IsUpgradeState()) { LOG(L"Machine needs to complete DC upgrade"); String commandline = Win::GetCommandLine(); // If we were launched from explorer then // don't show the message, just exit silently if (commandline.find(EXPLORER_SWITCH) == String::npos) { popup.MessageBox( Win::GetDesktopWindow(), IDS_DC_UPGRADE_NOT_COMPLETE, MB_OK); } // State::GetInstance().SetRerunWizard(false); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } // Machine cannot have DCPROMO running or a reboot pending if (State::GetInstance().IsDCPromoRunning()) { LOG(L"DCPROMO is running"); popup.MessageBox( Win::GetDesktopWindow(), IDS_DCPROMO_RUNNING, MB_OK); // State::GetInstance().SetRerunWizard(false); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } else if (State::GetInstance().IsDCPromoPendingReboot()) { LOG(L"DCPROMO was run, pending reboot"); popup.MessageBox( Win::GetDesktopWindow(), IDS_DCPROMO_PENDING_REBOOT, MB_OK); // State::GetInstance().SetRerunWizard(false); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } DWORD productSKU = State::GetInstance().RetrieveProductSKU(); if (CYS_UNSUPPORTED_SKU == productSKU) { LOG(L"Cannot run CYS on any SKU but servers"); popup.MessageBox( Win::GetDesktopWindow(), IDS_SERVER_ONLY, MB_OK); // State::GetInstance().SetRerunWizard(false); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } // The machine cannot be a member of a cluster if (IsClusterServer()) { LOG(L"Machine is a member of a cluster"); Win::CloseHandle(cysRunningMutex); popup.MessageBox( Win::GetDesktopWindow(), IDS_CLUSTER, MB_OK); exitCode = EXIT_CODE_UNSUCCESSFUL; break; } // We can run the wizard. Yea!!! exitCode = RunWizard(); } while (0); LOG(String::format(L"exitCode = %1!d!", static_cast(exitCode))); return exitCode; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE /* hPrevInstance */ , PSTR /* lpszCmdLine */ , int /* nCmdShow */) { hResourceModuleHandle = hInstance; ExitCode exitCode = EXIT_CODE_UNSUCCESSFUL; String mutexName = L"Global\\"; mutexName += RUNTIME_NAME; HRESULT hr = Win::CreateMutex(0, true, mutexName, cysRunningMutex); if (hr == Win32ToHresult(ERROR_ALREADY_EXISTS)) { // First close the handle so that the owner can reacquire if they // restart Win::CloseHandle(cysRunningMutex); // Now show the error message popup.MessageBox( Win::GetDesktopWindow(), IDS_ALREADY_RUNNING, MB_OK); } else { do { hr = ::CoInitialize(0); if (FAILED(hr)) { ASSERT(SUCCEEDED(hr)); break; } // Initialize the common controls so that we can use // animation in the NetDetectProgressDialog INITCOMMONCONTROLSEX commonControlsEx; commonControlsEx.dwSize = sizeof(commonControlsEx); commonControlsEx.dwICC = ICC_ANIMATE_CLASS; BOOL init = ::InitCommonControlsEx(&commonControlsEx); ASSERT(init); // For now there is no more rerunning CYS // do // { exitCode = Start(); // } while(State::GetInstance().RerunWizard()); InstallationUnitProvider::Destroy(); State::Destroy(); CoUninitialize(); } while(false); } if (brush) { // delete the background brush (void)Win::DeleteObject(brush); } return static_cast(exitCode); }