#include "stdafx.h" #include "grpinfo.h" #include // DsGetDCName and DS structures #include #include // ADsGetObject #include #include #pragma hdrstop CGroupPageBase* g_pGroupPageBase; // used for the group page DWORD g_dwWhichNet = 0; UINT g_uWizardIs = NAW_NETID; BOOL g_fRebootOnExit = FALSE; BOOL g_fShownLastPage = FALSE; BOOL g_fCreatedConnection = FALSE; // we created a RAS connection during the wizard, therefore kill it on exit BOOL g_fMachineRenamed = FALSE; WCHAR g_szUser[MAX_DOMAINUSER + 1] = { L'\0' }; WCHAR g_szDomain[MAX_DOMAIN + 1] = { L'\0' }; WCHAR g_szCompDomain[MAX_DOMAIN + 1] = { L'\0' }; // Stuff for creating a default autologon user #define ITEMDATA_DEFAULTLOCALUSER 0xDEADBEEF // default workgroup to be joined #define DEFAULT_WORKGROUP L"WORKGROUP" // Set the Wizard buttons for the dialog void SetWizardButtons(HWND hwndPage, DWORD dwButtons) { HWND hwndParent = GetParent(hwndPage); if (g_uWizardIs != NAW_NETID) { EnableWindow(GetDlgItem(hwndParent,IDHELP),FALSE); ShowWindow(GetDlgItem(hwndParent,IDHELP),SW_HIDE); } if (g_fRebootOnExit) { TCHAR szBuffer[80]; LoadString(g_hinst, IDS_CLOSE, szBuffer, ARRAYSIZE(szBuffer)); SetDlgItemText(hwndParent, IDCANCEL, szBuffer); } PropSheet_SetWizButtons(hwndParent, dwButtons); } // intro dialog - set the title text etc INT_PTR CALLBACK _IntroDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { SendDlgItemMessage(hwnd, IDC_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0); return TRUE; } case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: SetWizardButtons(hwnd, PSWIZB_NEXT); return TRUE; case PSN_WIZNEXT: { switch (g_uWizardIs) { case NAW_PSDOMAINJOINED: WIZARDNEXT(hwnd, IDD_PSW_ADDUSER); break; default: // Let the wizard go to the next page break; } return TRUE; } } break; } } return FALSE; } // how do they user this machine corp/vs home INT_PTR CALLBACK _HowUseDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: CheckRadioButton(hwnd, IDC_NETWORKED, IDC_NOTNETWORKED, IDC_NETWORKED); return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: SetWizardButtons(hwnd, PSWIZB_NEXT|PSWIZB_BACK); return TRUE; case PSN_WIZBACK: WIZARDNEXT(hwnd, IDD_PSW_WELCOME); return TRUE; case PSN_WIZNEXT: { if (IsDlgButtonChecked(hwnd, IDC_NETWORKED) == BST_CHECKED) { WIZARDNEXT(hwnd, IDD_PSW_WHICHNET); } else { g_dwWhichNet = IDC_NONE; if (SUCCEEDED(JoinDomain(hwnd, FALSE, DEFAULT_WORKGROUP, NULL, &g_fRebootOnExit))) { WIZARDNEXT(hwnd, IDD_PSW_DONE); } else { WIZARDNEXT(hwnd, -1); } } return TRUE; } } break; } } return FALSE; } // determine the network they want to join INT_PTR CALLBACK _WhichNetDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: CheckRadioButton(hwnd, IDC_DOMAIN, IDC_WORKGROUP, IDC_DOMAIN); return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: SetWizardButtons(hwnd, PSWIZB_NEXT|PSWIZB_BACK); return TRUE; case PSN_WIZBACK: WIZARDNEXT(hwnd, IDD_PSW_HOWUSE); return TRUE; case PSN_WIZNEXT: { if (IsDlgButtonChecked(hwnd, IDC_DOMAIN) == BST_CHECKED) { g_dwWhichNet = IDC_DOMAIN; WIZARDNEXT(hwnd, IDD_PSW_DOMAININFO); } else { g_dwWhichNet = IDC_WORKGROUP; WIZARDNEXT(hwnd, IDD_PSW_WORKGROUP); } return TRUE; } } break; } } return FALSE; } // we are joining a workgroup etc INT_PTR CALLBACK _WorkgroupDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { Edit_LimitText(GetDlgItem(hwnd, IDC_WORKGROUP), MAX_WORKGROUP); SetDlgItemText(hwnd, IDC_WORKGROUP, DEFAULT_WORKGROUP); return TRUE; } case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: { DWORD dwButtons = PSWIZB_NEXT|PSWIZB_BACK; if (!FetchTextLength(hwnd, IDC_WORKGROUP)) dwButtons &= ~PSWIZB_NEXT; SetWizardButtons(hwnd, dwButtons); return TRUE; } case PSN_WIZBACK: WIZARDNEXT(hwnd, IDD_PSW_WHICHNET); return TRUE; case PSN_WIZNEXT: { WCHAR szWorkgroup[MAX_WORKGROUP+1]; FetchText(hwnd, IDC_WORKGROUP, szWorkgroup, ARRAYSIZE(szWorkgroup)); if (SUCCEEDED(JoinDomain(hwnd, FALSE, szWorkgroup, NULL, &g_fRebootOnExit))) { ClearAutoLogon(); WIZARDNEXT(hwnd, IDD_PSW_DONE); } else { WIZARDNEXT(hwnd, -1); } return TRUE; } } break; } case WM_COMMAND: { if (HIWORD(wParam) == EN_CHANGE) { DWORD dwButtons = PSWIZB_NEXT|PSWIZB_BACK; if (!FetchTextLength(hwnd, IDC_WORKGROUP)) dwButtons &= ~PSWIZB_NEXT; SetWizardButtons(hwnd, dwButtons); return TRUE; } break; } } return FALSE; } // were done, show the final page INT_PTR CALLBACK _DoneDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: SendDlgItemMessage(hwnd, IDC_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0); return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: { TCHAR szBuffer[MAX_PATH]; // change the closing prompt if we are supposed to be LoadString(g_hinst, g_fRebootOnExit ? IDS_NETWIZFINISHREBOOT:IDS_NETWIZFINISH, szBuffer, ARRAYSIZE(szBuffer)); SetDlgItemText(hwnd, IDC_FINISHSTATIC, szBuffer); SetWizardButtons(hwnd, PSWIZB_BACK|PSWIZB_FINISH); g_fShownLastPage = TRUE; // show the last page of the wizard return TRUE; } case PSN_WIZBACK: { switch (g_dwWhichNet) { case IDC_DOMAIN: WIZARDNEXT(hwnd, g_fMachineRenamed ? IDD_PSW_COMPINFO : IDD_PSW_ADDUSER); break; case IDC_WORKGROUP: WIZARDNEXT(hwnd, IDD_PSW_WORKGROUP); break; case IDC_NONE: WIZARDNEXT(hwnd, IDD_PSW_HOWUSE); break; } return TRUE; } } break; } } return FALSE; } // subclass this is used for the setup scenario where we want to remove various // buttons and stop the dialog from being moved. therefore we subclass the // wizard during its creation and lock its place. static WNDPROC _oldDlgWndProc; LRESULT CALLBACK _WizardSubWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // // on WM_WINDOWPOSCHANGING and the window is moving then lets centre it onto the // desktop window. unfortunately setting the DS_CENTER bit doesn't buy us anything // as the wizard is resized after creation. // if (uMsg == WM_WINDOWPOSCHANGING) { LPWINDOWPOS lpwp = (LPWINDOWPOS)lParam; RECT rcDlg, rcDesktop; GetWindowRect(hwnd, &rcDlg); GetWindowRect(GetDesktopWindow(), &rcDesktop); lpwp->x = ((rcDesktop.right-rcDesktop.left)-(rcDlg.right-rcDlg.left))/2; lpwp->y = ((rcDesktop.bottom-rcDesktop.top)-(rcDlg.bottom-rcDlg.top))/2; lpwp->flags &= ~SWP_NOMOVE; } return _oldDlgWndProc(hwnd, uMsg, wParam, lParam); } int CALLBACK _PropSheetCB(HWND hwnd, UINT uMsg, LPARAM lParam) { switch (uMsg) { // in pre-create lets set the window styles accorindlgy // - remove the context menu and system menu case PSCB_PRECREATE: { DLGTEMPLATE *pdlgtmp = (DLGTEMPLATE*)lParam; pdlgtmp->style &= ~(DS_CONTEXTHELP|WS_SYSMENU); break; } // we now have a dialog, so lets sub class it so we can stop it being // move around. case PSCB_INITIALIZED: { if (g_uWizardIs != NAW_NETID) _oldDlgWndProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)_WizardSubWndProc); break; } } return FALSE; } // gather domain information about the user INT_PTR CALLBACK _DomainInfoDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch ( uMsg ) { case WM_INITDIALOG: return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: SetWizardButtons(hwnd, PSWIZB_NEXT|PSWIZB_BACK); return TRUE; case PSN_WIZBACK: { if ( g_uWizardIs != NAW_NETID ) WIZARDNEXT(hwnd, IDD_PSW_WELCOME); return TRUE; } } break; } } return FALSE; } // handle searching the active directory for an object // // Search columns are returns as a ADS_SEARCH_COLUMN which is like a variant, // but, the data form is more specific to a DS. // // We only need strings, therefore barf if any other type is given to us. // HRESULT _GetStringFromColumn(ADS_SEARCH_COLUMN *pasc, LPWSTR pBuffer, INT cchBuffer) { switch ( pasc->dwADsType ) { case ADSTYPE_DN_STRING: case ADSTYPE_CASE_EXACT_STRING: case ADSTYPE_CASE_IGNORE_STRING: case ADSTYPE_PRINTABLE_STRING: case ADSTYPE_NUMERIC_STRING: StrCpyN(pBuffer, pasc->pADsValues[0].DNString, cchBuffer); break; default: return E_FAIL; } return S_OK; } // // Search the DS for a computer object that matches this computer name, if // we find one then try and crack the name to give us something that // can be used to join a domain. // HRESULT _FindComputerInDomain(LPWSTR pszUserName, LPWSTR pszUserDomain, LPWSTR pszSearchDomain, LPWSTR pszPassword, BSTR *pbstrCompDomain) { HRESULT hres; CWaitCursor cur; HRESULT hrInit = SHCoInitialize(); WCHAR wszComputerObjectPath[MAX_PATH + 1] = { 0 }; // path to the computer object // Lets try and deterrmine the domain to search by taking the users domain and // calling DsGetDcName with it. PDOMAIN_CONTROLLER_INFO pdci; DWORD dwres = DsGetDcName(NULL, pszSearchDomain, NULL, NULL, DS_RETURN_DNS_NAME|DS_DIRECTORY_SERVICE_REQUIRED, &pdci); if ( (NO_ERROR == dwres) && pdci->DnsForestName ) { TCHAR szDomainUser[MAX_DOMAINUSER + 1]; MakeDomainUserString(pszUserDomain, pszUserName, szDomainUser, ARRAYSIZE(szDomainUser)); WCHAR szBuffer[MAX_PATH + 1]; wsprintf(szBuffer, L"GC://%s", pdci->DnsForestName); // now open the GC with the domain user (formatting the forest name above) IDirectorySearch* pds = NULL; hres = ADsOpenObject(szBuffer, szDomainUser, pszPassword, ADS_SECURE_AUTHENTICATION, IID_PPV_ARG(IDirectorySearch, &pds)); if (SUCCEEDED(hres)) { // we have a GC object, so lets search it... ADS_SEARCHPREF_INFO prefInfo[1]; prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; // sub-tree search prefInfo[0].vValue.dwType = ADSTYPE_INTEGER; prefInfo[0].vValue.Integer = ADS_SCOPE_SUBTREE; hres = pds->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo)); if (SUCCEEDED(hres)) { LPWSTR c_aszAttributes[] = { L"ADsPath", }; // using the computer name for this object lets scope the query accordingly WCHAR szComputerName[MAX_COMPUTERNAME + 1]; DWORD dwComputerName = ARRAYSIZE(szComputerName); GetComputerName(szComputerName, &dwComputerName); wsprintf(szBuffer, L"(&(sAMAccountType=805306369)(sAMAccountName=%s$))", szComputerName); // issue the query ADS_SEARCH_HANDLE hSearch = NULL; hres = pds->ExecuteSearch(szBuffer, c_aszAttributes, ARRAYSIZE(c_aszAttributes), &hSearch); if (SUCCEEDED(hres)) { // we executed the search, so we can now attempt to read the results back hres = pds->GetNextRow(hSearch); if (SUCCEEDED(hres) && (hres != S_ADS_NOMORE_ROWS)) { // we received a result back, so lets get the ADsPath of the computer ADS_SEARCH_COLUMN ascADsPath; hres = pds->GetColumn(hSearch, L"ADsPath", &ascADsPath); if (SUCCEEDED(hres)) hres = _GetStringFromColumn(&ascADsPath, wszComputerObjectPath, ARRAYSIZE(wszComputerObjectPath)); } pds->CloseSearchHandle(hSearch); } } pds->Release(); } NetApiBufferFree(pdci); } else { hres = E_FAIL; } // So we found an object that is of the category computer, and it has the same name // as the computer object we are looking for. Lets try and crack the name now // and determine which domain it is in. if (SUCCEEDED(hres)) { IADsPathname* padp = NULL; hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&padp); if (SUCCEEDED(hres)) { hres = padp->Set(wszComputerObjectPath, ADS_SETTYPE_FULL); if (SUCCEEDED(hres)) { BSTR bstrX500DN = NULL; hres = padp->Retrieve(ADS_FORMAT_X500_DN, &bstrX500DN); if (SUCCEEDED(hres)) { PDS_NAME_RESULT pdnr = NULL; dwres = DsCrackNames(NULL, DS_NAME_FLAG_SYNTACTICAL_ONLY, DS_FQDN_1779_NAME, DS_CANONICAL_NAME, 1, &bstrX500DN, &pdnr); if ( (NO_ERROR == dwres) && (pdnr->cItems == 1)) { // try and get the NETBIOS name for the domain dwres = DsGetDcName(NULL, pdnr->rItems->pDomain, NULL, NULL, DS_IS_DNS_NAME|DS_RETURN_FLAT_NAME, &pdci); if (NO_ERROR == dwres) { if ( pbstrCompDomain ) *pbstrCompDomain = SysAllocString(pdci->DomainName); hres = ((pbstrCompDomain && !*pbstrCompDomain)) ? E_OUTOFMEMORY:S_OK; } else { hres = E_FAIL; // no flat name for the domain } DsFreeNameResult(pdnr); } else { hres = E_FAIL; // failed to find the computer in the domain } SysFreeString(bstrX500DN); } } padp->Release(); } } SHCoUninitialize(hrInit); return hres; } // This is the phonebook callback, it is used to notify the book of the user name, domain // and password to be used in this connection. It is also used to receive changes made by // the user. VOID WINAPI _PhoneBkCB(ULONG_PTR dwCallBkID, DWORD dwEvent, LPWSTR pszEntry, void *pEventArgs) { RASNOUSER *pInfo = (RASNOUSER *)pEventArgs; CREDINFO *pci = (CREDINFO *)dwCallBkID; switch ( dwEvent ) { case RASPBDEVENT_NoUser: { // // we are about to initialize the phonebook dialog, therefore // lets pass through our credential information. // pInfo->dwSize = SIZEOF(RASNOUSER); pInfo->dwFlags = 0; pInfo->dwTimeoutMs = 0; StrCpy(pInfo->szUserName, pci->pszUser); StrCpy(pInfo->szDomain, pci->pszDomain); StrCpy(pInfo->szPassword, pci->pszPassword); break; } case RASPBDEVENT_NoUserEdit: { // // the user has changed the credetials we supplied for the // login, therefore we must update them in our copy accordingly. // if ( pInfo->szUserName[0] ) StrCpyN(pci->pszUser, pInfo->szUserName, pci->cchUser); if ( pInfo->szPassword[0] ) StrCpyN(pci->pszPassword, pInfo->szPassword, pci->cchPassword); if ( pInfo->szDomain[0] ) StrCpyN(pci->pszDomain, pInfo->szDomain, pci->cchDomain); break; } } } // modify the RAS key for allowing phone book edits - so we can create a connectiod // during setup. BOOL SetAllowKey(DWORD dwNewValue, DWORD* pdwOldValue) { BOOL fValueWasSet = FALSE; HKEY hkey = NULL; if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_USERS, TEXT(".DEFAULT\\Software\\Microsoft\\RAS Logon Phonebook"), NULL, TEXT(""), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL)) { const LPCTSTR pcszAllowEdit = TEXT("AllowLogonPhonebookEdits"); if (NULL != pdwOldValue) { DWORD dwType = 0; DWORD cbSize = sizeof(DWORD); if (ERROR_SUCCESS != RegQueryValueEx(hkey, pcszAllowEdit, NULL, &dwType, (LPBYTE)pdwOldValue, &cbSize)) { *pdwOldValue = 0; // Assume FALSE if the value doesn't exist } } // Set the new value if (ERROR_SUCCESS == RegSetValueEx(hkey, pcszAllowEdit, NULL, REG_DWORD, (CONST BYTE*) &dwNewValue, sizeof (DWORD))) { fValueWasSet = TRUE; } RegCloseKey(hkey); } return fValueWasSet; } // // The user is trying to advance from the user info tab in the Wizard. Therefore // we must take the information they have entered and: // // - log in using RAS (if ras is selected) // - try and locate a computer object // - if we find a computer object then allow them to use it // // If we failed to find a computer object, or the user found one and decided not // to use then we advance them to the 'computer info' page in the wizard. If // they decide to use it then we must apply it and advance to permissions. // void _DoUserInfoNext(HWND hwnd) { HRESULT hres; WCHAR szPassword[MAX_PASSWORD + 1]; BSTR bstrCompDomain = NULL; LONG idNextPage = -1; TCHAR szSearchDomain[MAX_DOMAIN + 1]; *szSearchDomain = 0; BOOL fTranslateNameTriedAndFailed = FALSE; // fSetAllowKey - Have we set the regval that says "allow connectiod creation before logon?" BOOL fSetAllowKey = FALSE; DWORD dwPreviousAllowValue = 0; // // read the user, domain and password from the dialog. then // lets search for the computer object that matches the currently // configure computer name. // FetchText(hwnd, IDC_USER, g_szUser, ARRAYSIZE(g_szUser)); FetchText(hwnd, IDC_DOMAIN, g_szDomain, ARRAYSIZE(g_szDomain)); FetchText(hwnd, IDC_PASSWORD, szPassword, ARRAYSIZE(szPassword)); // Handle possible UPN case if (StrChr(g_szUser, TEXT('@'))) { *g_szDomain = 0; } // // before we search for the computer object lets check to see if we should be using RAS // to get ourselves onto the network. // if ( IsDlgButtonChecked(hwnd, IDC_DIALUP) == BST_CHECKED ) { fSetAllowKey = SetAllowKey(1, &dwPreviousAllowValue); // Its ok to use globals here - we want to overwrite them. CREDINFO ci = { g_szUser, ARRAYSIZE(g_szUser), g_szDomain, ARRAYSIZE(g_szDomain), szPassword, ARRAYSIZE(szPassword) }; RASPBDLG info = { 0 }; info.dwSize = SIZEOF(info); info.hwndOwner = hwnd; info.dwFlags = RASPBDFLAG_NoUser; info.pCallback = _PhoneBkCB; info.dwCallbackId = (ULONG_PTR)&ci; if ( !RasPhonebookDlg(NULL, NULL, &info) ) { hres = E_FAIL; // failed to show the phone book goto exit_gracefully; } // Signal that the wizard has created a RAS connection. // Just to be extra paranoid, only do this if the wizard isn't a NETID wizard if (g_uWizardIs != NAW_NETID) { g_fCreatedConnection = TRUE; } SetDlgItemText(hwnd, IDC_USER, g_szUser); SetDlgItemText(hwnd, IDC_DOMAIN, g_szDomain); } // // now attempt to look up the computer object in the user domain. // if (StrChr(g_szUser, TEXT('@'))) { TCHAR szDomainUser[MAX_DOMAINUSER + 1]; ULONG ch = ARRAYSIZE(szDomainUser); if (TranslateName(g_szUser, NameUserPrincipal, NameSamCompatible, szDomainUser, &ch)) { TCHAR szUser[MAX_USER + 1]; DomainUserString_GetParts(szDomainUser, szUser, ARRAYSIZE(szUser), szSearchDomain, ARRAYSIZE(szSearchDomain)); } else { fTranslateNameTriedAndFailed = TRUE; } } if (0 == *szSearchDomain) lstrcpyn(szSearchDomain, g_szDomain, ARRAYSIZE(szSearchDomain)); hres = _FindComputerInDomain(g_szUser, g_szDomain, szSearchDomain, szPassword, &bstrCompDomain); switch ( hres ) { case S_OK: { StrCpy(g_szCompDomain, bstrCompDomain); // they want to change the domain // // we found an object in the DS that matches the current computer name // and domain. show the domain to the user before we join, allowing them // to confirm that this is what they want to do. // if ( IDYES == ShellMessageBox(g_hinst, hwnd, MAKEINTRESOURCE(IDS_ABOUTTOJOIN), MAKEINTRESOURCE(IDS_USERINFO), MB_YESNO|MB_ICONQUESTION, bstrCompDomain) ) { // // they don't want to modify the parameters so lets do the join. // idNextPage = IDD_PSW_ADDUSER; // Make local copies of the user/domain buffers since we don't want to modify globals TCHAR szUser[MAX_DOMAINUSER + 1]; lstrcpyn(szUser, g_szUser, ARRAYSIZE(szUser)); TCHAR szDomain[MAX_DOMAIN + 1]; lstrcpyn(szDomain, g_szDomain, ARRAYSIZE(szDomain)); CREDINFO ci = {szUser, ARRAYSIZE(szUser), szDomain, ARRAYSIZE(szDomain), szPassword, ARRAYSIZE(szPassword)}; if ( FAILED(JoinDomain(hwnd, TRUE, bstrCompDomain, &ci, &g_fRebootOnExit)) ) { idNextPage = -1; // don't advance they failed to join } } else { idNextPage = IDD_PSW_COMPINFO; } break; } case HRESULT_FROM_WIN32(ERROR_INVALID_DOMAINNAME): { // the domain was invalid, so we should really tell them ShellMessageBox(g_hinst, hwnd, MAKEINTRESOURCE(IDS_ERR_BADDOMAIN), MAKEINTRESOURCE(IDS_USERINFO), MB_OK|MB_ICONWARNING, g_szDomain); break; } case HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD): case HRESULT_FROM_WIN32(ERROR_LOGON_FAILURE): case HRESULT_FROM_WIN32(ERROR_BAD_USERNAME): { // this was a credentail failure, so lets tell the user they got something // wrong, and let them correct it. if (!fTranslateNameTriedAndFailed) { ShellMessageBox(g_hinst, hwnd, MAKEINTRESOURCE(IDS_ERR_BADPWUSER), MAKEINTRESOURCE(IDS_USERINFO), MB_OK|MB_ICONWARNING); break; } else { // Fall through... We tried to translate a UPN but we failed, so // we want to act as if we just failed to find a computer account goto default_label; } } default: { default_label: // failed to find a computer that matches the information we have, therefore // lets advance to the computer information page. StrCmp(g_szCompDomain, g_szDomain); idNextPage = IDD_PSW_COMPINFO; break; } } exit_gracefully: // Reset the "allow connectiod creation before login" value if appropriate if (fSetAllowKey) SetAllowKey(dwPreviousAllowValue, NULL); SysFreeString(bstrCompDomain); SetDlgItemText(hwnd, IDC_PASSWORD, L""); WIZARDNEXT(hwnd, idNextPage); } // // wizard page to handle the user information (name, password and domain); // BOOL _UserInfoBtnState(HWND hwnd) { DWORD dwButtons = PSWIZB_NEXT|PSWIZB_BACK; // the username/domain fields cannot be blank if ( !FetchTextLength(hwnd, IDC_USER) ) dwButtons &= ~PSWIZB_NEXT; if (IsWindowEnabled(GetDlgItem(hwnd, IDC_DOMAIN))) { if ( !FetchTextLength(hwnd, IDC_DOMAIN) ) dwButtons &= ~PSWIZB_NEXT; } SetWizardButtons(hwnd, dwButtons); return TRUE; } INT_PTR CALLBACK _UserInfoDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch ( uMsg ) { case WM_INITDIALOG: { Edit_LimitText(GetDlgItem(hwnd, IDC_USER), MAX_DOMAINUSER); Edit_LimitText(GetDlgItem(hwnd, IDC_PASSWORD), MAX_PASSWORD); Edit_LimitText(GetDlgItem(hwnd, IDC_DOMAIN), MAX_DOMAIN); // if we are launched from the netid tab then lets read the current // user and domain and display accordingly. if ( g_uWizardIs == NAW_NETID ) { DWORD dwcchUser = ARRAYSIZE(g_szUser); DWORD dwcchDomain = ARRAYSIZE(g_szDomain); GetCurrentUserAndDomainName(g_szUser, &dwcchUser, g_szDomain, &dwcchDomain); ShowWindow(GetDlgItem(hwnd, IDC_DIALUP), SW_HIDE); } SetDlgItemText(hwnd, IDC_USER, g_szUser); SetDlgItemText(hwnd, IDC_DOMAIN, g_szDomain); EnableDomainForUPN(GetDlgItem(hwnd, IDC_USER), GetDlgItem(hwnd, IDC_DOMAIN)); return TRUE; } case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: return _UserInfoBtnState(hwnd); case PSN_WIZBACK: WIZARDNEXT(hwnd, IDD_PSW_DOMAININFO); return TRUE; case PSN_WIZNEXT: _DoUserInfoNext(hwnd); // handles setting the next page etc return TRUE; } break; } case WM_COMMAND: { switch (HIWORD(wParam)) { case EN_CHANGE: if ((IDC_USER == LOWORD(wParam)) || (IDC_DOMAIN == LOWORD(wParam))) { EnableDomainForUPN(GetDlgItem(hwnd, IDC_USER), GetDlgItem(hwnd, IDC_DOMAIN)); _UserInfoBtnState(hwnd); } } break; } } return FALSE; } // modifying the computer name etc BOOL _IsTCPIPAvailable(void) { BOOL fTCPIPAvailable = FALSE; HKEY hk; DWORD dwSize = 0; // we check to see if the TCP/IP stack is installed and which object it is // bound to, this is a string, we don't check the value only that the // length is non-zero. if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\Tcpip\\Linkage"), 0x0, KEY_QUERY_VALUE, &hk) ) { if ( ERROR_SUCCESS == RegQueryValueEx(hk, TEXT("Export"), 0x0, NULL, NULL, &dwSize) ) { if ( dwSize > 2 ) { fTCPIPAvailable = TRUE; } } RegCloseKey(hk); } return (fTCPIPAvailable); } BOOL _ChangeMachineName(HWND hwnd, WCHAR* pszDomainUser, WCHAR* pszPassword) { BOOL fSuccess = FALSE; // the user has entered a short computer name (possibly a DNS host name), retrieve it WCHAR szNewShortMachineName[MAX_COMPUTERNAME + 1]; FetchText(hwnd, IDC_COMPUTERNAME, szNewShortMachineName, ARRAYSIZE(szNewShortMachineName)); // get the current short computer name WCHAR szOldShortMachineName[MAX_COMPUTERNAME + 1]; DWORD cchShort = ARRAYSIZE(szOldShortMachineName); BOOL fGotOldName = GetComputerName(szOldShortMachineName, &cchShort); if (fGotOldName) { // did the user change the short computer name? if (0 != StrCmpI(szOldShortMachineName, szNewShortMachineName)) { g_fMachineRenamed = TRUE; // if so we need to rename the machine in the domain. For this we need the NetBIOS computer name WCHAR szNewNetBIOSMachineName[MAX_COMPUTERNAME + 1]; // Get the netbios name from the short name DWORD cchNetbios = ARRAYSIZE(szNewNetBIOSMachineName); DnsHostnameToComputerName(szNewShortMachineName, szNewNetBIOSMachineName, &cchNetbios); // rename the computer in the domain NET_API_STATUS rename_status = ::NetRenameMachineInDomain(0, szNewNetBIOSMachineName, pszDomainUser, pszPassword, NETSETUP_ACCT_CREATE); // if the domain rename succeeded BOOL fDomainRenameSucceeded = (rename_status == ERROR_SUCCESS); if (fDomainRenameSucceeded) { // set the new short name locally BOOL fLocalRenameSucceeded; // do we have TCPIP? if (_IsTCPIPAvailable()) { // We can set the name using the short name fLocalRenameSucceeded = ::SetComputerNameEx(ComputerNamePhysicalDnsHostname, szNewShortMachineName); } else { // We need to set using the netbios name - kind of a hack fLocalRenameSucceeded = ::SetComputerNameEx(ComputerNamePhysicalNetBIOS, szNewNetBIOSMachineName); } fSuccess = fLocalRenameSucceeded; } // Handle errors that may have occured changing the name if (rename_status != ERROR_SUCCESS) { TCHAR szMessage[512]; switch (rename_status) { case NERR_UserExists: { // We don't really mean "user exists" in this case, we mean // "computer name exists", so load that reason string LoadString(g_hinst, IDS_COMPNAME_EXISTS, szMessage, ARRAYSIZE(szMessage)); } break; default: { if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) rename_status, 0, szMessage, ARRAYSIZE(szMessage), NULL)) LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage)); } break; } // Note that this is not a hard error, so we use the information icon ::DisplayFormatMessage(hwnd, IDS_ERR_CAPTION, IDS_NAW_NAMECHANGE_ERROR, MB_OK|MB_ICONINFORMATION, szMessage); } } else { // Computer name hasn't changed - just return success fSuccess = TRUE; } } return(fSuccess); } // handle processing the changes HRESULT _ChangeNameAndJoin(HWND hwnd) { WCHAR szDomain[MAX_DOMAIN + 1]; WCHAR szUser[MAX_DOMAINUSER + 1]; szUser[0] = 0; WCHAR szPassword[MAX_PASSWORD + 1]; szPassword[0] = 0; BOOL fNameChangeSucceeded = FALSE; FetchText(hwnd, IDC_DOMAIN, szDomain, ARRAYSIZE(szDomain)); // try to join the new domain TCHAR szUserDomain[MAX_DOMAIN + 1]; *szUserDomain = 0; CREDINFO ci = { szUser, ARRAYSIZE(szUser), szUserDomain, ARRAYSIZE(szUserDomain), szPassword, ARRAYSIZE(szPassword) }; HRESULT hres = JoinDomain(hwnd, TRUE, szDomain, &ci, &g_fRebootOnExit); if (SUCCEEDED(hres)) { #ifndef DONT_JOIN LPTSTR pszUser = szUser[0] ? szUser : NULL; LPTSTR pszPassword = szPassword[0] ?szPassword : NULL; fNameChangeSucceeded = _ChangeMachineName(hwnd, pszUser, pszPassword); #endif } return hres;; } // ensure the wizard buttons reflect what we can do BOOL _CompInfoBtnState(HWND hwnd) { DWORD dwButtons = PSWIZB_NEXT|PSWIZB_BACK; if ( !FetchTextLength(hwnd, IDC_COMPUTERNAME) ) dwButtons &= ~PSWIZB_NEXT; if ( !FetchTextLength(hwnd, IDC_DOMAIN) ) dwButtons &= ~PSWIZB_NEXT; SetWizardButtons(hwnd, dwButtons); return TRUE; } BOOL _ValidateMachineName(HWND hwnd) { BOOL fNameInUse = FALSE; NET_API_STATUS name_status = NERR_Success; // the user has entered a short computer name (possibly a DNS host name), retrieve it WCHAR szNewShortMachineName[MAX_COMPUTERNAME + 1]; FetchText(hwnd, IDC_COMPUTERNAME, szNewShortMachineName, ARRAYSIZE(szNewShortMachineName)); // get the current short computer name WCHAR szOldShortMachineName[MAX_COMPUTERNAME + 1]; DWORD cchShort = ARRAYSIZE(szOldShortMachineName); BOOL fGotOldName = GetComputerName(szOldShortMachineName, &cchShort); if (fGotOldName) { // did the user change the short computer name? if (0 != StrCmpI(szOldShortMachineName, szNewShortMachineName)) { // first we need to check the flat, netbios name WCHAR szNewNetBIOSMachineName[MAX_COMPUTERNAME + 1]; // Get the netbios name from the short name DWORD cchNetbios = ARRAYSIZE(szNewNetBIOSMachineName); DnsHostnameToComputerName(szNewShortMachineName, szNewNetBIOSMachineName, &cchNetbios); name_status = NetValidateName(NULL, szNewNetBIOSMachineName, NULL, NULL, NetSetupMachine); } } if (name_status != NERR_Success) { TCHAR szMessage[512]; if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) name_status, 0, szMessage, ARRAYSIZE(szMessage), NULL)) LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage)); ::DisplayFormatMessage(hwnd, IDS_ERR_CAPTION, IDS_MACHINENAMEINUSE, MB_ICONERROR | MB_OK, szMessage); } return (name_status == NERR_Success); } INT_PTR CALLBACK _CompInfoDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch ( uMsg ) { case WM_INITDIALOG: Edit_LimitText(GetDlgItem(hwnd, IDC_DOMAIN), MAX_DOMAIN); Edit_LimitText(GetDlgItem(hwnd, IDC_COMPUTERNAME), MAX_COMPUTERNAME); return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: { WCHAR szCompName[MAX_PATH + 1], szMessage[MAX_PATH+MAX_DOMAIN]; DWORD dwBuffer = ARRAYSIZE(szCompName); // fill in the user domain FormatMessageString(IDS_COMPNOTFOUND, szMessage, ARRAYSIZE(szMessage), g_szDomain); SetDlgItemText(hwnd, IDC_COMPINFO, szMessage); // default the computer name to something sensible GetComputerName(szCompName, &dwBuffer); SetDlgItemText(hwnd, IDC_COMPUTERNAME, szCompName); SetDlgItemText(hwnd, IDC_DOMAIN, g_szCompDomain); return _CompInfoBtnState(hwnd); } case PSN_WIZBACK: WIZARDNEXT(hwnd, IDD_PSW_USERINFO); return TRUE; case PSN_WIZNEXT: { INT idNextPage = -1; if (_ValidateMachineName(hwnd)) { if (SUCCEEDED(_ChangeNameAndJoin(hwnd))) { if (!g_fMachineRenamed) { idNextPage = IDD_PSW_ADDUSER; } else { idNextPage = IDD_PSW_DONE; } } } WIZARDNEXT(hwnd, idNextPage); return TRUE; } } break; } case WM_COMMAND: { if ( HIWORD(wParam) == EN_CHANGE ) return _CompInfoBtnState(hwnd); break; } } return FALSE; } // changing the group membership for the user, adds a domain user to a local group on the machine // eg. NET LOCALGROUP /ADD BOOL _AddUserToGroup(HWND hwnd, LPCTSTR pszLocalGroup, LPCWSTR pszUser, LPCWSTR pszDomain) { #ifndef DONT_JOIN BOOL fResult = FALSE; NET_API_STATUS nas; LOCALGROUP_MEMBERS_INFO_3 lgm; TCHAR szDomainUser[MAX_DOMAINUSER + 1]; CWaitCursor cur; MakeDomainUserString(pszDomain, pszUser, szDomainUser, ARRAYSIZE(szDomainUser)); lgm.lgrmi3_domainandname = szDomainUser; nas = NetLocalGroupAddMembers(NULL, pszLocalGroup, 3, (BYTE *)&lgm, 1); switch ( nas ) { // Success conditions case NERR_Success: case ERROR_MEMBER_IN_GROUP: case ERROR_MEMBER_IN_ALIAS: { fResult = TRUE; break; } case ERROR_INVALID_MEMBER: { DisplayFormatMessage(hwnd, IDS_PERMISSIONS, IDS_ERR_BADUSER, MB_OK|MB_ICONWARNING, pszUser, pszDomain); break; } case ERROR_NO_SUCH_MEMBER: { DisplayFormatMessage(hwnd, IDS_PERMISSIONS, IDS_ERR_NOSUCHUSER, MB_OK|MB_ICONWARNING, pszUser, pszDomain); break; } default: { TCHAR szMessage[512]; if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD) nas, 0, szMessage, ARRAYSIZE(szMessage), NULL)) LoadString(g_hinst, IDS_ERR_UNEXPECTED, szMessage, ARRAYSIZE(szMessage)); ::DisplayFormatMessage(hwnd, IDS_ERR_CAPTION, IDS_ERR_ADDUSER, MB_OK|MB_ICONERROR, szMessage); fResult = FALSE; break; } } return(fResult); #else return TRUE; #endif } // ensure the wizard buttons reflect what we can do BOOL _PermissionsBtnState(HWND hwnd) { // Next is always valid DWORD dwButtons = PSWIZB_NEXT | PSWIZB_BACK; SetWizardButtons(hwnd, dwButtons); return TRUE; } // BtnState function for _AddUserDlgProc BOOL _AddUserBtnState(HWND hwnd) { DWORD dwButtons = PSWIZB_NEXT|PSWIZB_BACK; BOOL fEnableEdits; if (BST_CHECKED == Button_GetCheck(GetDlgItem(hwnd, IDC_ADDUSER))) { // Enable the user and domain edits fEnableEdits = TRUE; if ( !FetchTextLength(hwnd, IDC_USER) ) dwButtons &= ~PSWIZB_NEXT; } else { // Disable user and domain edits fEnableEdits = FALSE; } EnableWindow(GetDlgItem(hwnd, IDC_USER), fEnableEdits); if (fEnableEdits) { EnableDomainForUPN(GetDlgItem(hwnd, IDC_USER), GetDlgItem(hwnd, IDC_DOMAIN)); } else { EnableWindow(GetDlgItem(hwnd, IDC_DOMAIN), FALSE); } EnableWindow(GetDlgItem(hwnd, IDC_USER_STATIC), fEnableEdits); EnableWindow(GetDlgItem(hwnd, IDC_DOMAIN_STATIC), fEnableEdits); SetWizardButtons(hwnd, dwButtons); return TRUE; } INT_PTR CALLBACK _AddUserDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch ( uMsg ) { case WM_INITDIALOG: Edit_LimitText(GetDlgItem(hwnd, IDC_USER), MAX_DOMAINUSER); Edit_LimitText(GetDlgItem(hwnd, IDC_DOMAIN), MAX_DOMAIN); Button_SetCheck(GetDlgItem(hwnd, IDC_ADDUSER), BST_CHECKED); return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: { SetDlgItemText(hwnd, IDC_USER, g_szUser); SetDlgItemText(hwnd, IDC_DOMAIN, g_szDomain); _AddUserBtnState(hwnd); return TRUE; } case PSN_WIZBACK: { if ( g_uWizardIs == NAW_PSDOMAINJOINED ) WIZARDNEXT(hwnd, IDD_PSW_WELCOME); else WIZARDNEXT(hwnd, IDD_PSW_USERINFO); return TRUE; } case PSN_WIZNEXT: { if (BST_CHECKED == Button_GetCheck(GetDlgItem(hwnd, IDC_ADDUSER))) { FetchText(hwnd, IDC_USER, g_szUser, ARRAYSIZE(g_szUser)); FetchText(hwnd, IDC_DOMAIN, g_szDomain, ARRAYSIZE(g_szDomain)); if (StrChr(g_szUser, TEXT('@'))) { *g_szDomain = 0; } WIZARDNEXT(hwnd, IDD_PSW_PERMISSIONS); } else { WIZARDNEXT(hwnd, IDD_PSW_DONE); } return TRUE; } } break; } case WM_COMMAND: { switch ( HIWORD(wParam) ) { case EN_CHANGE: case BN_CLICKED: _AddUserBtnState(hwnd); break; } break; } } return FALSE; } // // DlgProc for the permissions page. // INT_PTR CALLBACK _PermissionsDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { // Handle local-group related messages g_pGroupPageBase->HandleGroupMessage(hwnd, uMsg, wParam, lParam); switch ( uMsg ) { case WM_INITDIALOG: return TRUE; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: { // Set the "What level of access do you want to grant %S" message TCHAR szMessage[256]; TCHAR szDisplayName[MAX_DOMAINUSER]; // Make a domain/user string MakeDomainUserString(g_szDomain, g_szUser, szDisplayName, ARRAYSIZE(szDisplayName)); FormatMessageString(IDS_WHATACCESS_FORMAT, szMessage, ARRAYSIZE(szMessage), szDisplayName); SetDlgItemText(hwnd, IDC_WHATACCESS, szMessage); return _PermissionsBtnState(hwnd); } case PSN_WIZBACK: { WIZARDNEXT(hwnd, IDD_PSW_ADDUSER); return TRUE; } case PSN_WIZNEXT: { // Get the local group here! TODO TCHAR szGroup[MAX_GROUP + 1]; CUserInfo::GROUPPSEUDONYM gs; g_pGroupPageBase->GetSelectedGroup(hwnd, szGroup, ARRAYSIZE(szGroup), &gs); if ( !_AddUserToGroup(hwnd, szGroup, g_szUser, g_szDomain) ) { WIZARDNEXT(hwnd, -1); } else { SetDefAccount(g_szUser, g_szDomain); WIZARDNEXT(hwnd, IDD_PSW_DONE); } return TRUE; } } break; } case WM_COMMAND: { switch ( HIWORD(wParam) ) { case EN_CHANGE: return _PermissionsBtnState(hwnd); } break; } } return FALSE; } // pages that make up the wizard #define WIZDLG(name, dlgproc, dwFlags) \ { MAKEINTRESOURCE(IDD_PSW_##name##), dlgproc, MAKEINTRESOURCE(IDS_##name##), MAKEINTRESOURCE(IDS_##name##_SUB), dwFlags } WIZPAGE pages[] = { WIZDLG(WELCOME, _IntroDlgProc, PSP_HIDEHEADER), WIZDLG(HOWUSE, _HowUseDlgProc, 0), WIZDLG(WHICHNET, _WhichNetDlgProc, 0), WIZDLG(DOMAININFO, _DomainInfoDlgProc, 0), WIZDLG(USERINFO, _UserInfoDlgProc, 0), WIZDLG(COMPINFO, _CompInfoDlgProc, 0), WIZDLG(ADDUSER, _AddUserDlgProc, 0), WIZDLG(PERMISSIONS, _PermissionsDlgProc, 0), WIZDLG(WORKGROUP, _WorkgroupDlgProc, 0), WIZDLG(DONE, _DoneDlgProc, PSP_HIDEHEADER), }; STDAPI NetAccessWizard(HWND hwnd, UINT uType, BOOL *pfReboot) { // init comctrl INITCOMMONCONTROLSEX iccex = { 0 }; iccex.dwSize = sizeof (iccex); iccex.dwICC = ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&iccex); switch (uType) { case NAW_NETID: break; case NAW_PSDOMAINJOINFAILED: g_dwWhichNet = IDC_NONE; g_uWizardIs = uType; break; case NAW_PSDOMAINJOINED: g_dwWhichNet = IDC_DOMAIN; g_uWizardIs = uType; break; default: return E_INVALIDARG; } // create the pages HPROPSHEETPAGE rghpage[ARRAYSIZE(pages)]; INT cPages = 0; for (cPages = 0 ; cPages < ARRAYSIZE(pages) ; cPages++) { PROPSHEETPAGE psp = { 0 }; WCHAR szBuffer[MAX_PATH] = { 0 }; psp.dwSize = SIZEOF(PROPSHEETPAGE); psp.hInstance = g_hinst; psp.lParam = cPages; psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE | pages[cPages].dwFlags; psp.pszTemplate = pages[cPages].idPage; psp.pfnDlgProc = pages[cPages].pDlgProc; psp.pszTitle = MAKEINTRESOURCE(IDS_NETWIZCAPTION); psp.pszHeaderTitle = pages[cPages].pHeading; psp.pszHeaderSubTitle = pages[cPages].pSubHeading; rghpage[cPages] = CreatePropertySheetPage(&psp); } // display the wizard PROPSHEETHEADER psh = { 0 }; psh.dwSize = SIZEOF(PROPSHEETHEADER); psh.hwndParent = hwnd; psh.hInstance = g_hinst; psh.dwFlags = PSH_WIZARD | PSH_WIZARD97 | PSH_WATERMARK | PSH_STRETCHWATERMARK | PSH_HEADER | PSH_USECALLBACK; psh.pszbmHeader = MAKEINTRESOURCE(IDB_PSW_BANNER); psh.pszbmWatermark = MAKEINTRESOURCE(IDB_PSW_WATERMARK); psh.nPages = cPages; psh.phpage = rghpage; psh.pfnCallback = _PropSheetCB; // Create the global CGroupPageBase object if necessary CGroupInfoList grouplist; if (SUCCEEDED(grouplist.Initialize())) { g_pGroupPageBase = new CGroupPageBase(NULL, &grouplist); if (NULL != g_pGroupPageBase) { PropertySheetIcon(&psh, MAKEINTRESOURCE(IDI_PSW)); delete g_pGroupPageBase; } } // // Hang up the all RAS connections if the wizard created one. It is assumed that no non-wizard connections will // exist at this time. 90% of the time, they've just changed their domain membership anyway to they will // be just about to reboot. Hanging up all connections MAY cause trouble if: There were existing connections // before the pre-logon wizard started AND the user cancelled after making connections with the wizard but before // changing their domain. There are no situations where this currently happens. // if (g_fCreatedConnection) { RASCONN* prgrasconn = (RASCONN*) LocalAlloc(0, sizeof(RASCONN)); if (NULL != prgrasconn) { prgrasconn[0].dwSize = sizeof(RASCONN); DWORD cb = sizeof(RASCONN); DWORD nConn = 0; DWORD dwSuccess = RasEnumConnections(prgrasconn, &cb, &nConn); if (ERROR_BUFFER_TOO_SMALL == dwSuccess) { LocalFree(prgrasconn); prgrasconn = (RASCONN*) LocalAlloc(0, cb); if (NULL != prgrasconn) { prgrasconn[0].dwSize = sizeof(RASCONN); dwSuccess = RasEnumConnections(prgrasconn, &cb, &nConn); } } if (0 == dwSuccess) { // Make sure we have one and only one connection before hanging up for (DWORD i = 0; i < nConn; i ++) { RasHangUp(prgrasconn[i].hrasconn); } } LocalFree(prgrasconn); } } // // restart the machine if we need to, eg: the domain changed // if (pfReboot) *pfReboot = g_fRebootOnExit; // // if this is coming from setup, then lets display the message // if (g_fRebootOnExit && !g_fShownLastPage && (g_uWizardIs != NAW_NETID)) { ShellMessageBox(g_hinst, hwnd, MAKEINTRESOURCE(IDS_RESTARTREQUIRED), MAKEINTRESOURCE(IDS_NETWIZCAPTION), MB_OK); } return S_OK; }