|
|
#include "pch.h"
#pragma hdrstop
#include <ntsam.h>
#include <lmerr.h>
#include <wincred.h>
#include "afilexp.h"
#include "dsgetdc.h"
#include "ncatlui.h"
#include "nccom.h"
#include "nceh.h"
#include "ncerror.h"
#include "ncident.h"
#include "ncmisc.h"
#include "ncreg.h"
#include "ncsetup.h"
#include "ncsvc.h"
#include "ncui.h"
#include "resource.h"
#include "wizard.h"
#include "nslog.h"
#include "windns.h"
// header filename clash between config\inc\netsetup.h and
// private\net\inc\netsetup.h where this prototype lives.
// fix later.
//
EXTERN_C NET_API_STATUS NET_API_FUNCTION NetpUpgradePreNT5JoinInfo( VOID );
// Setup Wizard Global - Only used during setup.
extern CWizard * g_pSetupWizard;
//
// NOTE: Set breakpoints in JoinDomainWorkThrd if debugging join problems
//
static const UINT PWM_JOINFAILURE = WM_USER+1201; static const UINT PWM_JOINSUCCESS = WM_USER+1202;
static const INT MAX_USERNAME_LENGTH = UNLEN;
// the number of bytes in a full DNS name to reserve for stuff
// netlogon pre-/appends to DNS names when registering them
static const INT SRV_RECORD_RESERVE = 100;
EXTERN_C const INT MAX_DOMAINNAME_LENGTH = DNS_MAX_NAME_LENGTH - SRV_RECORD_RESERVE; EXTERN_C const INT MAX_WORKGROUPNAME_LENGTH = 15;
static const INT MAX_TITLEBASE = 128; static const INT MAX_TITLENEW = 256; static const INT MAX_NAME_LENGTH = max( max( max( SAM_MAX_PASSWORD_LENGTH, MAX_USERNAME_LENGTH ), MAX_COMPUTERNAME_LENGTH ), MAX_DOMAINNAME_LENGTH) + 1;
const int nrgIdc[] = {EDT_WORKGROUPJOIN_NAME, EDT_DOMAINJOIN_NAME, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, TXT_JOIN_WORKGROUP_LINE2};
const int nrgIdcWorkgroup[] = {EDT_WORKGROUPJOIN_NAME}; const int nrgIdcDomain[] = {EDT_DOMAINJOIN_NAME}; const c_dwDomainJoinWaitDelay = 10000;
static const WCHAR c_szNetMsg[] = L"netmsg.dll"; static const WCHAR c_szIpParameters[] = L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters"; static const WCHAR c_szSyncDomainWithMembership[] = L"SyncDomainWithMembership"; static const WCHAR c_szWinlogonPath[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"; static const WCHAR c_szRunNetAccessWizard[] = L"RunNetAccessWizard"; static const WCHAR c_szAfSectionGuiUnattended[] = L"GuiUnattended"; static const WCHAR c_szAfAutoLogonAccountCreation[] = L"AutoLogonAccountCreation";
extern const WCHAR c_szAfSectionIdentification[]; // L"Identification";
extern const WCHAR c_szAfComputerName[]; // L"ComputerName";
extern const WCHAR c_szAfJoinWorkgroup[]; // L"JoinWorkgroup";
extern const WCHAR c_szAfJoinDomain[]; // L"JoinDomain";
extern const WCHAR c_szAfDomainAdmin[]; // L"DomainAdmin";
extern const WCHAR c_szAfDomainAdminPassword[]; // L"DomainAdminPassword";
extern const WCHAR c_szAfSectionNetworking[]; // L"Networking";
extern const WCHAR c_szAfUpgradeFromProduct[]; // L"UpgradeFromProduct";
extern const WCHAR c_szAfWin95[]; // L"Windows95";
extern const WCHAR c_szSvcWorkstation[]; // L"LanmanWorkstation";
extern const WCHAR c_szAfMachineObjectOU[]; // L"MachineObjectOU";
extern const WCHAR c_szAfUnsecureJoin[]; // L"DoOldStyleDomainJoin";
extern const WCHAR c_szAfBuildNumber[]; // L"BuildNumber";
// For Secure Domain Join Support, the computer account password
extern const WCHAR c_szAfComputerPassword[]; // L"ComputerPassword";
typedef struct _tagJoinData { BOOL fUpgraded; BOOL fUnattendedFailed; CNetCfgIdentification * pIdent; HCURSOR hClassCursor; HCURSOR hOldCursor;
// Used by join thread
//
HWND hwndDlg;
// Set from answer file or user input then supplied to the join thread
// as the join parameters
//
DWORD dwJoinFlag; WCHAR szUserName[MAX_USERNAME_LENGTH + 1]; WCHAR szPassword[SAM_MAX_PASSWORD_LENGTH + 1]; WCHAR szDomain[MAX_DOMAINNAME_LENGTH + 1]; WCHAR szComputerPassword[SAM_MAX_PASSWORD_LENGTH + 1]; WCHAR * pszMachineObjectOU; } JoinData;
typedef enum { PSW_JOINEDDOMAIN = 2, PSW_JOINFAILED = 3 } POSTSETUP_STATE;
//
// Function: IsRunningOnPersonal
//
// Purpose: Determines whether running on Whistler Personal
//
// Returns: Returns true if running on Personal - FALSE otherwise
BOOL IsRunningOnPersonal( VOID ) { TraceFileFunc(ttidGuiModeSetup);
OSVERSIONINFOEXW OsVer = {0}; ULONGLONG ConditionMask = 0;
OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); OsVer.wSuiteMask = VER_SUITE_PERSONAL; OsVer.wProductType = VER_NT_WORKSTATION;
VER_SET_CONDITION(ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL); VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_AND);
return VerifyVersionInfo(&OsVer, VER_PRODUCT_TYPE | VER_SUITENAME, ConditionMask ); }
//
// Function: IsValidDomainName
//
// Purpose: Determines whether the domain name is valid.
//
// Returns: See win32 documentation on DnsValidateName.
//
// Author: Alok Sinha
//
DNS_STATUS IsValidDomainName (HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup);
WCHAR szDomain[MAX_DOMAINNAME_LENGTH+1]; HWND hwndEdit;
hwndEdit = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME); Assert(0 != GetWindowTextLength(hwndEdit));
GetWindowText(hwndEdit, szDomain, MAX_DOMAINNAME_LENGTH + 1);
return DnsValidateName( szDomain, DnsNameDomain ); }
//
// Function: SetCursorToHourglass
//
// Purpose: Changes the cursor to hourglass.
//
// Returns: Nothing
//
// Author: asinha 3/28/2001
//
VOID SetCursorToHourglass (HWND hwndDlg, JoinData *pData) { TraceFileFunc(ttidGuiModeSetup);
Assert( pData != NULL);
if ( pData ) { pData->hClassCursor = (HCURSOR)GetClassLongPtr( hwndDlg, GCLP_HCURSOR ); SetClassLongPtr( hwndDlg, GCLP_HCURSOR, (LONG_PTR)NULL ); pData->hOldCursor = SetCursor( LoadCursor(NULL,IDC_WAIT) );
SetCapture( hwndDlg ); }
return; }
//
// Function: RestoreCursor
//
// Purpose: Changes the cursor back to its orginal state.
//
// Returns: Nothing
//
// Author: asinha 3/28/2001
//
VOID RestoreCursor (HWND hwndDlg, JoinData *pData) { TraceFileFunc(ttidGuiModeSetup);
Assert( pData != NULL);
if ( pData ) { if ( pData->hClassCursor ) { SetClassLongPtr( hwndDlg, GCLP_HCURSOR, (LONG_PTR)pData->hClassCursor ); pData->hClassCursor = NULL; }
if ( pData->hOldCursor ) { SetCursor( pData->hOldCursor ); pData->hOldCursor = NULL; }
ReleaseCapture(); }
return; }
//
// Function: NotifyPostSetupWizard
//
// Purpose: Subclass the edit control so we can enable/disable the
// Next button as the content of the name edit control changes
//
// Parameters: State - Final Join State
//
// Returns: nothing
//
VOID NotifyPostSetupWizard(POSTSETUP_STATE State, CWizard * pWizard) { TraceFileFunc(ttidGuiModeSetup); HKEY hkey; HRESULT hr = S_OK; BOOL fRunNaWizard = TRUE; CSetupInfFile csif;
// If this is an upgrade or an unattended install, or it's a server do nothing
//
if (IsUpgrade(pWizard) || IsUnattended(pWizard) || (PRODUCT_WORKSTATION != ProductType(pWizard)) ) { fRunNaWizard = FALSE; }
// Is there an unattended flag to override default behavior?
if (IsUnattended(pWizard)) { hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); if (SUCCEEDED(hr)) { BOOL fValue = FALSE; hr = csif.HrGetStringAsBool(c_szAfSectionGuiUnattended, c_szAfAutoLogonAccountCreation, &fValue); if (SUCCEEDED(hr) && fValue) { fRunNaWizard = fValue; } }
hr = S_OK; }
if (fRunNaWizard) { hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szWinlogonPath, KEY_WRITE, &hkey); if (SUCCEEDED(hr)) { hr = HrRegSetDword (hkey, c_szRunNetAccessWizard, (DWORD)State); TraceTag(ttidWizard, "NotifyPostSetupWizard - State = %d",(DWORD)State); RegCloseKey(hkey); } }
TraceError("WJOIN.CPP - NotifyPostSetupWizard",hr); }
//
// Function: JoinEditSubclassProc
//
// Purpose: Subclass the edit control so we can enable/disable the
// Next button as the content of the name edit control changes
//
// Parameters: std for a window proc
//
// Returns: std for a window proc
//
STDAPI JoinEditSubclassProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { TraceFileFunc(ttidGuiModeSetup); LRESULT lReturn; HWND hwndDlg = GetParent(hwnd);
lReturn = CallWindowProc((WNDPROC)::GetWindowLongPtr(hwnd, GWLP_USERDATA), hwnd, wMsg, wParam, lParam);
// If we processing a character send the message through the regular proc
if (WM_CHAR == wMsg) { CWizard * pWizard = reinterpret_cast<CWizard *> (::GetWindowLongPtr(hwndDlg, DWLP_USER));
Assert(NULL != pWizard);
if ( !pWizard ) { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); return (HRESULT)lReturn; }
JoinData * pData = reinterpret_cast<JoinData *> (pWizard->GetPageData(IDD_Join)); Assert(NULL != pData);
if ( !pData ) { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); return (HRESULT)lReturn; }
if (!IsUnattended(pWizard) || (IsUnattended(pWizard) && pData->fUnattendedFailed)) { if (0 == GetWindowTextLength(hwnd)) { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK); } else { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); } } }
return (HRESULT)lReturn; }
HRESULT HrGetIdentInterface(CNetCfgIdentification ** ppIdent) { TraceFileFunc(ttidGuiModeSetup);
HRESULT hr = S_OK;
*ppIdent = new CNetCfgIdentification; if (NULL == *ppIdent) hr = E_OUTOFMEMORY;
TraceHr(ttidWizard, FAL, hr, FALSE, "HrGetIdentInterface"); return hr; }
//
// Function: IdsFromIdentError
//
// Purpose: Map a error code into a display string.
//
// Parameters: hr [IN] - Error code to map
//
// Returns: INT, the string ID of the corresponding error message
//
INT IdsFromIdentError(HRESULT hr, BOOL fWorkgroup) { TraceFileFunc(ttidGuiModeSetup); INT ids = -1;
switch (hr) { case HRESULT_FROM_WIN32(ERROR_NO_TRUST_SAM_ACCOUNT): ids = IDS_DOMMGR_CANT_CONNECT_DC_PW; break;
case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH): case HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN): ids = IDS_DOMMGR_CANT_FIND_DC1; break;
case HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD): ids = IDS_DOMMGR_INVALID_PASSWORD; break;
case HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE): ids = IDS_DOMMGR_NETWORK_UNREACHABLE; break;
case HRESULT_FROM_WIN32(ERROR_ACCOUNT_DISABLED): case HRESULT_FROM_WIN32(ERROR_LOGON_FAILURE): case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED): ids = IDS_DOMMGR_ACCESS_DENIED; break;
case HRESULT_FROM_WIN32(ERROR_SESSION_CREDENTIAL_CONFLICT): ids = IDS_DOMMGR_CREDENTIAL_CONFLICT; break;
case NETCFG_E_ALREADY_JOINED: ids = IDS_DOMMGR_ALREADY_JOINED; break;
case NETCFG_E_NAME_IN_USE: ids = IDS_DOMMGR_NAME_IN_USE; break;
case NETCFG_E_NOT_JOINED: ids = IDS_DOMMGR_NOT_JOINED; break;
case NETCFG_E_INVALID_DOMAIN: case HRESULT_FROM_WIN32(ERROR_INVALID_NAME): if (fWorkgroup) ids = IDS_DOMMGR_INVALID_WORKGROUP; else ids = IDS_DOMMGR_INVALID_DOMAIN; break;
default: ids = IDS_E_UNEXPECTED; break; }
return ids; }
//
// Function: SzFromError
//
// Purpose: Convert an error code into a message displayable to the user.
// The error message could come from our resources, or from netmsg.dll
//
// Parameters: hr [IN] - The error to map
// fWorkgroup [IN] - Flag to provide "workgroup" flavored error
// messages for some cases.
//
// Returns: PCWSTR, Pointer to a static buffer containing the error message
//
PCWSTR SzFromError(HRESULT hr, BOOL fWorkgroup) { TraceFileFunc(ttidGuiModeSetup); static WCHAR szErrorMsg[1024]; INT nIds = IdsFromIdentError(hr, fWorkgroup);
// If the error string returned is unexpected, it means we couldn't
// a local match for the string. Search netmsg.dll if the errors'
// range is correct
if (IDS_E_UNEXPECTED == nIds) { // Extract the error code from the HRESULT
DWORD dwErr = ((DWORD)hr & 0x0000FFFF); if ((NERR_BASE <= dwErr) && (MAX_NERR >= dwErr)) { // The error is within the range hosted by netmsg.dll
HMODULE hModule = LoadLibraryEx(c_szNetMsg, NULL, LOAD_LIBRARY_AS_DATAFILE); if (NULL != hModule) { // Try to locate the error string
DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, (LPVOID)hModule, dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrorMsg, // Output buffer
1024, // szErrorMsg in characters.
NULL);
FreeLibrary(hModule);
if (dwRet) { // We successfully found an error message
// Remove the trailing newline that format message adds
// This string is concatenated with another and the newline
// messes up the appearance.
//
// Raid 146173 - scottbri
//
int nLen = wcslen(szErrorMsg); if ((nLen>2) && (L'\r' == szErrorMsg[nLen-2])) { szErrorMsg[nLen-2] = 0; } return szErrorMsg; } } } else if ( (dwErr >= 9000) && (dwErr <= 9999) ) { // The error code from 9000-9999 is dns error code in which
// case show the dns error message.
//
DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, (LPVOID)NULL, dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrorMsg, // Output buffer
1024, // szErrorMsg in characters.
NULL); if (dwRet) { // We successfully found an error message
// Remove the trailing newline that format message adds
int nLen = wcslen(szErrorMsg); if ((nLen>2) && (L'\r' == szErrorMsg[nLen-2])) { szErrorMsg[nLen-2] = 0; } return szErrorMsg; } } }
// Load the error found
wcscpy(szErrorMsg, SzLoadIds(nIds)); return szErrorMsg; }
//
// Function: GetJoinNameIIDFromSelection
//
// Purpose: Get the edit box for the workgroup / domain dialog depending on the current user selection
//
// Parameters: hwndDlg - The join domain dialog
//
// Returns:
//
inline DWORD GetJoinNameIIDFromSelection(HWND hwndDlg) { if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_WORKGROUP)) { return EDT_WORKGROUPJOIN_NAME; } else { return EDT_DOMAINJOIN_NAME; } }
//
// Function: UpdateNextBackBttns
//
// Purpose:
//
// Parameters:
//
// Returns:
//
VOID UpdateNextBackBttns(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup);
int b = PSWIZB_BACK; if (0 != GetWindowTextLength(GetDlgItem(hwndDlg, GetJoinNameIIDFromSelection(hwndDlg)))) { b |= PSWIZB_NEXT; }
PropSheet_SetWizButtons(GetParent(hwndDlg), b); }
//
// Function: EnableAndDisableWorkGroupDomainControls
//
// Purpose: Disable the domain/workgroup edit boxes depending on the current user selection
//
// Parameters: hwndDlg - The join domain dialog
//
// Returns:
//
VOID EnableAndDisableWorkGroupDomainControls(HWND hwndDlg) { if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_WORKGROUP)) { EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcWorkgroup), nrgIdcWorkgroup, TRUE); EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcDomain), nrgIdcDomain, FALSE); } else { EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcDomain), nrgIdcDomain, TRUE); EnableOrDisableDialogControls(hwndDlg, celems(nrgIdcWorkgroup), nrgIdcWorkgroup, FALSE); } }
//
// Function: JoinUpdatePromptText
//
// Purpose: Update the prompt text
//
// Parameters: hwndDlg [IN] - Current dialog handle
//
// Returns: Nothing
//
VOID JoinUpdatePromptText(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); HWND hwndDomain = NULL; int idsNew = IDS_WORKGROUP; int idsOld = IDS_DOMAIN; WCHAR szDomain[MAX_DOMAINNAME_LENGTH + 1]; JoinData * pData=NULL; CWizard * pWizard = NULL;
// Based on the button selected, update the Prompt text & dialog box
if (!IsDlgButtonChecked(hwndDlg, BTN_JOIN_WORKGROUP)) { hwndDomain = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME); idsNew = IDS_DOMAIN; idsOld = IDS_WORKGROUP; } else { hwndDomain = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME); } Assert(NULL != hwndDomain); EnableAndDisableWorkGroupDomainControls(hwndDlg);
// Update the domain/workgroup only if the current contents
// are the default workgroup/domain
GetWindowText(hwndDomain, szDomain, celems(szDomain)); if (0 == lstrcmpW(szDomain, SzLoadIds(idsOld))) { SetWindowText(hwndDomain, SzLoadIds(idsNew)); }
// Update the back/next buttons based on the selected button. See bug 355978
pWizard = (CWizard *) ::GetWindowLongPtr(hwndDlg, DWLP_USER); Assert(NULL != pWizard); if ( pWizard) { pData = (JoinData *) pWizard->GetPageData(IDD_Join); Assert(NULL != pData); } if ( pWizard && pData ) { if (!IsUnattended(pWizard) || (IsUnattended(pWizard) && pData->fUnattendedFailed)) { if (0 == GetWindowTextLengthW(hwndDomain)) { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK); } else { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); } } } }
//
// Function: UpdateJoinUsingComputerRole
//
// Purpose:
//
// Parameters:
//
// Returns:
//
VOID UpdateJoinUsingComputerRole(HWND hwndDlg, CWizard * pWizard) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr; INetCfg * pNetCfg = pWizard->PNetCfg(); JoinData * pData = reinterpret_cast<JoinData *> (pWizard->GetPageData(IDD_Join)); Assert(NULL != pNetCfg); Assert(NULL != pData); Assert(NULL != pData->pIdent);
PWSTR pszwText = NULL; DWORD computer_role; int nIdc;
Assert(NULL != pData->pIdent);
if(!pData || !pData->pIdent) { return; }
hr = pData->pIdent->GetComputerRole(&computer_role); if (SUCCEEDED(hr)) { if (computer_role & GCR_STANDALONE) { // Get the workgroup name
hr = pData->pIdent->GetWorkgroupName(&pszwText); Assert(NULL != pszwText); Assert(lstrlenW(pszwText) <= MAX_WORKGROUPNAME_LENGTH); nIdc = BTN_JOIN_WORKGROUP; } else { // Get the domain name
hr = pData->pIdent->GetDomainName(&pszwText); Assert(NULL != pszwText); Assert(lstrlenW(pszwText) <= MAX_DOMAINNAME_LENGTH); nIdc = BTN_JOIN_DOMAIN; } }
if (SUCCEEDED(hr)) { HWND hwndEdit = GetDlgItem(hwndDlg, nIdc == BTN_JOIN_DOMAIN ? EDT_DOMAINJOIN_NAME : EDT_WORKGROUPJOIN_NAME); Assert(NULL != hwndEdit);
SetWindowText(hwndEdit, pszwText); CoTaskMemFree(pszwText);
CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, nIdc); } else { HWND hwndEdit = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME); Assert(NULL != hwndEdit); SetWindowText(hwndEdit, SzLoadIds(IDS_WORKGROUP)); CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, BTN_JOIN_WORKGROUP); TraceHr(ttidWizard, FAL, hr, FALSE, "UpdateJoinUsingComputerRole - Unable to determine role, using default"); }
JoinUpdatePromptText(hwndDlg); }
//
// Function: JoinDefaultWorkgroup
//
// Purpose: Join the machine to the workgroup "WORKGROUP"
//
// Parameters: pWizard [IN] - Ptr to a wizard instance, containing
// hopefully a INetCfg instance pointer
// hwndDlg [IN] - HWND to parent error dialogs against
//
// Returns: nothing
//
void JoinDefaultWorkgroup(CWizard *pWizard, HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); // Join default workgroup.
CNetCfgIdentification *pINetid = NULL; HRESULT hr = S_OK;
hr = HrGetIdentInterface(&pINetid); if (S_OK == hr) { if (IsRunningOnPersonal()) { hr = pINetid->JoinWorkgroup(SzLoadIds(IDS_WORKGROUP_PERSONAL)); } else { hr = pINetid->JoinWorkgroup(SzLoadIds(IDS_WORKGROUP)); } if (SUCCEEDED(hr)) { hr = pINetid->Validate(); if (SUCCEEDED(hr)) { hr = pINetid->Apply(); } } if (FAILED(hr)) { if (UM_FULLUNATTENDED == pWizard->GetUnattendedMode()) { // Raid 380374: no UI allowed if in full unattended mode ..
NetSetupLogStatusV( LogSevWarning, SzLoadIds (IDS_E_UNATTENDED_JOIN_DEFAULT_WORKGROUP), hr ); } else { MessageBox(GetParent(hwndDlg), SzFromError(hr, TRUE), SzLoadIds(IDS_SETUP_CAPTION), MB_OK); }
goto Done; } } else { AssertSz(0,"Cannot find the INegCfgIdentification interface!"); }
Done: delete pINetid; TraceHr(ttidWizard, FAL, hr, FALSE, "JoinDefaultWorkgroup"); }
BOOL OnJoinSuccess(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); CWizard * pWizard = reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER)); Assert(NULL != pWizard);
if ( !pWizard ) { return TRUE; }
// Reset the Wait Cursor
JoinData * pData = reinterpret_cast<JoinData *> (pWizard->GetPageData(IDD_Join)); Assert(NULL != pData);
if ( !pData ) { return TRUE; }
RestoreCursor( hwndDlg, pData );
EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, TRUE); EnableAndDisableWorkGroupDomainControls(hwndDlg);
if (!(IsUnattended(pWizard) && pData->fUpgraded)) { PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); }
// Goto the Exit page
pWizard->SetPageDirection(IDD_Join, NWPD_BACKWARD); HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_Exit); PostMessage(GetParent(hwndDlg), PSM_SETCURSEL, 0, (LPARAM)(HPROPSHEETPAGE)hPage);
return TRUE; }
BOOL OnJoinFailure(HWND hwndDlg, LPARAM lParam) { TraceFileFunc(ttidGuiModeSetup); JoinData * pData = NULL; BOOL fWorkgroup; tstring str; HRESULT hr = (HRESULT)lParam; CWizard * pWizard = reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER)); Assert(NULL != pWizard);
if (pWizard) {
pData = reinterpret_cast<JoinData *>(pWizard->GetPageData(IDD_Join)); Assert(NULL != pData); }
fWorkgroup = !IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN);
if (fWorkgroup) { str = SzLoadIds(IDS_JOIN_E_WORKGROUP_MSG); } else { if (pData && (pData->dwJoinFlag & JDF_WIN9x_UPGRADE)) { str = SzLoadIds(IDS_JOIN_E_DOMAIN_WIN9X_MSG_1); str += SzLoadIds(IDS_JOIN_E_DOMAIN_WIN9X_MSG_2); } else { // Raid 372087: removed additional text
str = SzLoadIds(IDS_JOIN_E_DOMAIN_MSG); } }
// If an error actually occurred, ask the user if they want to ignore
// the error and proceed. Note Unattended can succeed but require us
// to stay on the page so this function is envoked to do this.
if (FAILED(hr)) { if (IDYES == NcMsgBox(GetParent(hwndDlg), IDS_SETUP_CAPTION, IDS_JOIN_FAILURE, MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2, SzFromError(hr, fWorkgroup), str.c_str())) { // User choose to proceed in spite of the failure, go to the Exit page
if (!fWorkgroup) { NotifyPostSetupWizard(PSW_JOINFAILED, pWizard); }
OnJoinSuccess(hwndDlg); return TRUE; } }
if (pData) { // Reset the Wait Cursor
RestoreCursor( hwndDlg, pData ); }
// Make sure the page is visible.
if (g_pSetupWizard != NULL) { g_pSetupWizard->PSetupData()->ShowHideWizardPage(TRUE); }
// The user wants to stay and correct whatever's wrong
EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, TRUE); EnableAndDisableWorkGroupDomainControls(hwndDlg);
UpdateNextBackBttns(hwndDlg);
return TRUE; }
// ONLY return failure if the workstation is installed and it doesn't start for 2 minutes
HRESULT HrWorkstationStart(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr = S_OK; HRESULT hrTmp; CServiceManager scm;
TraceTag(ttidWizard, "Entering HrWorkstationStart...Checking for LanmanWorkstation presence");
// Open the service control manager
//
hrTmp = scm.HrOpen(); if (SUCCEEDED(hrTmp)) { // Find the workstation service
//
SC_HANDLE hSvc = OpenService (scm.Handle(), c_szSvcWorkstation, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL); if (hSvc) { SERVICE_STATUS status; const UINT cmsWait = 100; const UINT cmsTotal = 120000; // 2 minutes
UINT cLoop = cmsTotal / cmsWait;
// Check it's status, exit test once it's running
//
for (UINT nLoop = 0; nLoop < cLoop; nLoop++, Sleep (cmsWait)) { BOOL fr = QueryServiceStatus (hSvc, &status); Assert(fr);
if (SERVICE_RUNNING == status.dwCurrentState) { break; } }
if (SERVICE_RUNNING != status.dwCurrentState) { hr = HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE); #if DBG
OutputDebugString (L"***ERROR*** NETCFG - Workstation service didn't start after more than 2 minutes!\n"); OutputDebugString (L"***ERROR*** NETCFG - Join Domain will fail!\n"); #endif
}
CloseServiceHandle(hSvc); }
scm.Close(); } else { TraceError("WJOIN.CPP - HrWorkstationStart - Unable to open the Service Manager",hrTmp); }
TraceError("WJOIN.CPP - HrWorkstationStart",hr); TraceTag(ttidWizard, "Leaving HrWorkstationStart"); return hr; }
// Purpose: Secure Domain Join with the new ComputerPassword Answer-File key.
// Domain Join tries to join the domain with the random precreated machine password.
// (the username is not required in this case)
// note: code path is inspired from HrAttemptJoin(JoinData * pData)
// functions defined in ncident.cpp
extern HRESULT HrNetValidateName(IN PCWSTR lpMachine, IN PCWSTR lpName, IN PCWSTR lpAccount, IN PCWSTR lpPassword, IN NETSETUP_NAME_TYPE NameType); extern HRESULT HrNetJoinDomain(IN PWSTR lpMachine, IN PWSTR lpMachineObjectOU, IN PWSTR lpDomain, IN PWSTR lpAccount, IN PWSTR lpPassword, IN DWORD fJoinOptions);
EXTERN_C HRESULT HrAttemptSecureDomainJoin(JoinData * pData) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr; Assert(pData); // 1. wait for the start of LanmanWorkstation service
// 2. check for valid domain
// 3. secure join domain
hr = HrWorkstationStart(pData->hwndDlg); if (SUCCEEDED(hr)) { hr = HrNetValidateName(NULL, pData->szDomain , NULL, NULL, NetSetupDomain); } TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptSecureDomainJoin - HrNetValidateName"); if (SUCCEEDED(hr)) { // do the secure join domain
DWORD dwJoinOption = 0; dwJoinOption |= (NETSETUP_JOIN_DOMAIN | NETSETUP_JOIN_UNSECURE | NETSETUP_MACHINE_PWD_PASSED); if (FInSystemSetup()) { // During system setup, need to pass special flag that tells join code
// to not do certain operations because SAM is not initialized yet.
dwJoinOption |= NETSETUP_INSTALL_INVOCATION; } hr = HrNetJoinDomain(NULL,pData->pszMachineObjectOU, pData->szDomain, NULL, pData->szComputerPassword, dwJoinOption); TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptSecureDomainJoin - HrNetJoinDomain"); }
TraceError("HrAttemptSecureDomainJoin", hr); return hr; }
EXTERN_C HRESULT HrAttemptJoin(JoinData * pData) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr;
if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN)) { hr = HrWorkstationStart(pData->hwndDlg); if (SUCCEEDED(hr)) { hr = pData->pIdent->JoinDomain(pData->szDomain, pData->pszMachineObjectOU, pData->szUserName, pData->szPassword, pData->dwJoinFlag); } TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - JoinDomain"); } else { // Join a workgroup
hr = pData->pIdent->JoinWorkgroup(pData->szDomain); TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - JoinWorkgroup"); }
if (SUCCEEDED(hr)) { if (S_OK == pData->pIdent->Validate()) { hr = pData->pIdent->Apply(); TraceHr(ttidWizard, FAL, hr, FALSE, "HrAttemptJoin - Apply"); } }
if (FAILED(hr)) { // Rollback any changes
pData->pIdent->Cancel(); }
TraceError("HrAttemptJoin",hr); return hr; }
HRESULT HrAttemptJoin(JoinData * pData, DWORD dwRetries, DWORD dwDelayPeriod) { HRESULT hr = E_FAIL; DWORD dwCount = dwRetries; do { hr = HrAttemptJoin(pData); if (FAILED(hr)) { dwCount--; Sleep(dwDelayPeriod); } } while (FAILED(hr) && (dwCount)); return hr; }
HRESULT HrAttemptSecureDomainJoin(JoinData * pData, DWORD dwRetries, DWORD dwDelayPeriod) { HRESULT hr = E_FAIL; DWORD dwCount = dwRetries; do { hr = HrAttemptSecureDomainJoin(pData); if (FAILED(hr)) { dwCount--; Sleep(dwDelayPeriod); } } while (FAILED(hr) && (dwCount)); return hr; }
EXTERN_C DWORD JoinDomainWorkThrd(JoinData * pData) { TraceFileFunc(ttidGuiModeSetup); BOOL fUninitCOM = FALSE; HRESULT hr; Assert(NULL != pData);
CWizard * pWizard = reinterpret_cast<CWizard *>(::GetWindowLongPtr(pData->hwndDlg, DWLP_USER)); Assert(NULL != pWizard);
// Initialize COM on this thread
//
hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED); if (FAILED(hr)) { // $REVIEW - LogError ?
TraceTag(ttidWizard, "Failed to initialize COM join work thread"); goto Done; } else { // Remember to uninitialize COM on thread exit
fUninitCOM = TRUE; }
DWORD dwNumTries = 1;
if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN)) { Sleep(c_dwDomainJoinWaitDelay); dwNumTries = 5; }
if (pData->dwJoinFlag & JDF_MACHINE_PWD_PASSED) { // Unattended Answer-File specified with ComputerPassword key
// Try Secure Domain Join
TraceTag(ttidWizard, "Attempting join with precreated computer password."); hr = HrAttemptSecureDomainJoin(pData, dwNumTries, c_dwDomainJoinWaitDelay); if (FAILED(hr)) { // clear the secure domain join flag and try the normal join
TraceTag(ttidWizard, "Failed in secure join domain."); pData->dwJoinFlag &= ~JDF_MACHINE_PWD_PASSED; } else goto Cleanup; } // Try the normal join
//
TraceTag(ttidWizard, "Attempting join WITHOUT trying to create an account."); hr = HrAttemptJoin(pData, 3, 10000);
// If the join failed, and the Create Account flag has not been
// specified, try adding it and reattempt the join.
//
if (FAILED(hr) && !(pData->dwJoinFlag & JDF_CREATE_ACCOUNT)) { // Clear the Unsecure join flag if set, creating an account is
// mutually exclusive. Set the create account flag to reattempt.
//
pData->dwJoinFlag &= ~JDF_JOIN_UNSECURE; pData->dwJoinFlag |= JDF_CREATE_ACCOUNT;
TraceTag(ttidWizard, "Attempting join but trying to create an account.");
hr = HrAttemptJoin(pData, dwNumTries, c_dwDomainJoinWaitDelay); } Cleanup: // Cleanup username/password and MachineObjectOU
//
pData->szUserName[0] = 0; pData->szPassword[0] = 0; pData->dwJoinFlag = 0; MemFree(pData->pszMachineObjectOU); pData->pszMachineObjectOU = NULL; pData->szComputerPassword[0] = 0;
if (FAILED(hr)) { // Raid 380374: no UI allowed if in full unattended mode ..
if (UM_FULLUNATTENDED == pWizard->GetUnattendedMode()) { if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN)) { NetSetupLogStatusV( LogSevError, SzLoadIds (IDS_E_UNATTENDED_JOIN_DOMAIN), pData->szDomain, hr ); } else { NetSetupLogStatusV( LogSevWarning, SzLoadIds (IDS_E_UNATTENDED_JOIN_WORKGROUP), pData->szDomain, hr ); } // proceed to the exit page
PostMessage(pData->hwndDlg, PWM_JOINSUCCESS, 0, 0L); } else { // If we're in unattended mode, consider it failed.
pData->fUnattendedFailed = TRUE; PostMessage(pData->hwndDlg, PWM_JOINFAILURE, 0, (LPARAM)hr); } } else { if (IsDlgButtonChecked(pData->hwndDlg, BTN_JOIN_DOMAIN)) NotifyPostSetupWizard(PSW_JOINEDDOMAIN, pWizard);
PostMessage(pData->hwndDlg, PWM_JOINSUCCESS, 0, 0L); }
Done: // Uninitialize COM for this thread
//
if (fUninitCOM) { CoUninitialize(); }
TraceTag(ttidWizard, "Leaving JoinDomainWorkThrd..."); return( 0 ); }
//
// Function: HrJoinProcessAnswerFile
//
// Purpose: Read the answer file and populate the in memory structures
// and UI with the data found
//
// Parameters:
//
// Returns: HRESULT, S_OK on success
// S_FALSE if required information is missing
// A failed error code on error
//
HRESULT HrJoinProcessAnswerFile(HWND hwndDlg, CWizard * pWizard, JoinData * pData) { TraceFileFunc(ttidGuiModeSetup); CSetupInfFile csif; INFCONTEXT ctx; BOOL fValue; HRESULT hr; int nId = BTN_JOIN_WORKGROUP; tstring str;
pData->dwJoinFlag = 0; pData->szDomain[0] = 0; pData->szUserName[0] = 0; pData->szPassword[0] = 0; pData->pszMachineObjectOU = NULL; pData->szComputerPassword[0] = 0;
if ((NULL == pWizard->PSetupData()) || (NULL == pWizard->PSetupData()->UnattendFile)) { hr = NETSETUP_E_ANS_FILE_ERROR; goto Error; }
// Open the answerfile
//
hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); if (FAILED(hr)) { hr = S_FALSE; TraceTag(ttidWizard, "Unable to open answer file!!!"); goto Error; }
// Check for existance of the identification section. If it is not
// present return S_FALSE to indicate identification info not supplied
//
hr = HrSetupFindFirstLine (csif.Hinf(), c_szAfSectionIdentification, NULL, &ctx); if (SPAPI_E_LINE_NOT_FOUND == hr) { hr = S_FALSE; goto Error; }
// Try to get the workgroup
//
hr = csif.HrGetString(c_szAfSectionIdentification, c_szAfJoinWorkgroup, &str); if (SUCCEEDED(hr) && str.length()) { if (MAX_WORKGROUPNAME_LENGTH >= str.length()) { wcscpy(pData->szDomain, str.c_str()); TraceTag(ttidWizard, "Joining workgroup: %S", pData->szDomain); } else { hr = NETSETUP_E_ANS_FILE_ERROR; TraceTag(ttidWizard, "JOIN Workgroup - Invalid workgroup supplied."); goto Error; } } else { if ( IsRunningOnPersonal() ) { // No workgroup entry, skip joining since we are already
// in the default workgroup.
hr = S_FALSE;
goto Error; } else { // Try to get the domain
//
hr = csif.HrGetString(c_szAfSectionIdentification, c_szAfJoinDomain, &str); if (SPAPI_E_LINE_NOT_FOUND == hr) { // No domain or workgroup entry, skip joining a domain
hr = S_FALSE; goto Error; } else if (FAILED(hr) || (0 == str.length()) || (MAX_DOMAINNAME_LENGTH < str.length())) { hr = NETSETUP_E_ANS_FILE_ERROR; TraceTag(ttidWizard, "JOIN Domain - Invalid domain supplied."); goto Error; }
// Joining a domain...
//
nId = BTN_JOIN_DOMAIN; wcscpy(pData->szDomain, str.c_str()); TraceTag(ttidWizard, "Joining domain: %S", pData->szDomain);
// If we're upgrading from win9x add the special flag
//
hr = csif.HrGetString(c_szAfSectionNetworking, c_szAfUpgradeFromProduct, &str); if (SUCCEEDED(hr) && (0 == lstrcmpiW(str.c_str(), c_szAfWin95))) { pData->dwJoinFlag |= JDF_WIN9x_UPGRADE; }
// Support unsecure joins
//
hr = csif.HrGetStringAsBool(c_szAfSectionIdentification, c_szAfUnsecureJoin, &fValue); if (SUCCEEDED(hr) && fValue) { pData->dwJoinFlag |= JDF_JOIN_UNSECURE; }
// Is a MachineObjectOU specified?
//
hr = csif.HrGetString(c_szAfSectionIdentification, c_szAfMachineObjectOU, &str); if (SUCCEEDED(hr) && str.length()) { pData->pszMachineObjectOU = reinterpret_cast<WCHAR *>(MemAlloc(sizeof(WCHAR) * (str.length() + 1))); if (pData->pszMachineObjectOU) { TraceTag(ttidWizard, "Machine Object OU: %S", pData->szDomain); lstrcpyW(pData->pszMachineObjectOU, str.c_str()); } } // Bug# 204377 secure domain join shouldn't require both "ComputerPassword" and
// "DomainAdmin"/"DomainAdminPassword" options to be presence
// in Answer-File simultaneously.
//
// Bug# 204378 If the Answer-File specifies the "ComputerPassword" key
// in the "Indentification" section, the code should attempt
// a secure domain join, regardless of the presence of the
// "DoOldstyleDomainjoin" key
// check if this is a secure domain join by
// quering the random machine account password
hr = csif.HrGetString(c_szAfSectionIdentification, c_szAfComputerPassword, &str); if (SUCCEEDED(hr) && (str.length() <= SAM_MAX_PASSWORD_LENGTH) && str.length()) { TraceTag(ttidWizard, "Got the value of ComputerPassword"); wcscpy(pData->szComputerPassword, str.c_str()); // signal that we need to try secure domain join by
// passing the random machine password
pData->dwJoinFlag |= JDF_MACHINE_PWD_PASSED;
// Bug# 204378, make sure we won't do the unsecure domain join
// and we'll try to read "DomainAdmin"/"DomainAdminPassword"
// in the following if block.
pData->dwJoinFlag &= ~JDF_JOIN_UNSECURE; }
// If not a remote boot client, Query the username, unless this
// is an unsecure domain join which doesn't need username/password.
//
if ( #if defined(REMOTE_BOOT)
(S_FALSE == HrIsRemoteBootMachine()) || #endif // defined(REMOTE_BOOT)
((pData->dwJoinFlag & JDF_JOIN_UNSECURE) == 0)) { hr = csif.HrGetString(c_szAfSectionIdentification, c_szAfDomainAdmin, &str); if (SUCCEEDED(hr) && (MAX_USERNAME_LENGTH > str.length())) { wcscpy(pData->szUserName, str.c_str());
// Query the password
//
hr = csif.HrGetString(c_szAfSectionIdentification, c_szAfDomainAdminPassword, &str); if (SUCCEEDED(hr) && (SAM_MAX_PASSWORD_LENGTH > str.length())) { wcscpy(pData->szPassword, str.c_str());
// Raid 195920 - If both username and password are
// present, treat this like a fresh install and DO NOT
// use the JDF_WIN9x_UPGRADE flag
pData->dwJoinFlag &= ~(JDF_WIN9x_UPGRADE | JDF_JOIN_UNSECURE); } }
// Bug# 204377
// ignore any error on reading of "DomainAdmin"/"DomainAdminPassword" keys
// if "ComputerPassword" has been specified.
if (! (pData->dwJoinFlag & JDF_MACHINE_PWD_PASSED) ) { // If failed or either is longer than the maximum length return an error
//
if (FAILED(hr) || (MAX_USERNAME_LENGTH <= str.length())) { hr = NETSETUP_E_ANS_FILE_ERROR; TraceTag(ttidWizard, "JOIN Domain - Invalid username/password supplied."); goto Error; } } } } }
// Normalize any optional errors
//
hr = S_OK;
Error: // Update the UI and pData with the info we managed to read.
//
CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, nId); SetWindowText(GetDlgItem(hwndDlg, nId == BTN_JOIN_DOMAIN ? EDT_DOMAINJOIN_NAME : EDT_WORKGROUPJOIN_NAME ), pData->szDomain); JoinUpdatePromptText(hwndDlg);
TraceHr(ttidWizard, FAL, hr, FALSE, "HrJoinProcessAnswerFile"); return hr; }
//
// Function: OnJoinDoUnattended
//
// Purpose:
//
// Parameters:
//
// Returns: BOOL
//
BOOL OnJoinDoUnattended(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); DWORD dwThreadId = 0; HRESULT hr = S_OK; CWizard * pWizard = reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER)); Assert(NULL != pWizard);
JoinData * pData = reinterpret_cast<JoinData *> (pWizard->GetPageData(IDD_Join)); Assert(NULL != pData); Assert(NULL != pData->pIdent); Assert(NULL != pData->hwndDlg);
if(pData) { // Create the unattended thread
//
HANDLE hthrd = CreateThread(NULL, STACK_SIZE_TINY, (LPTHREAD_START_ROUTINE)JoinDomainWorkThrd, (LPVOID)pData, 0, &dwThreadId); if (NULL != hthrd) { // Set the wait cursor
//
SetCursorToHourglass( hwndDlg, pData );
// Disable all the controls
//
PropSheet_SetWizButtons(GetParent(hwndDlg), 0); EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE);
CloseHandle(hthrd); } else { hr = HrFromLastWin32Error(); } }
TraceHr(ttidWizard, FAL, hr, FALSE, "OnJoinDoUnattended"); return (SUCCEEDED(hr)); }
// Determines whether the computer DNS domain name should be kept in sync with
// the DNS domain name of the domain to which it is joined. If no
// synchonization is required (unlikely, as sync is the default), do nothing.
//
// Otherwise, attempt to determine the DNS domain name of the domain and write
// this value into reg key. If the DNS domain name cannot be determined,
// write a flag to a separate reg key to indicate that whatever services that
// care about the computer DNS domain name (i.e. kerberos authentication)
// should try to fix up the name.
void fixupComputerDNSDomainName() { TraceFileFunc(ttidGuiModeSetup);
TraceTag(ttidWizard, "Entering fixupComputerDNSDomainName");
// check a reg key for the sync flag.
bool fSetName = false; HKEY hkeyParams = 0;
HRESULT hr = HrRegOpenKeyEx( HKEY_LOCAL_MACHINE, c_szIpParameters,
// all access as we may need to write a value here if we fail.
KEY_READ_WRITE, &hkeyParams);
if (SUCCEEDED(hr)) { DWORD dwValue = 0; hr = HrRegQueryDword(hkeyParams, c_szSyncDomainWithMembership, &dwValue); if (SUCCEEDED(hr) && (1 == dwValue)) { // the sync flag was present in the registry, and its value is true
fSetName = true; } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { // the sync flag was not present in the registry, which means we
// assume the default value of true.
fSetName = true; hr = S_OK; } }
if (SUCCEEDED(hr) && fSetName) { bool fixup_success = false;
// 293301 Here we attempt to determine the DNS domain name of the domain
// the machine is joined to. If we cannot determine that name (because
// a dc could not be located, for example), then we will set a flag in
// the registry so that kerberos authentication agents will come along
// and fixup the DNS domain name.
PDOMAIN_CONTROLLER_INFO pDCInfo = 0; DWORD dw = DsGetDcName( 0, 0, 0, 0,
// make sure to ask for the DNS domain name, otherwise we're
// likely to get the flatname
DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME, &pDCInfo);
if (NOERROR == dw) { Assert(pDCInfo->DomainName); Assert(pDCInfo->Flags & DS_DNS_DOMAIN_FLAG);
TraceTag(ttidWizard, "DsGetDcName succeeded %s", pDCInfo->DomainName);
if (pDCInfo->Flags & DS_DNS_DOMAIN_FLAG) { // the domain name is indeed the DNS name
// chop off any trailing '.'
WCHAR* AbsoluteSignifier = &pDCInfo->DomainName[ wcslen(pDCInfo->DomainName) - 1 ]; if (*AbsoluteSignifier == L'.') { *AbsoluteSignifier = 0; }
// set the computer's DNS domain name.
if ( SetComputerNameEx( ComputerNamePhysicalDnsDomain, pDCInfo->DomainName) ) { fixup_success = true; } #if DBG
else { // this just isn't our day.
TraceTag(ttidWizard, "SetComputerNameEx failed"); } #endif
} NetApiBufferFree(pDCInfo); } #if DBG
else { TraceTag(ttidWizard, "DsGetDcName returned %ld",dw); } #endif
// at this point, fixup_success will indicate whether we successfully
// set the computer's domain DNS name, or not. If not, then we need
// to write a flag into the registry so that kerberos auth will fix
// it up later.
if (!fixup_success) { // write a flag to have someone else do the fixup
HrRegSetDword(hkeyParams, L"DoDNSDomainFixup", 1); } }
RegCloseKey(hkeyParams); }
//
// Function: JoinUpgradeNT351orNT4toNT5
//
// Purpose: If currently processing NT4 -> NT5 upgrade, set the
// computer name.
//
// Parameters:
//
// Returns: none
//
VOID JoinUpgradeNT351orNT4toNT5(CWizard * pWizard, JoinData * pData) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr = S_OK; CSetupInfFile csif; INFCONTEXT ctx;
TraceTag(ttidWizard, "Checking for the need to do NT4->NT5 Join conversions...");
// If unattended
//
if (IsUnattended(pWizard) && (NULL != pWizard->PSetupData()) && (NULL != pWizard->PSetupData()->UnattendFile)) { hr = csif.HrOpen(pWizard->PSetupData()->UnattendFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); if (SUCCEEDED(hr)) { DWORD dw;
hr = csif.HrGetDword(c_szAfSectionNetworking, c_szAfBuildNumber, &dw); if (SUCCEEDED(hr) && ((wWinNT4BuildNumber == dw) || (wWinNT351BuildNumber == dw))) { hr = pData->pIdent->GetComputerRole(&dw); if (SUCCEEDED(hr) && (dw == GCR_MEMBER)) { //fixupComputerDNSDomainName();
TraceTag (ttidWizard, "Calling NetpUpgradePreNT5JoinInfo..."); if (NERR_Success != NetpUpgradePreNT5JoinInfo ()) { TraceHr ( ttidWizard, FAL, E_FAIL, FALSE, "NetpUpgradePreNT5JoinInfo failed. " "Likely delay load problem with netapi32.dll"); } } } } }
TraceHr(ttidWizard, FAL, hr, FALSE, "JoinUpgradeNT351orNT4toNT5"); }
//
// Function: OnJoinPageActivate
//
// Purpose:
//
// Parameters:
//
// Returns:
//
BOOL OnJoinPageActivate(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); // Retrieve the CWizard instance from the dialog
CWizard * pWizard = reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER)); Assert(NULL != pWizard); JoinData * pData = reinterpret_cast<JoinData *> (pWizard->GetPageData(IDD_Join)); Assert(NULL != pData);
TraceTag(ttidWizard, "Entering Join page...");
if (ISDC(ProductType(pWizard))) { // 412142 : we are going to skip the Join page a little farther down, but
// even for a DC, we need to do this.
//
if (FALSE == pData->fUpgraded) { JoinUpgradeNT351orNT4toNT5(pWizard, pData); pData->fUpgraded = TRUE; } }
// mbend 02/08/2000
//
// BUG 433915
// Domain/Workgroup page in setup: Check to by-pass for SBS case
// If this computer is a domain controller or there are no adapters or we don't
// want activation, then don't show the Join page.
//
if (IsSBS() || ISDC(ProductType(pWizard)) || !pWizard->PAdapterQueue()->FAdaptersInstalled() || (IsRunningOnPersonal() && !(IsUnattended(pWizard) && (FALSE == pData->fUpgraded))) ) { PAGEDIRECTION PageDir = pWizard->GetPageDirection(IDD_Join);
if (NWPD_FORWARD == PageDir) { // if forward goto exit page
//
pWizard->SetPageDirection(IDD_Join, NWPD_BACKWARD); ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_Exit); } else { // if backward goto upgrade page
//
pWizard->SetPageDirection(IDD_Join, NWPD_FORWARD); ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, IDD_Upgrade); } } else // !DC
{ // Accept focus
//
::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 0); PropSheet_SetWizButtons(GetParent(hwndDlg), 0);
// If we're in unattended mode and haven't tried to join a
// domain/workgroup then give it a try
//
if (IsUnattended(pWizard) && (FALSE == pData->fUpgraded)) { HRESULT hr;
pData->fUpgraded = TRUE;
// Raid 193450 - NT4 -> NT5 we need to SetComputerNameEx
//
JoinUpgradeNT351orNT4toNT5(pWizard, pData);
// Get the join parameters from the answer file and populate the UI
//
hr = HrJoinProcessAnswerFile(hwndDlg, pWizard, pData); if (S_FALSE == hr) { // S_FALSE was returned, this means no answerfile section
// for identification is present or not all the required
// data is present. Advance without joining unless we're in
// defaulthide mode.
// Advance regardless if we're upgrading, or running on Personal.
//
Assert(S_FALSE == hr);
if (((UM_DEFAULTHIDE == pWizard->GetUnattendedMode()) || (UM_READONLY == pWizard->GetUnattendedMode())) && (!IsUpgrade(pWizard)) && (!IsRunningOnPersonal()) ) { // Pretend something failed so that pressing NEXT will
// do the join. Basically we just populated the UI from
// the answerfile.
//
pData->fUnattendedFailed = TRUE; } else { PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L); } } else { if (FAILED(hr)) { if ( IsRunningOnPersonal() ) { // In case of Home Edition, log the error irrespective of the type of
// unattended mode since we never show domain/workgroup join page.
NetSetupLogStatusV( LogSevWarning, SzLoadIds (IDS_E_UNATTENDED_INVALID_WORKGROUP_ID_SECTION));
PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L); } else { if ( UM_FULLUNATTENDED == pWizard->GetUnattendedMode() ) { // Raid 380374: no UI allowed if in full unattended mode ..
NetSetupLogStatusV( LogSevWarning, SzLoadIds (IDS_E_UNATTENDED_INVALID_ID_SECTION));
PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L); } else { // Stop on this page, something was wrong in the answerfile
//
pData->fUnattendedFailed = TRUE; } } } else { // If we're in UM_FULLUNATTENDED or in UM_DEFAULTHIDE mode
// launch the thread which will do the unattended join
//
Assert(S_OK == hr); if ((UM_FULLUNATTENDED == pWizard->GetUnattendedMode()) || (UM_DEFAULTHIDE == pWizard->GetUnattendedMode()) || (UM_READONLY == pWizard->GetUnattendedMode())) { OnJoinDoUnattended(hwndDlg); } else { // Pretend something failed so that pressing NEXT will
// do the join. Basically we just populated the UI from
// the answerfile.
//
pData->fUnattendedFailed = TRUE; } } } }
// If something failed in the unattended case or
// If we are not unattended and we did not process this,
// make sure the page shows if called from GUI mode setup.
if (pData->fUnattendedFailed || !IsUnattended(pWizard)) { // Make the page visible if it is not an upgrade, otherwise just continue to the next page.
if (!IsUpgrade(pWizard)) { // else, make sure the page is visible.
if (g_pSetupWizard != NULL) { g_pSetupWizard->PSetupData()->ShowHideWizardPage(TRUE); }
UpdateNextBackBttns(hwndDlg); } else { PostMessage(hwndDlg, PWM_JOINSUCCESS, 0, 0L); } } }
return TRUE; }
//
// Function: OnJoinInitDialog
//
// Purpose:
//
// Parameters:
//
// Returns:
//
BOOL OnJoinInitDialog(HWND hwndDlg, LPARAM lParam) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr; CWizard * pWizard; JoinData * pData; PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam; Assert(psp->lParam); ::SetWindowLongPtr(hwndDlg, DWLP_USER, psp->lParam);
pWizard = reinterpret_cast<CWizard *>(psp->lParam); Assert(NULL != pWizard);
pData = reinterpret_cast<JoinData *>(pWizard->GetPageData(IDD_Join)); Assert(NULL != pData);
if(!pData) { return false; }
// Set the descriptive text
tstring str = SzLoadIds(IDS_TXT_JOIN_DESC_1); str += L"\n"; str += SzLoadIds(IDS_TXT_JOIN_DESC_2); SetWindowText(GetDlgItem(hwndDlg, TXT_JOIN_DESC), str.c_str());
// Set the maximum length of text in the edit control, and
// subclass it so when the control has no text the next bttn
// is disabled.
HWND hwndEditDomain = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME); SendMessage(hwndEditDomain, EM_LIMITTEXT, MAX_DOMAINNAME_LENGTH, 0L); ::SetWindowLongPtr(hwndEditDomain, GWLP_USERDATA, ::GetWindowLongPtr(hwndEditDomain, GWLP_WNDPROC)); ::SetWindowLongPtr(hwndEditDomain, GWLP_WNDPROC, (LONG_PTR)JoinEditSubclassProc);
HWND hwndEditWorkgroup = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME); SendMessage(hwndEditWorkgroup, EM_LIMITTEXT, MAX_WORKGROUPNAME_LENGTH, 0L); ::SetWindowLongPtr(hwndEditWorkgroup, GWLP_USERDATA, ::GetWindowLongPtr(hwndEditWorkgroup, GWLP_WNDPROC)); ::SetWindowLongPtr(hwndEditWorkgroup, GWLP_WNDPROC, (LONG_PTR)JoinEditSubclassProc);
pData->hwndDlg = hwndDlg;
// Initialize to Workgroup default
CheckRadioButton(hwndDlg, BTN_JOIN_WORKGROUP, BTN_JOIN_DOMAIN, BTN_JOIN_WORKGROUP);
// Get the identification interface
TraceTag(ttidWizard, "Querying computer role..."); hr = HrGetIdentInterface(&pData->pIdent); if (FAILED(hr)) { Assert(NULL == pData->pIdent); EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE); PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_NEXT); } else { // Update the UI based on the selection
UpdateJoinUsingComputerRole(hwndDlg, pWizard); }
return FALSE; }
//
// Function: OnJoinWizBack
//
// Purpose:
//
// Parameters:
//
// Returns:
//
BOOL OnJoinWizBack(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); OnProcessPrevAdapterPagePrev(hwndDlg, IDD_Upgrade);
return TRUE; }
//
// Function: GetCredentials
//
// Purpose: Prompt the user for username and password.
//
// Returns: TRUE on success, otherwise FALSE.
//
// Author: asinha 5/03/2001
//
BOOL GetCredentials (HWND hwndParent, JoinData *pData) { WCHAR szCaption[CREDUI_MAX_CAPTION_LENGTH+1]; CREDUI_INFOW uiInfo; DWORD dwErr;
TraceFileFunc(ttidGuiModeSetup);
DwFormatString(SzLoadIds(IDS_JOIN_DOMAIN_CAPTION), szCaption, celems(szCaption), pData->szDomain);
ZeroMemory( &uiInfo, sizeof(uiInfo) ); uiInfo.cbSize = sizeof(CREDUI_INFOW); uiInfo.hwndParent = hwndParent; uiInfo.pszMessageText = SzLoadIds(IDS_JOIN_DOMAIN_TEXT); uiInfo.pszCaptionText = szCaption;
dwErr = CredUIPromptForCredentialsW( &uiInfo, NULL, NULL, NO_ERROR, pData->szUserName, MAX_USERNAME_LENGTH+1, pData->szPassword, SAM_MAX_PASSWORD_LENGTH+1, NULL, CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_GENERIC_CREDENTIALS | CREDUI_FLAGS_VALIDATE_USERNAME | CREDUI_FLAGS_COMPLETE_USERNAME | CREDUI_FLAGS_EXCLUDE_CERTIFICATES | CREDUI_FLAGS_ALWAYS_SHOW_UI );
Assert(dwErr != ERROR_INVALID_PARAMETER); Assert(dwErr != ERROR_INVALID_FLAGS);
if (dwErr == ERROR_CANCELLED) { pData->szUserName[0] = 0; pData->szPassword[0] = 0; }
return NO_ERROR == dwErr; // e.g. ERROR_CANCELLED
}
//
// Function: JoinWorkgroupDomain
//
// Purpose:
//
// Parameters:
//
// Returns: nothing
//
VOID JoinWorkgroupDomain(HWND hwndDlg, CWizard * pWizard, JoinData * pData) { TraceFileFunc(ttidGuiModeSetup); DWORD dwThreadId = 0; HANDLE hthrd = NULL;
Assert(NULL != pData->pIdent); Assert(NULL != pData->hwndDlg);
// Retain the domain/workgroup name. Note that the answerfile
// populates the UI before this routine is called so requerying
// from the UI covers both cases (answerfile or user input)
//
if (IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN)) { HWND hwndEdit = GetDlgItem(hwndDlg, EDT_DOMAINJOIN_NAME); Assert(0 != GetWindowTextLength(hwndEdit));
GetWindowText(hwndEdit, pData->szDomain, MAX_DOMAINNAME_LENGTH + 1);
// If no information was seeded into the structure, prompt for it.
//
if (0 == pData->szUserName[0]) { pData->szPassword[0] = 0;
// Preserve the only Win9x upgrade flag if it was present
//
pData->dwJoinFlag &= JDF_WIN9x_UPGRADE;
// Get the username/password when joining a domain
//
BOOL bRet;
bRet = GetCredentials(GetParent(hwndDlg), pData);
// Note: Must set return as -1 otherwise join page will advance
//
::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1);
if (bRet == FALSE) { return; } } } else { HWND hwndEdit = GetDlgItem(hwndDlg, EDT_WORKGROUPJOIN_NAME); Assert(0 != GetWindowTextLength(hwndEdit)); GetWindowText(hwndEdit, pData->szDomain, MAX_WORKGROUPNAME_LENGTH + 1);
// Initialize the workstation settings for username/password
// and join flags
//
pData->szUserName[0] = 0; pData->dwJoinFlag = 0; pData->szPassword[0] = 0; MemFree(pData->pszMachineObjectOU); pData->pszMachineObjectOU = NULL; pData->szComputerPassword[0] = 0; }
// Create the thread to join the workgroup/domain
hthrd = CreateThread(NULL, STACK_SIZE_TINY, (LPTHREAD_START_ROUTINE)JoinDomainWorkThrd, (LPVOID)pData, 0, &dwThreadId); if (NULL != hthrd) { SetCursorToHourglass( hwndDlg, pData );
PropSheet_SetWizButtons(GetParent(hwndDlg), 0); EnableOrDisableDialogControls(hwndDlg, celems(nrgIdc), nrgIdc, FALSE); CloseHandle(hthrd); } else { // Failed to create the required netsetup thread
AssertSz(0,"Unable to create JoinWorkgroupDomain thread."); TraceHr(ttidWizard, FAL, E_OUTOFMEMORY, FALSE, "JoinWorkgroupDomain - Create thread failed"); } }
//
// Function: OnJoinWizNext
//
// Purpose:
//
// Parameters:
//
// Returns:
//
BOOL OnJoinWizNext(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); // Retrieve the CWizard instance from the dialog
CWizard * pWizard = reinterpret_cast<CWizard *>(::GetWindowLongPtr(hwndDlg, DWLP_USER)); Assert(NULL != pWizard);
JoinData * pData = reinterpret_cast<JoinData *> (pWizard->GetPageData(IDD_Join)); Assert(NULL != pData);
if(!pData) { return false; }
// Attempt to join the workgroup/domain if we have the Ident interface
//
if (pData->pIdent) {
// Ensure the user supplied a workgroup/domain name
//
if (0 == GetWindowTextLength(GetDlgItem(hwndDlg, GetJoinNameIIDFromSelection(hwndDlg)))) { ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1); return TRUE; }
// Join the workgroup/domain
//
if (!IsUnattended(pWizard) || pData->fUnattendedFailed) { DNS_STATUS dnsStatus;
if ( IsDlgButtonChecked(hwndDlg, BTN_JOIN_DOMAIN) ) {
dnsStatus = IsValidDomainName( hwndDlg ); } else { dnsStatus = ERROR_SUCCESS; }
if ( (dnsStatus == ERROR_SUCCESS) || (dnsStatus == DNS_ERROR_NON_RFC_NAME) ) { JoinWorkgroupDomain(hwndDlg, pWizard, pData); } else { tstring str;
str = SzLoadIds(IDS_JOIN_E_DOMAIN_INVALID_NAME);
MessageBox(GetParent(hwndDlg), str.c_str(), SzLoadIds(IDS_SETUP_CAPTION), MB_OK); } }
::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1); } else { OnJoinSuccess(hwndDlg); }
return TRUE; }
//
// Function: dlgprocJoin
//
// Purpose: Dialog Procedure for the Join wizard page
//
// Parameters: standard dlgproc parameters
//
// Returns: INT_PTR
//
INT_PTR CALLBACK dlgprocJoin( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { TraceFileFunc(ttidGuiModeSetup); BOOL frt = FALSE;
switch (uMsg) { case PWM_JOINFAILURE: frt = OnJoinFailure(hwndDlg, lParam); break;
case PWM_JOINSUCCESS: frt = OnJoinSuccess(hwndDlg); break;
case WM_INITDIALOG: frt = OnJoinInitDialog(hwndDlg, lParam); break;
case WM_COMMAND: { if ((BN_CLICKED == HIWORD(wParam)) && ((BTN_JOIN_DOMAIN == LOWORD(wParam)) || (BTN_JOIN_WORKGROUP == LOWORD(wParam)))) { JoinUpdatePromptText(hwndDlg); } } break;
case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam;
switch (pnmh->code) { // propsheet notification
case PSN_HELP: break;
case PSN_SETACTIVE: frt = OnJoinPageActivate(hwndDlg); break;
case PSN_APPLY: break;
case PSN_KILLACTIVE: break;
case PSN_RESET: break;
case PSN_WIZBACK: frt = OnJoinWizBack(hwndDlg); break;
case PSN_WIZFINISH: break;
case PSN_WIZNEXT: frt = OnJoinWizNext(hwndDlg); break;
default: break; } } break;
default: break; }
return( frt ); }
//
// Function: JoinPageCleanup
//
// Purpose: As a callback function to allow any page allocated memory
// to be cleaned up, after the page will no longer be accessed.
//
// Parameters: pWizard [IN] - The wizard against which the page called
// register page
// lParam [IN] - The lParam supplied in the RegisterPage call
//
// Returns: nothing
//
VOID JoinPageCleanup(CWizard *pWizard, LPARAM lParam) { TraceFileFunc(ttidGuiModeSetup); JoinData * pData = reinterpret_cast<JoinData *>(lParam); if (NULL != pData) { delete pData->pIdent; } MemFree(reinterpret_cast<void*>(lParam)); }
//
// Function: CreateJoinPage
//
// Purpose: To determine if the Join page needs to be shown, and to
// to create the page if requested. Note the Join page is
// responsible for initial installs also.
//
// Parameters: pWizard [IN] - Ptr to a Wizard instance
// pData [IN] - Context data to describe the world in
// which the Wizard will be run
// fCountOnly [IN] - If True, only the maximum number of
// pages this routine will create need
// be determined.
// pnPages [IN] - Increment by the number of pages
// to create/created
//
// Returns: HRESULT, S_OK on success
//
HRESULT HrCreateJoinPage(CWizard *pWizard, PINTERNAL_SETUP_DATA pData, BOOL fCountOnly, UINT *pnPages) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr = S_OK;
// Batch Mode or for fresh install
if (!IsPostInstall(pWizard)) { // If not only counting, create and register the page
if (!fCountOnly) { JoinData * pData = NULL; HPROPSHEETPAGE hpsp; PROPSHEETPAGE psp;
TraceTag(ttidWizard, "Creating Join Page"); hr = E_OUTOFMEMORY; pData = reinterpret_cast<JoinData *>(MemAlloc(sizeof(JoinData))); if (pData) { pData->fUnattendedFailed = FALSE; pData->fUpgraded = FALSE; pData->pIdent = NULL; pData->hOldCursor = NULL; pData->hwndDlg = NULL; pData->dwJoinFlag = 0; pData->szUserName[0] = 0; pData->szPassword[0] = 0; pData->szDomain[0] = 0; pData->pszMachineObjectOU = NULL; pData->szComputerPassword[0] = 0;
psp.dwSize = sizeof( PROPSHEETPAGE ); psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; psp.hInstance = _Module.GetResourceInstance(); psp.pszTemplate = MAKEINTRESOURCE( IDD_Join ); psp.hIcon = NULL; psp.pfnDlgProc = dlgprocJoin; psp.lParam = reinterpret_cast<LPARAM>(pWizard); psp.pszHeaderTitle = SzLoadIds(IDS_T_Join); psp.pszHeaderSubTitle = SzLoadIds(IDS_ST_Join);
hpsp = CreatePropertySheetPage( &psp ); if (hpsp) { pWizard->RegisterPage(IDD_Join, hpsp, JoinPageCleanup, reinterpret_cast<LPARAM>(pData)); hr = S_OK; } else { MemFree(pData); } } }
if (SUCCEEDED(hr)) { (*pnPages)++; } }
TraceHr(ttidWizard, FAL, hr, FALSE, "HrCreateJoinPage"); return hr; }
//
// Function: AppendJoinPage
//
// Purpose: Add the Join page, if it was created, to the set of pages
// that will be displayed.
//
// Parameters: pWizard [IN] - Ptr to Wizard Instance
// pahpsp [IN,OUT] - Array of pages to add our page to
// pcPages [IN,OUT] - Count of pages in pahpsp
//
// Returns: Nothing
//
VOID AppendJoinPage(CWizard *pWizard, HPROPSHEETPAGE* pahpsp, UINT *pcPages) { TraceFileFunc(ttidGuiModeSetup); if (!IsPostInstall(pWizard)) { HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_Join); Assert(hPage); pahpsp[*pcPages] = hPage; (*pcPages)++; } }
|