//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: fsmoui.cpp // //-------------------------------------------------------------------------- // File: fsmoui.cpp #include "stdafx.h" #include "dsutil.h" #include "util.h" #include "uiutil.h" #include "fsmoui.h" #include "helpids.h" #include "dsgetdc.h" //DsEnumerateDomainTrusts #include "lm.h" //NetApiBufferFree #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////////////////////////////////////////////////////////// // CFsmoPropertyPage BEGIN_MESSAGE_MAP(CFsmoPropertyPage, CPropertyPage) ON_BN_CLICKED(IDC_CHANGE_FSMO, OnChange) ON_WM_HELPINFO() END_MESSAGE_MAP() CFsmoPropertyPage::CFsmoPropertyPage(CFsmoPropertySheet* pSheet, FSMO_TYPE fsmoType) { m_pSheet = pSheet; m_fsmoType = fsmoType; // load the caption (tab control text) depending on the FSMO type UINT nIDCaption = 0; switch (m_fsmoType) { case RID_POOL_FSMO: nIDCaption = IDS_RID_POOL_FSMO; break; case PDC_FSMO: nIDCaption = IDS_PDC_FSMO; break; case INFRASTUCTURE_FSMO: nIDCaption = IDS_INFRASTRUCTURE_FSMO; break; }; Construct(IDD_FSMO_PAGE, nIDCaption); } BOOL CFsmoPropertyPage::OnInitDialog() { CPropertyPage::OnInitDialog(); // // We just want a close button since we are not applying any changes // directly from this page // ::SendMessage(GetParent()->GetSafeHwnd(), PSM_CANCELTOCLOSE, 0, 0); // init the status (online/offline) control) m_fsmoServerState.Init(::GetDlgItem(m_hWnd, IDC_STATIC_FSMO_STATUS)); // set the server we are focused on SetDlgItemText(IDC_EDIT_CURRENT_DC, m_pSheet->GetBasePathsInfo()->GetServerName()); // set the FSMO description CString szDesc; switch (m_fsmoType) { case RID_POOL_FSMO: VERIFY(szDesc.LoadString(IDS_RID_POOL_FSMO_DESC)); break; case PDC_FSMO: VERIFY(szDesc.LoadString(IDS_PDC_FSMO_DESC)); break; case INFRASTUCTURE_FSMO: VERIFY(szDesc.LoadString(IDS_INFRASTRUCTURE_FSMO_DESC)); break; }; SetDlgItemText(IDC_STATIC_FSMO_DESC, szDesc); { // scope for the wait cursor object CWaitCursor wait; // retrieve the FSMO owner MyBasePathsInfo fsmoOwnerInfo; PWSTR pszFsmoOwner = 0; HRESULT hr = FindFsmoOwner(m_pSheet->GetBasePathsInfo(), m_fsmoType, &fsmoOwnerInfo, &pszFsmoOwner); if (SUCCEEDED(hr) && pszFsmoOwner) { m_szFsmoOwnerServerName = pszFsmoOwner; delete[] pszFsmoOwner; pszFsmoOwner = 0; } _SetFsmoServerStatus(SUCCEEDED(hr)); } return TRUE; } #ifdef DBG UINT GetInfoFromIniFileIfDBG(LPCWSTR lpszSection, LPCWSTR lpszKey, INT nDefault = 0) { static LPCWSTR lpszFile = L"\\system32\\dsadmin.ini"; WCHAR szFilePath[2*MAX_PATH]; UINT nLen = ::GetSystemWindowsDirectory(szFilePath, 2*MAX_PATH); if (nLen == 0) return nDefault; wcscat(szFilePath, lpszFile); return ::GetPrivateProfileInt(lpszSection, lpszKey, nDefault, szFilePath); } #endif void CFsmoPropertyPage::OnChange() { // Test stuff /* { HRESULT hrTest = E_OUTOFMEMORY; BOOL bTest = AllowForcedTransfer(hrTest); return; } */ CThemeContextActivator activator; // verify we have different servers if (m_szFsmoOwnerServerName.CompareNoCase(m_pSheet->GetBasePathsInfo()->GetServerName()) == 0) { ReportErrorEx(m_hWnd,IDS_WARNING_FSMO_CHANGE_FOCUS,S_OK, MB_OK | MB_ICONERROR, NULL, 0); return; } bool bConfirm = false; //ask for confirmation only once if( m_fsmoType == INFRASTUCTURE_FSMO ) { //check if target DC is GC //Try to bind to GC port, fails if not GC IADs *pObject; HRESULT hr1; CString strServer = L"GC://"; strServer += m_pSheet->GetBasePathsInfo()->GetServerName(); hr1 = DSAdminOpenObject(strServer, IID_IADs, (void**) &pObject, TRUE /*bServer*/); if (SUCCEEDED(hr1)) { //Release Interface, we don't need it pObject->Release(); //Check if domain has any trusted domains in the forest // The Infrastructure Master is responsible for ensuring // referencial integrity in the database (all DN attributes // actually have an object at the other end). The problem // with having the Infrastructure Master on a GC is that // when there are references across domain the object // actually exists instead of a phantom (placeholder) that the DS inserts // into the database. Normally the phantom would tell the DS // to check in the other domain to see if the object has been // moved or deleted, but since the "real" object is already in // the database (because its a GC) then the integrity can be // broken. So we only have to warn the user when transferring to // a GC if there is the possibility for cross domain references DS_DOMAIN_TRUSTS *Domains = 0; DWORD result = 0; ULONG DomainCount=0; result = DsEnumerateDomainTrusts ( (LPWSTR)m_pSheet->GetBasePathsInfo()->GetServerName(), DS_DOMAIN_IN_FOREST, &Domains, &DomainCount ); if( HRESULT_CODE(result) == NO_ERROR ) { NetApiBufferFree( Domains ); if( DomainCount > 0 ) { LPTSTR ptzFormat = NULL; LPTSTR ptzMessage = NULL; int cch = 0; INT_PTR retval; // load message format if (!LoadStringToTchar(IDS_IFSMO_TARGET_DC_IS_GC, &ptzFormat)) { ASSERT(FALSE); } PVOID apv[2] = { NULL, (LPWSTR)m_pSheet->GetBasePathsInfo()->GetServerName() }; // generate actual message cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, ptzFormat, NULL, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (PTSTR)&ptzMessage, 0, (va_list*)apv); if (!cch) { ASSERT(FALSE); } //NTRAID#NTBUG9-572002-2002/03/10-jmessec not handling error if cch == 0 (not critical) CMoreInfoMessageBox dlg(m_hWnd, m_pSheet->GetIDisplayHelp(), TRUE, true); dlg.SetMessage(ptzMessage); dlg.SetURL(DSADMIN_MOREINFO_FSMO_TARGET_DC_IS_GC); retval = dlg.DoModal(); bConfirm = true; //clean up if( NULL != ptzFormat ) { delete ptzFormat; } if( NULL != ptzMessage ) { LocalFree(ptzMessage); } if( retval == IDCANCEL ) { return; } } } } } // make sure the user wants to do it if (!bConfirm && ReportErrorEx(m_hWnd,IDS_CHANGE_FSMO_CONFIRMATION,S_OK, MB_YESNO | MB_ICONWARNING|MB_DEFBUTTON2, NULL, 0) != IDYES) return; // try a graceful transfer HRESULT hr; { // scope for the wait cursor object CWaitCursor wait; if ( m_fsmoType == PDC_FSMO ) { hr = CheckpointFsmoOwnerTransfer(m_pSheet->GetBasePathsInfo()); TRACE(_T("back from Checkpoint API, hr is %lx\n"), hr); if (FAILED(hr)) { // // See if we are in native mode or mixed mode // BOOL bMixedMode = TRUE; CString szDomainRoot; m_pSheet->GetBasePathsInfo()->GetDefaultRootPath(szDomainRoot); if (!szDomainRoot.IsEmpty()) { // // bind to the domain object // CComPtr spDomainObj; hr = DSAdminOpenObject(szDomainRoot, IID_IADs, (void **) &spDomainObj, TRUE /*bServer*/); if (SUCCEEDED(hr)) { // // retrieve the mixed node attribute // CComVariant Mixed; CComBSTR bsMixed(L"nTMixedDomain"); spDomainObj->Get(bsMixed, &Mixed); bMixedMode = (BOOL)Mixed.bVal; } } if (bMixedMode) { if (ReportErrorEx(m_hWnd, IDS_CHANGE_FSMO_CHECKPOINT_FAILED, S_OK, MB_YESNO | MB_ICONWARNING, NULL, 0) != IDYES) { return; } } else { if (ReportErrorEx(m_hWnd, IDS_CHANGE_FSMO_CHECKPOINT_FAILED_NATIVEMODE, S_OK, MB_YESNO | MB_ICONWARNING, NULL, 0) != IDYES) { return; } } } } hr = GracefulFsmoOwnerTransfer(m_pSheet->GetBasePathsInfo(), m_fsmoType); } if (FAILED(hr)) { if (!AllowForcedTransfer(hr)) return; // try the forced transfer CWaitCursor wait; hr = ForcedFsmoOwnerTransfer(m_pSheet->GetBasePathsInfo(), m_fsmoType); } if (SUCCEEDED(hr)) { m_szFsmoOwnerServerName = m_pSheet->GetBasePathsInfo()->GetServerName(); _SetFsmoServerStatus(TRUE); ReportErrorEx(m_hWnd,IDS_CHANGE_FSMO_SUCCESS,S_OK, MB_OK, NULL, 0); } else { ReportErrorEx(m_hWnd, IDS_ERROR_CHANGE_FSMO_OWNER, hr, MB_OK | MB_ICONERROR, NULL, 0); } } void CFsmoPropertyPage::_SetFsmoServerStatus(BOOL bOnLine) { // set the FSMO owner server name if (m_szFsmoOwnerServerName.IsEmpty()) { CString szError; szError.LoadString(IDS_FSMO_SERVER_ERROR); SetDlgItemText(IDC_EDIT_CURRENT_FSMO_DC, szError); } else { SetDlgItemText(IDC_EDIT_CURRENT_FSMO_DC, m_szFsmoOwnerServerName); } // set the status of the FSMO owner server m_fsmoServerState.SetToggleState(bOnLine); } void CFsmoPropertyPage::OnHelpInfo(HELPINFO * pHelpInfo ) { TRACE(_T("OnHelpInfo: CtrlId = %d, ContextId = 0x%x\n"), pHelpInfo->iCtrlId, pHelpInfo->dwContextId); if (pHelpInfo->iCtrlId < 1) { return; } DWORD_PTR HelpArray = 0; switch (m_fsmoType) { case RID_POOL_FSMO: HelpArray = (DWORD_PTR)g_aHelpIDs_IDD_RID_FSMO_PAGE; break; case PDC_FSMO: HelpArray = (DWORD_PTR)g_aHelpIDs_IDD_PDC_FSMO_PAGE; break; case INFRASTUCTURE_FSMO: HelpArray = (DWORD_PTR)g_aHelpIDs_IDD_INFRA_FSMO_PAGE; break; }; ::WinHelp((HWND)pHelpInfo->hItemHandle, DSADMIN_CONTEXT_HELP_FILE, HELP_WM_HELP, HelpArray); } void ChangeFormatParamOnString(CString& szFmt) { int nPos = szFmt.Find(L"%1"); if (nPos == -1) return; szFmt.SetAt(nPos+1, L's'); } BOOL CFsmoPropertyPage::AllowForcedTransfer(HRESULT hr) { CThemeContextActivator activator; BOOL bAllow = FALSE; PWSTR pszError = 0; StringErrorFromHr(hr, &pszError); // retrieve the DWORD error code DWORD dwErr = (hr & 0x0000FFFF); if ( (dwErr != ERROR_ACCESS_DENIED) && ((m_fsmoType == PDC_FSMO) || (m_fsmoType == INFRASTUCTURE_FSMO))) { // allow forced, so ask CString szFmt, szMsg; szFmt.LoadString(IDS_CHANGE_FSMO_CONFIRMATION_FORCED); szMsg.Format(szFmt, pszError); CMoreInfoMessageBox dlg(m_hWnd, m_pSheet->GetIDisplayHelp(), TRUE, true); dlg.SetMessage(szMsg); dlg.SetURL((m_fsmoType == PDC_FSMO) ? DSADMIN_MOREINFO_PDC_FSMO_TOPIC : DSADMIN_MOREINFO_INFRASTUCTURE_FSMO_TOPIC); if (dlg.DoModal() == IDOK) bAllow = TRUE; } else { // warn only, no forced transfer option CString szFmt, szMsg; szFmt.LoadString(IDS_ERROR_CHANGE_FSMO_OWNER); // this format string has the replaceable parameter marked as %1 // we need it changed into %s ChangeFormatParamOnString(szFmt); szMsg.Format(szFmt, pszError); CMoreInfoMessageBox dlg(m_hWnd, m_pSheet->GetIDisplayHelp(), FALSE); dlg.SetMessage(szMsg); dlg.SetURL(DSADMIN_MOREINFO_RID_POOL_FSMO_TOPIC); dlg.DoModal(); } if (pszError) { delete[] pszError; pszError = 0; } return bAllow; } ////////////////////////////////////////////////////////////////// // CFsmoPropertySheet int CALLBACK CFsmoPropertySheet::PropSheetCallBack(HWND hwndDlg, UINT uMsg, LPARAM) { switch (uMsg) { case PSCB_INITIALIZED: DWORD dwStyle = GetWindowLong (hwndDlg, GWL_EXSTYLE); dwStyle |= WS_EX_CONTEXTHELP; SetWindowLong (hwndDlg, GWL_EXSTYLE, dwStyle); break; } return 0; } CFsmoPropertySheet::CFsmoPropertySheet(MyBasePathsInfo* pInfo, HWND HWndParent, IDisplayHelp* pIDisplayHelp, LPCWSTR) : m_spIDisplayHelp(pIDisplayHelp), m_pInfo(pInfo), m_page1(this, RID_POOL_FSMO), m_page2(this, PDC_FSMO), m_page3(this, INFRASTUCTURE_FSMO) { // build the sheet title CString szTitle; szTitle.LoadString(IDS_FSMO_SHEET_TITLE); // delayed construction Construct(szTitle, CWnd::FromHandle(HWndParent)); m_psh.dwFlags |= PSH_NOAPPLYNOW | PSH_USECALLBACK; m_psh.pfnCallback = PropSheetCallBack; AddPage(&m_page1); AddPage(&m_page2); AddPage(&m_page3); }