windows-nt/Source/XPSP1/NT/shell/ext/netplwiz/netwiz.cpp

1694 lines
52 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "stdafx.h"
#include "grpinfo.h"
#include <dsgetdc.h> // DsGetDCName and DS structures
#include <ntdsapi.h>
#include <activeds.h> // ADsGetObject
#include <rasdlg.h>
#include <raserror.h>
#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;
}