You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2139 lines
67 KiB
2139 lines
67 KiB
#include "stdafx.h"
|
|
#include "grpinfo.h"
|
|
#include "unpage.h"
|
|
#include "netpage.h"
|
|
#include "password.h"
|
|
#include "cryptui.h" // for certificate mgr
|
|
#pragma hdrstop
|
|
|
|
|
|
// Certificate Manager helper static functions and delay-load stuff
|
|
class CCertificateAPI
|
|
{
|
|
public:
|
|
static BOOL ManagePasswords(HWND hwnd);
|
|
static BOOL Wizard(HWND hwnd);
|
|
|
|
private:
|
|
static BOOL m_fFailed;
|
|
static HINSTANCE m_hInstCryptUI;
|
|
};
|
|
|
|
BOOL CCertificateAPI::m_fFailed = FALSE;
|
|
HINSTANCE CCertificateAPI::m_hInstCryptUI = NULL;
|
|
|
|
|
|
// CCertificateAPI::ManagePasswords - launch certificate manager
|
|
typedef BOOL (WINAPI *PFNCRYPTUIDLGCERTMGR)(IN PCCRYPTUI_CERT_MGR_STRUCT pCryptUICertMgr);
|
|
BOOL CCertificateAPI::ManagePasswords(HWND hwnd)
|
|
{
|
|
// Use shellexecuteex to open up the keyring control panel
|
|
SHELLEXECUTEINFO shexinfo = {0};
|
|
shexinfo.cbSize = sizeof (shexinfo);
|
|
shexinfo.fMask = SEE_MASK_DOENVSUBST;
|
|
shexinfo.nShow = SW_SHOWNORMAL;
|
|
shexinfo.lpFile = L"%windir%\\system32\\rundll32.exe";
|
|
shexinfo.lpParameters = L"shell32.dll,Control_RunDLL keymgr.dll";
|
|
shexinfo.lpVerb = TEXT("open");
|
|
|
|
return ShellExecuteEx(&shexinfo);
|
|
}
|
|
|
|
|
|
// CCertificateAPI::Wizard - launch the enrollment wizard
|
|
|
|
typedef BOOL (WINAPI *PFNCRYPTUIWIZCERTREQUEST)(IN DWORD dwFlags,
|
|
IN OPTIONAL HWND, IN OPTIONAL LPCWSTR pwszWizardTitle,
|
|
IN PCCRYPTUI_WIZ_CERT_REQUEST_INFO pCertRequestInfo,
|
|
OUT OPTIONAL PCCERT_CONTEXT *ppCertContext,
|
|
OUT OPTIONAL DWORD *pCAdwStatus);
|
|
|
|
BOOL CCertificateAPI::Wizard(HWND hwnd)
|
|
{
|
|
static PFNCRYPTUIWIZCERTREQUEST pCryptUIWizCertRequest = NULL;
|
|
|
|
if ((m_hInstCryptUI == NULL) && (!m_fFailed))
|
|
{
|
|
m_hInstCryptUI = LoadLibrary(TEXT("cryptui.dll"));
|
|
}
|
|
|
|
if (m_hInstCryptUI != NULL)
|
|
{
|
|
pCryptUIWizCertRequest = (PFNCRYPTUIWIZCERTREQUEST)
|
|
GetProcAddress(m_hInstCryptUI, "CryptUIWizCertRequest");
|
|
}
|
|
|
|
if (pCryptUIWizCertRequest)
|
|
{
|
|
CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW CertRequestPvkNew = {0};
|
|
CertRequestPvkNew.dwSize=sizeof(CRYPTUI_WIZ_CERT_REQUEST_PVK_NEW);
|
|
|
|
CRYPTUI_WIZ_CERT_REQUEST_INFO CertRequestInfo = {0};
|
|
CertRequestInfo.dwSize=sizeof(CRYPTUI_WIZ_CERT_REQUEST_INFO);
|
|
CertRequestInfo.dwPurpose=CRYPTUI_WIZ_CERT_ENROLL;
|
|
CertRequestInfo.dwPvkChoice=CRYPTUI_WIZ_CERT_REQUEST_PVK_CHOICE_NEW;
|
|
CertRequestInfo.pPvkNew=&CertRequestPvkNew;
|
|
|
|
// This can take a while!
|
|
CWaitCursor cur;
|
|
pCryptUIWizCertRequest(0, hwnd, NULL, &CertRequestInfo, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
m_fFailed = TRUE;
|
|
}
|
|
|
|
return (!m_fFailed);
|
|
}
|
|
|
|
|
|
// handle auto logon of users
|
|
|
|
class CAutologonUserDlg: public CDialog
|
|
{
|
|
public:
|
|
CAutologonUserDlg(LPTSTR szInitialUser)
|
|
{m_pszUsername = szInitialUser;}
|
|
|
|
private:
|
|
virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
|
|
LPTSTR m_pszUsername;
|
|
};
|
|
|
|
|
|
INT_PTR CAutologonUserDlg::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog);
|
|
HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CAutologonUserDlg::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
HWND hwndUsername = GetDlgItem(hwnd, IDC_USER);
|
|
HWND hwndPassword = GetDlgItem(hwnd, IDC_PASSWORD);
|
|
HWND hwndConfirm = GetDlgItem(hwnd, IDC_CONFIRMPASSWORD);
|
|
|
|
// limit the text with of the controls
|
|
Edit_LimitText(hwndUsername, MAX_USER);
|
|
Edit_LimitText(hwndPassword, MAX_PASSWORD);
|
|
Edit_LimitText(hwndConfirm, MAX_PASSWORD);
|
|
|
|
// Populate the username field and set focus to password
|
|
SetWindowText(hwndUsername, m_pszUsername);
|
|
SetFocus(hwndPassword);
|
|
BOOL fSetDefaultFocus = FALSE;
|
|
|
|
return (fSetDefaultFocus);
|
|
}
|
|
|
|
BOOL CAutologonUserDlg::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case IDOK:
|
|
{
|
|
TCHAR szUsername[MAX_USER + 1];
|
|
TCHAR szPassword[MAX_PASSWORD + 1];
|
|
TCHAR szConfirm[MAX_PASSWORD + 1];
|
|
|
|
FetchText(hwnd, IDC_USER, szUsername, ARRAYSIZE(szUsername));
|
|
GetWindowText(GetDlgItem(hwnd, IDC_PASSWORD), szPassword, ARRAYSIZE(szPassword));
|
|
GetWindowText(GetDlgItem(hwnd, IDC_CONFIRMPASSWORD), szConfirm, ARRAYSIZE(szConfirm));
|
|
|
|
if (StrCmp(szConfirm, szPassword) != 0)
|
|
{
|
|
// Display a message saying the passwords don't match
|
|
DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_ERR_PWDNOMATCH, MB_OK | MB_ICONERROR);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Actually apply the autologon
|
|
SetAutoLogon(szUsername, szPassword);
|
|
SecureZeroMemory(szPassword, ARRAYSIZE(szPassword));
|
|
}
|
|
}
|
|
|
|
// Fall through
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, id);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
// user list page (the main page the user sees)
|
|
|
|
class CUserlistPropertyPage: public CPropertyPage
|
|
{
|
|
public:
|
|
CUserlistPropertyPage(CUserManagerData* pdata): m_pData(pdata)
|
|
{m_himlLarge = NULL;}
|
|
~CUserlistPropertyPage();
|
|
|
|
static HRESULT AddUserToListView(HWND hwndList, CUserInfo* pUserInfo, BOOL fSelectUser = FALSE);
|
|
|
|
private:
|
|
virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
BOOL OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh);
|
|
BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL OnGetInfoTip(HWND hwndList, LPNMLVGETINFOTIP pGetInfoTip);
|
|
BOOL OnListViewItemChanged(HWND hwnd);
|
|
BOOL OnListViewDeleteItem(HWND hwndList, int iItem);
|
|
BOOL OnHelp(HWND hwnd, LPHELPINFO pHelpInfo);
|
|
BOOL OnContextMenu(HWND hwnd);
|
|
BOOL OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg);
|
|
long OnApply(HWND hwnd);
|
|
HRESULT InitializeListView(HWND hwndList, BOOL fShowDomain);
|
|
HRESULT LaunchNewUserWizard(HWND hwndParent);
|
|
HRESULT LaunchAddNetUserWizard(HWND hwndParent);
|
|
HRESULT LaunchUserProperties(HWND hwndParent);
|
|
HRESULT LaunchSetPasswordDialog(HWND hwndParent);
|
|
CUserInfo* GetSelectedUserInfo(HWND hwndList);
|
|
void OnRemove(HWND hwnd);
|
|
int ConfirmRemove(HWND hwnd, CUserInfo* pUserInfo);
|
|
void RemoveSelectedUserFromList(HWND hwndList, BOOL fFreeUserInfo);
|
|
void SetPageState(HWND hwnd);
|
|
HRESULT SetAutologonState(HWND hwnd, BOOL fAutologon);
|
|
void SetupList(HWND hwnd);
|
|
HPSXA AddExtraUserPropPages(ADDPROPSHEETDATA* ppsd, PSID psid);
|
|
|
|
static int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2,
|
|
LPARAM lParamSort);
|
|
|
|
CUserManagerData* m_pData;
|
|
HIMAGELIST m_himlLarge;
|
|
|
|
// When a column header is clicked, the list is sorted based on that column
|
|
// When this happens, we store the column number here so that if the same
|
|
// column is clicked again, we sort it in reverse. A 0 is stored if no
|
|
// column should be sorted in reverse when clicked.
|
|
int m_iReverseColumnIfSelected;
|
|
|
|
BOOL m_fAutologonCheckChanged;
|
|
};
|
|
|
|
|
|
// Help ID array
|
|
static const DWORD rgUserListHelpIds[] =
|
|
{
|
|
IDC_AUTOLOGON_CHECK, IDH_AUTOLOGON_CHECK,
|
|
IDC_LISTTITLE_STATIC, IDH_USER_LIST,
|
|
IDC_USER_LIST, IDH_USER_LIST,
|
|
IDC_ADDUSER_BUTTON, IDH_ADDUSER_BUTTON,
|
|
IDC_REMOVEUSER_BUTTON, IDH_REMOVEUSER_BUTTON,
|
|
IDC_USERPROPERTIES_BUTTON, IDH_USERPROPERTIES_BUTTON,
|
|
IDC_PASSWORD_STATIC, IDH_PASSWORD_BUTTON,
|
|
IDC_CURRENTUSER_ICON, IDH_PASSWORD_BUTTON,
|
|
IDC_PASSWORD_BUTTON, IDH_PASSWORD_BUTTON,
|
|
IDC_PWGROUP_STATIC, (DWORD) -1,
|
|
IDC_ULISTPG_TEXT, (DWORD) -1,
|
|
IDC_USERLISTPAGE_ICON, (DWORD) -1,
|
|
0, 0
|
|
};
|
|
|
|
// Control ID arrays for enabling/disabling/moving
|
|
static const UINT rgidDisableOnAutologon[] =
|
|
{
|
|
IDC_USER_LIST,
|
|
IDC_ADDUSER_BUTTON,
|
|
IDC_REMOVEUSER_BUTTON,
|
|
IDC_USERPROPERTIES_BUTTON,
|
|
IDC_PASSWORD_BUTTON
|
|
};
|
|
|
|
static const UINT rgidDisableOnNoSelection[] =
|
|
{
|
|
IDC_REMOVEUSER_BUTTON,
|
|
IDC_USERPROPERTIES_BUTTON,
|
|
IDC_PASSWORD_BUTTON
|
|
};
|
|
|
|
static const UINT rgidMoveOnNoAutologonCheck[] =
|
|
{
|
|
IDC_LISTTITLE_STATIC,
|
|
IDC_USER_LIST,
|
|
};
|
|
|
|
CUserlistPropertyPage::~CUserlistPropertyPage()
|
|
{
|
|
if (m_himlLarge != NULL)
|
|
ImageList_Destroy(m_himlLarge);
|
|
}
|
|
|
|
INT_PTR CUserlistPropertyPage::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog);
|
|
HANDLE_MSG(hwndDlg, WM_NOTIFY, OnNotify);
|
|
HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hwndDlg, WM_SETCURSOR, OnSetCursor);
|
|
|
|
case WM_HELP:
|
|
return OnHelp(hwndDlg, (LPHELPINFO) lParam);
|
|
|
|
case WM_CONTEXTMENU:
|
|
return OnContextMenu((HWND) wParam);
|
|
|
|
case WM_ADDUSERTOLIST:
|
|
return SUCCEEDED(AddUserToListView(GetDlgItem(hwndDlg, IDC_USER_LIST), (CUserInfo*)lParam, (BOOL) wParam));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnHelp(HWND hwnd, LPHELPINFO pHelpInfo)
|
|
{
|
|
WinHelp((HWND) pHelpInfo->hItemHandle, m_pData->GetHelpfilePath(),
|
|
HELP_WM_HELP, (ULONG_PTR) (LPTSTR)rgUserListHelpIds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnContextMenu(HWND hwnd)
|
|
{
|
|
WinHelp(hwnd, m_pData->GetHelpfilePath(),
|
|
HELP_CONTEXTMENU, (ULONG_PTR) (LPTSTR)rgUserListHelpIds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST);
|
|
InitializeListView(hwndList, m_pData->IsComputerInDomain());
|
|
m_pData->Initialize(hwnd);
|
|
|
|
SetupList(hwnd);
|
|
|
|
m_fAutologonCheckChanged = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnListViewDeleteItem(HWND hwndList, int iItem)
|
|
{
|
|
LVITEM lvi = {0};
|
|
lvi.iItem = iItem;
|
|
lvi.mask = LVIF_PARAM;
|
|
ListView_GetItem(hwndList, &lvi);
|
|
|
|
CUserInfo* pUserInfo = (CUserInfo*)lvi.lParam;
|
|
if (NULL != pUserInfo)
|
|
{
|
|
delete pUserInfo;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh)
|
|
{
|
|
switch (pnmh->code)
|
|
{
|
|
case PSN_APPLY:
|
|
{
|
|
long applyEffect = OnApply(hwnd);
|
|
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, applyEffect);
|
|
break;
|
|
}
|
|
|
|
case LVN_GETINFOTIP:
|
|
return OnGetInfoTip(pnmh->hwndFrom, (LPNMLVGETINFOTIP) pnmh);
|
|
break;
|
|
|
|
case LVN_ITEMCHANGED:
|
|
return OnListViewItemChanged(hwnd);
|
|
break;
|
|
|
|
case LVN_DELETEITEM:
|
|
return OnListViewDeleteItem(GetDlgItem(hwnd, IDC_USER_LIST), ((LPNMLISTVIEW) pnmh)->iItem);
|
|
|
|
case NM_DBLCLK:
|
|
LaunchUserProperties(hwnd);
|
|
return TRUE;
|
|
|
|
case LVN_COLUMNCLICK:
|
|
{
|
|
int iColumn = ((LPNMLISTVIEW) pnmh)->iSubItem;
|
|
|
|
// Want to work with 1-based columns so we can use zero as
|
|
// a special value
|
|
iColumn += 1;
|
|
|
|
// If we aren't showing the domain column because we're in
|
|
// non-domain mode, then map column 2 (group since we're not in
|
|
// domain mode to column 3 since the callback always expects
|
|
// the columns to be, "username", "domain", "group".
|
|
if ((iColumn == 2) && (!m_pData->IsComputerInDomain()))
|
|
{
|
|
iColumn = 3;
|
|
}
|
|
|
|
if (m_iReverseColumnIfSelected == iColumn)
|
|
{
|
|
m_iReverseColumnIfSelected = 0;
|
|
iColumn = -iColumn;
|
|
}
|
|
else
|
|
{
|
|
m_iReverseColumnIfSelected = iColumn;
|
|
}
|
|
|
|
ListView_SortItems(pnmh->hwndFrom, ListCompare, (LPARAM) iColumn);
|
|
return TRUE;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case IDC_ADDUSER_BUTTON:
|
|
if (m_pData->IsComputerInDomain())
|
|
{
|
|
// Launch the wizard to add a network user to a local group
|
|
LaunchAddNetUserWizard(hwnd);
|
|
}
|
|
else
|
|
{
|
|
// No domain; create a new local machine user
|
|
LaunchNewUserWizard(hwnd);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_REMOVEUSER_BUTTON:
|
|
OnRemove(hwnd);
|
|
return TRUE;
|
|
|
|
case IDC_AUTOLOGON_CHECK:
|
|
{
|
|
m_fAutologonCheckChanged = TRUE;
|
|
BOOL fAutoLogon = (BST_UNCHECKED == SendMessage(GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK), BM_GETCHECK, 0, 0));
|
|
SetAutologonState(hwnd, fAutoLogon);
|
|
SetPageState(hwnd);
|
|
break;
|
|
}
|
|
|
|
case IDC_ADVANCED_BUTTON:
|
|
{
|
|
static const TCHAR szMMCCommandLineFormat[] = TEXT("mmc.exe /computer=%s %%systemroot%%\\system32\\lusrmgr.msc");
|
|
|
|
TCHAR szMMCCommandLine[MAX_PATH];
|
|
TCHAR szExpandedCommandLine[MAX_PATH];
|
|
|
|
wnsprintf(szMMCCommandLine, ARRAYSIZE(szMMCCommandLine), szMMCCommandLineFormat, m_pData->GetComputerName());
|
|
if (ExpandEnvironmentStrings(szMMCCommandLine, szExpandedCommandLine, ARRAYSIZE(szExpandedCommandLine)) > 0)
|
|
{
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si = {0};
|
|
si.cb = sizeof (si);
|
|
if (CreateProcess(NULL, szExpandedCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
|
|
{
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_PASSWORD_BUTTON:
|
|
LaunchSetPasswordDialog(hwnd);
|
|
break;
|
|
|
|
case IDC_USERPROPERTIES_BUTTON:
|
|
LaunchUserProperties(hwnd);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
|
|
if (m_pData->GetUserListLoader()->InitInProgress())
|
|
{
|
|
// If the thread is filling, handle by setting the appstarting cursor
|
|
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
|
|
fHandled = TRUE;
|
|
}
|
|
|
|
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, fHandled);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL CUserlistPropertyPage::OnGetInfoTip(HWND hwndList, LPNMLVGETINFOTIP pGetInfoTip)
|
|
{
|
|
// Get the UserInfo structure for the selected item
|
|
LVITEM lvi;
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = pGetInfoTip->iItem;
|
|
lvi.iSubItem = 0;
|
|
|
|
if ((lvi.iItem >= 0) && (ListView_GetItem(hwndList, &lvi)))
|
|
{
|
|
// Ensure full name and comment are available
|
|
CUserInfo* pUserInfo = (CUserInfo*)lvi.lParam;
|
|
pUserInfo->GetExtraUserInfo();
|
|
|
|
// Make a string containing our "Full Name: %s\nComment: %s" message
|
|
if ((pUserInfo->m_szFullName[0] != TEXT('\0')) &&
|
|
(pUserInfo->m_szComment[0] != TEXT('\0')))
|
|
{
|
|
// We have a full name and comment
|
|
FormatMessageString(IDS_USR_TOOLTIPBOTH_FORMAT, pGetInfoTip->pszText, pGetInfoTip->cchTextMax, pUserInfo->m_szFullName, pUserInfo->m_szComment);
|
|
}
|
|
else if (pUserInfo->m_szFullName[0] != TEXT('\0'))
|
|
{
|
|
// We only have full name
|
|
FormatMessageString(IDS_USR_TOOLTIPFULLNAME_FORMAT, pGetInfoTip->pszText, pGetInfoTip->cchTextMax, pUserInfo->m_szFullName);
|
|
}
|
|
else if (pUserInfo->m_szComment[0] != TEXT('\0'))
|
|
{
|
|
// We only have comment
|
|
FormatMessageString(IDS_USR_TOOLTIPCOMMENT_FORMAT, pGetInfoTip->pszText, pGetInfoTip->cchTextMax, pUserInfo->m_szComment);
|
|
}
|
|
else
|
|
{
|
|
// We have no extra information - do nothing (show no tip)
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
struct MYCOLINFO
|
|
{
|
|
int percentWidth;
|
|
UINT idString;
|
|
};
|
|
|
|
HRESULT CUserlistPropertyPage::InitializeListView(HWND hwndList, BOOL fShowDomain)
|
|
{
|
|
// Array of icon ids icons 0, 1, and 2 respectively
|
|
static const UINT rgIcons[] =
|
|
{
|
|
IDI_USR_LOCALUSER_ICON,
|
|
IDI_USR_DOMAINUSER_ICON,
|
|
IDI_USR_GROUP_ICON
|
|
};
|
|
|
|
// Array of relative column widths, for columns 0, 1, and 2 respectively
|
|
static const MYCOLINFO rgColWidthsWithDomain[] =
|
|
{
|
|
{40, IDS_USR_NAME_COLUMN},
|
|
{30, IDS_USR_DOMAIN_COLUMN},
|
|
{30, IDS_USR_GROUP_COLUMN}
|
|
};
|
|
|
|
static const MYCOLINFO rgColWidthsNoDomain[] =
|
|
{
|
|
{50, IDS_USR_NAME_COLUMN},
|
|
{50, IDS_USR_GROUP_COLUMN}
|
|
};
|
|
|
|
// Create a listview with three columns
|
|
RECT rect;
|
|
GetClientRect(hwndList, &rect);
|
|
|
|
// Width of our window minus width of a verticle scroll bar minus one for the
|
|
// little bevel at the far right of the header.
|
|
int cxListView = (rect.right - rect.left) - GetSystemMetrics(SM_CXVSCROLL) - 1;
|
|
|
|
// Make our columns
|
|
int i;
|
|
int nColumns;
|
|
const MYCOLINFO* pColInfo;
|
|
if (fShowDomain)
|
|
{
|
|
nColumns = ARRAYSIZE(rgColWidthsWithDomain);
|
|
pColInfo = rgColWidthsWithDomain;
|
|
}
|
|
else
|
|
{
|
|
nColumns = ARRAYSIZE(rgColWidthsNoDomain);
|
|
pColInfo = rgColWidthsNoDomain;
|
|
}
|
|
|
|
LVCOLUMN lvc;
|
|
lvc.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
|
|
for (i = 0; i < nColumns; i++)
|
|
{
|
|
TCHAR szText[MAX_PATH];
|
|
// Load this column's caption
|
|
LoadString(g_hinst, pColInfo[i].idString, szText, ARRAYSIZE(szText));
|
|
|
|
lvc.iSubItem = i;
|
|
lvc.cx = (int) MulDiv(pColInfo[i].percentWidth, cxListView, 100);
|
|
lvc.pszText = szText;
|
|
|
|
ListView_InsertColumn(hwndList, i, &lvc);
|
|
}
|
|
|
|
UINT flags = ILC_MASK;
|
|
if(IS_WINDOW_RTL_MIRRORED(hwndList))
|
|
{
|
|
flags |= ILC_MIRROR;
|
|
}
|
|
|
|
// Create an image list for the listview
|
|
HIMAGELIST himlSmall = ImageList_Create(16, 16, flags, 0, ARRAYSIZE(rgIcons));
|
|
|
|
// Large image lists for the "set password" group icon
|
|
m_himlLarge = ImageList_Create(32, 32, flags, 0, ARRAYSIZE(rgIcons));
|
|
if (himlSmall && m_himlLarge)
|
|
{
|
|
// Add our icons to the image list
|
|
for(i = 0; i < ARRAYSIZE(rgIcons); i ++)
|
|
{
|
|
HICON hIconSmall = (HICON) LoadImage(g_hinst, MAKEINTRESOURCE(rgIcons[i]), IMAGE_ICON, 16, 16, 0);
|
|
if (hIconSmall)
|
|
{
|
|
ImageList_AddIcon(himlSmall, hIconSmall);
|
|
DestroyIcon(hIconSmall);
|
|
}
|
|
|
|
HICON hIconLarge = (HICON) LoadImage(g_hinst, MAKEINTRESOURCE(rgIcons[i]), IMAGE_ICON, 32, 32, 0);
|
|
if (hIconLarge)
|
|
{
|
|
ImageList_AddIcon(m_himlLarge, hIconLarge);
|
|
DestroyIcon(hIconLarge);
|
|
}
|
|
}
|
|
}
|
|
|
|
ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL);
|
|
|
|
// Set extended styles for the listview
|
|
ListView_SetExtendedListViewStyleEx(hwndList,
|
|
LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP,
|
|
LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
|
|
|
|
// Set some settings for our tooltips - stolen from defview.cpp code
|
|
HWND hwndInfoTip = ListView_GetToolTips(hwndList);
|
|
if (hwndInfoTip != NULL)
|
|
{
|
|
//make the tooltip window to be topmost window
|
|
SetWindowPos(hwndInfoTip, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
|
|
// increase the ShowTime (the delay before we show the tooltip) to 2 times the default value
|
|
LRESULT uiShowTime = SendMessage(hwndInfoTip, TTM_GETDELAYTIME, TTDT_INITIAL, 0);
|
|
SendMessage(hwndInfoTip, TTM_SETDELAYTIME, TTDT_INITIAL, uiShowTime * 2);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserlistPropertyPage::AddUserToListView(HWND hwndList,
|
|
CUserInfo* pUserInfo,
|
|
BOOL fSelectUser /* = FALSE */)
|
|
{
|
|
if (!pUserInfo->m_fAccountDisabled)
|
|
{
|
|
LVITEM lvi = { 0 };
|
|
|
|
lvi.iItem = 0;
|
|
lvi.iSubItem = 0;
|
|
lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
|
|
lvi.pszText = pUserInfo->m_szUsername;
|
|
lvi.iImage = pUserInfo->m_userType;
|
|
lvi.lParam = (LPARAM) pUserInfo;
|
|
|
|
// Always select the first loaded user
|
|
if (ListView_GetItemCount(hwndList) == 0)
|
|
fSelectUser = TRUE;
|
|
|
|
if (fSelectUser)
|
|
{
|
|
lvi.mask |= LVIF_STATE;
|
|
lvi.state = lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
|
|
}
|
|
|
|
int iItem = ListView_InsertItem(hwndList, &lvi);
|
|
if (iItem >= 0)
|
|
{
|
|
if (fSelectUser)
|
|
ListView_EnsureVisible(hwndList, iItem, FALSE); // Make the item visible
|
|
|
|
// Success! Now add the subitems (domain, groups)
|
|
lvi.iItem = iItem;
|
|
lvi.mask = LVIF_TEXT;
|
|
|
|
// Only add the domain field if the user is in a domain
|
|
if (::IsComputerInDomain())
|
|
{
|
|
lvi.iSubItem = 1;
|
|
lvi.pszText = pUserInfo->m_szDomain;
|
|
ListView_SetItem(hwndList, &lvi);
|
|
|
|
// User is in a domain; group should be third column
|
|
lvi.iSubItem = 2;
|
|
}
|
|
else
|
|
{
|
|
// User isn't in a domain, group should be second column
|
|
lvi.iSubItem = 1;
|
|
}
|
|
|
|
// Add group regardless of whether user is in a domain
|
|
lvi.pszText = pUserInfo->m_szGroups;
|
|
ListView_SetItem(hwndList, &lvi);
|
|
}
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
// Do we leak the pUserInfo for disabled user accounts?
|
|
}
|
|
*/
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserlistPropertyPage::LaunchNewUserWizard(HWND hwndParent)
|
|
{
|
|
static const int nPages = 3;
|
|
int cPages = 0;
|
|
HPROPSHEETPAGE rghPages[nPages];
|
|
|
|
// Create a new user record
|
|
CUserInfo* pNewUser = new CUserInfo;
|
|
if ( !pNewUser )
|
|
{
|
|
DisplayFormatMessage(hwndParent, IDS_USR_NEWUSERWIZARD_CAPTION, IDS_USR_CREATE_MISC_ERROR, MB_OK | MB_ICONERROR);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pNewUser->InitializeForNewUser();
|
|
pNewUser->m_userType = CUserInfo::LOCALUSER;
|
|
|
|
PROPSHEETPAGE psp = {0};
|
|
// Common propsheetpage settings
|
|
psp.dwSize = sizeof (psp);
|
|
psp.hInstance = g_hinst;
|
|
psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
|
|
|
|
// Page 1: Username entry page
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERNAME_WIZARD_PAGE);
|
|
CUsernameWizardPage page1(pNewUser);
|
|
page1.SetPropSheetPageMembers(&psp);
|
|
rghPages[cPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
// Page 2: Password page
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_PASSWORD_WIZARD_PAGE);
|
|
CPasswordWizardPage page2(pNewUser);
|
|
page2.SetPropSheetPageMembers(&psp);
|
|
rghPages[cPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
// Page 3: Local group addition
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_WIZARD_PAGE);
|
|
CGroupWizardPage page3(pNewUser, m_pData->GetGroupList());
|
|
page3.SetPropSheetPageMembers(&psp);
|
|
rghPages[cPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
PROPSHEETHEADER psh = {0};
|
|
psh.dwSize = sizeof (psh);
|
|
psh.dwFlags = PSH_NOCONTEXTHELP | PSH_WIZARD | PSH_WIZARD_LITE;
|
|
psh.hwndParent = hwndParent;
|
|
psh.hInstance = g_hinst;
|
|
psh.nPages = nPages;
|
|
psh.phpage = rghPages;
|
|
|
|
if (PropertySheet(&psh) == IDOK)
|
|
{
|
|
AddUserToListView(GetDlgItem(hwndParent, IDC_USER_LIST), pNewUser, TRUE);
|
|
}
|
|
else
|
|
{
|
|
// User clicked cancel
|
|
delete pNewUser;
|
|
pNewUser = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserlistPropertyPage::LaunchAddNetUserWizard(HWND hwndParent)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
static const int nPages = 2;
|
|
int cPages = 0;
|
|
HPROPSHEETPAGE rghPages[nPages];
|
|
|
|
// Create a new user record
|
|
CUserInfo* pNewUser = new CUserInfo;
|
|
if ( !pNewUser )
|
|
{
|
|
DisplayFormatMessage(hwndParent, IDS_USR_NEWUSERWIZARD_CAPTION, IDS_USR_CREATE_MISC_ERROR, MB_OK | MB_ICONERROR);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pNewUser->InitializeForNewUser();
|
|
pNewUser->m_userType = CUserInfo::DOMAINUSER;
|
|
|
|
PROPSHEETPAGE psp = {0};
|
|
// Common propsheetpage settings
|
|
psp.dwSize = sizeof (psp);
|
|
psp.hInstance = g_hinst;
|
|
psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
|
|
|
|
// Page 1: Find a network user page
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_FINDNETUSER_WIZARD_PAGE);
|
|
CNetworkUserWizardPage page1(pNewUser);
|
|
page1.SetPropSheetPageMembers(&psp);
|
|
rghPages[cPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
// Page 2: Local group addition
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_WIZARD_PAGE);
|
|
CGroupWizardPage page2(pNewUser, m_pData->GetGroupList());
|
|
page2.SetPropSheetPageMembers(&psp);
|
|
rghPages[cPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
PROPSHEETHEADER psh = {0};
|
|
psh.dwSize = sizeof (psh);
|
|
psh.dwFlags = PSH_NOCONTEXTHELP | PSH_WIZARD | PSH_WIZARD_LITE;
|
|
psh.hwndParent = hwndParent;
|
|
psh.hInstance = g_hinst;
|
|
psh.nPages = nPages;
|
|
psh.phpage = rghPages;
|
|
|
|
if (PropertySheet(&psh) == IDOK)
|
|
{
|
|
AddUserToListView(GetDlgItem(hwndParent, IDC_USER_LIST), pNewUser, TRUE);
|
|
m_pData->UserInfoChanged(pNewUser->m_szUsername, pNewUser->m_szDomain);
|
|
}
|
|
else
|
|
{
|
|
// No errors, but the user clicked Cancel...
|
|
delete pNewUser;
|
|
pNewUser = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CUserlistPropertyPage::LaunchUserProperties(HWND hwndParent)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
HWND hwndList = GetDlgItem(hwndParent, IDC_USER_LIST);
|
|
CUserInfo* pUserInfo = GetSelectedUserInfo(hwndList);
|
|
if (pUserInfo != NULL)
|
|
{
|
|
pUserInfo->GetExtraUserInfo();
|
|
|
|
// page addition information
|
|
ADDPROPSHEETDATA apsd;
|
|
apsd.nPages = 0;
|
|
|
|
// Common propsheetpage settings
|
|
PROPSHEETPAGE psp = {0};
|
|
psp.dwSize = sizeof (psp);
|
|
psp.hInstance = g_hinst;
|
|
psp.dwFlags = PSP_DEFAULT;
|
|
|
|
// If we have a local user, show both the username and group page, ow
|
|
// just the group page
|
|
// Page 1: Username entry page
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERNAME_PROP_PAGE);
|
|
CUsernamePropertyPage page1(pUserInfo);
|
|
page1.SetPropSheetPageMembers(&psp);
|
|
|
|
// Only actually create the prop page if we have a local user
|
|
if (pUserInfo->m_userType == CUserInfo::LOCALUSER)
|
|
{
|
|
apsd.rgPages[apsd.nPages++] = CreatePropertySheetPage(&psp);
|
|
}
|
|
|
|
// Always add the second page
|
|
// Page 2: Local group addition
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_PROP_PAGE);
|
|
CGroupPropertyPage page2(pUserInfo, m_pData->GetGroupList());
|
|
page2.SetPropSheetPageMembers(&psp);
|
|
apsd.rgPages[apsd.nPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
HPSXA hpsxa = AddExtraUserPropPages(&apsd, pUserInfo->m_psid);
|
|
|
|
PROPSHEETHEADER psh = {0};
|
|
psh.dwSize = sizeof (psh);
|
|
psh.dwFlags = PSH_DEFAULT | PSH_PROPTITLE;
|
|
|
|
TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2];
|
|
MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
|
|
psh.pszCaption = szDomainUser;
|
|
psh.hwndParent = hwndParent;
|
|
psh.hInstance = g_hinst;
|
|
psh.nPages = apsd.nPages;
|
|
psh.phpage = apsd.rgPages;
|
|
|
|
int iRetCode = (int)PropertySheet(&psh);
|
|
|
|
if (hpsxa != NULL)
|
|
SHDestroyPropSheetExtArray(hpsxa);
|
|
|
|
if (iRetCode == IDOK)
|
|
{
|
|
// PropSheet_Changed(GetParent(hwndParent), hwndParent);
|
|
|
|
// So that we don't delete this pUserInfo when we remove
|
|
// this user from the list:
|
|
m_pData->UserInfoChanged(pUserInfo->m_szUsername, (pUserInfo->m_szDomain[0] == 0) ? NULL : pUserInfo->m_szDomain);
|
|
RemoveSelectedUserFromList(hwndList, FALSE);
|
|
AddUserToListView(hwndList, pUserInfo, TRUE);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
CUserInfo* CUserlistPropertyPage::GetSelectedUserInfo(HWND hwndList)
|
|
{
|
|
int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
|
|
if (iItem >= 0)
|
|
{
|
|
LVITEM lvi = {0};
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.iItem = iItem;
|
|
if (ListView_GetItem(hwndList, &lvi))
|
|
{
|
|
return (CUserInfo*)lvi.lParam;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void CUserlistPropertyPage::RemoveSelectedUserFromList(HWND hwndList, BOOL fFreeUserInfo)
|
|
{
|
|
int iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED);
|
|
|
|
// If we don't want to delete this user info, better set it to NULL
|
|
if (!fFreeUserInfo)
|
|
{
|
|
LVITEM lvi = {0};
|
|
lvi.iItem = iItem;
|
|
lvi.mask = LVIF_PARAM;
|
|
lvi.lParam = (LPARAM) (CUserInfo*) NULL;
|
|
ListView_SetItem(hwndList, &lvi);
|
|
}
|
|
|
|
ListView_DeleteItem(hwndList, iItem);
|
|
|
|
int iSelect = iItem > 0 ? iItem - 1 : 0;
|
|
ListView_SetItemState(hwndList, iSelect, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
|
|
|
|
SetFocus(hwndList);
|
|
}
|
|
|
|
|
|
void CUserlistPropertyPage::OnRemove(HWND hwnd)
|
|
{
|
|
HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST);
|
|
|
|
CUserInfo* pUserInfo = GetSelectedUserInfo(hwndList);
|
|
if (pUserInfo)
|
|
{
|
|
if (ConfirmRemove(hwnd, pUserInfo) == IDYES)
|
|
{
|
|
if (SUCCEEDED(pUserInfo->Remove()))
|
|
{
|
|
RemoveSelectedUserFromList(hwndList, TRUE);
|
|
}
|
|
else
|
|
{
|
|
TCHAR szDisplayName[MAX_USER + MAX_DOMAIN + 2];
|
|
::MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername,
|
|
szDisplayName, ARRAYSIZE(szDisplayName));
|
|
|
|
DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION,
|
|
IDS_USR_REMOVE_MISC_ERROR, MB_ICONERROR | MB_OK, szDisplayName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int CUserlistPropertyPage::ConfirmRemove(HWND hwnd, CUserInfo* pUserInfo)
|
|
{
|
|
TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2];
|
|
MakeDomainUserString(pUserInfo->m_szDomain, pUserInfo->m_szUsername, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
return DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_USR_REMOVEUSER_WARNING,
|
|
MB_ICONEXCLAMATION | MB_YESNO, szDomainUser);
|
|
}
|
|
|
|
void CUserlistPropertyPage::SetPageState(HWND hwnd)
|
|
{
|
|
BOOL fAutologon = (BST_UNCHECKED ==
|
|
SendMessage(GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK), BM_GETCHECK, 0, 0));
|
|
|
|
EnableControls(hwnd, rgidDisableOnAutologon, ARRAYSIZE(rgidDisableOnAutologon),
|
|
!fAutologon);
|
|
|
|
HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST);
|
|
CUserInfo* pUserInfo = GetSelectedUserInfo(hwndList);
|
|
if (pUserInfo)
|
|
{
|
|
TCHAR szPWGroup[128];
|
|
FormatMessageString(IDS_USR_PWGROUP_FORMAT, szPWGroup, ARRAYSIZE(szPWGroup), pUserInfo->m_szUsername);
|
|
SetWindowText(GetDlgItem(hwnd, IDC_PWGROUP_STATIC), szPWGroup);
|
|
|
|
TCHAR szPWMessage[128];
|
|
|
|
// If the logged on user is the selected user
|
|
CUserInfo* pLoggedOnUser = m_pData->GetLoggedOnUserInfo();
|
|
if ((StrCmpI(pUserInfo->m_szUsername, pLoggedOnUser->m_szUsername) == 0) &&
|
|
(StrCmpI(pUserInfo->m_szDomain, pLoggedOnUser->m_szDomain) == 0))
|
|
{
|
|
LoadString(g_hinst, IDS_USR_YOURPWMESSAGE_FORMAT, szPWMessage, ARRAYSIZE(szPWMessage));
|
|
EnableWindow(GetDlgItem(hwnd, IDC_PASSWORD_BUTTON), FALSE);
|
|
}
|
|
// If the user is a local user
|
|
else if (pUserInfo->m_userType == CUserInfo::LOCALUSER)
|
|
{
|
|
// We can set this user's password
|
|
FormatMessageString(IDS_USR_PWMESSAGE_FORMAT, szPWMessage, ARRAYSIZE(szPWMessage), pUserInfo->m_szUsername);
|
|
}
|
|
else
|
|
{
|
|
// Nothing can be done with this user's password
|
|
// the selected user may be a domain user or a group or something
|
|
// We can set this user's password
|
|
FormatMessageString(IDS_USR_CANTCHANGEPW_FORMAT, szPWMessage, ARRAYSIZE(szPWMessage), pUserInfo->m_szUsername);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_PASSWORD_BUTTON), FALSE);
|
|
}
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_PASSWORD_STATIC), szPWMessage);
|
|
|
|
// Set the icon for the user
|
|
HICON hIcon = ImageList_GetIcon(m_himlLarge, pUserInfo->m_userType, ILD_NORMAL);
|
|
Static_SetIcon(GetDlgItem(hwnd, IDC_CURRENTUSER_ICON), hIcon);
|
|
}
|
|
else
|
|
{
|
|
EnableControls(hwnd, rgidDisableOnNoSelection, ARRAYSIZE(rgidDisableOnNoSelection), FALSE);
|
|
}
|
|
}
|
|
|
|
HRESULT CUserlistPropertyPage::SetAutologonState(HWND hwnd, BOOL fAutologon)
|
|
{
|
|
PropSheet_Changed(GetParent(hwnd), hwnd);
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CUserlistPropertyPage::OnListViewItemChanged(HWND hwnd)
|
|
{
|
|
SetPageState(hwnd);
|
|
return TRUE;
|
|
}
|
|
|
|
long CUserlistPropertyPage::OnApply(HWND hwnd)
|
|
{
|
|
long applyEffect = PSNRET_NOERROR;
|
|
|
|
BOOL fAutologonSet = (BST_UNCHECKED == SendMessage(GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK), BM_GETCHECK, 0, 0));
|
|
if (!fAutologonSet)
|
|
{
|
|
ClearAutoLogon(); // Ensure autologon is cleared
|
|
}
|
|
else if (m_fAutologonCheckChanged)
|
|
{
|
|
CUserInfo* pSelectedUser = GetSelectedUserInfo(GetDlgItem(hwnd, IDC_USER_LIST));
|
|
|
|
TCHAR szNullName[] = TEXT("");
|
|
CAutologonUserDlg dlg((pSelectedUser != NULL) ? pSelectedUser->m_szUsername : szNullName);
|
|
if (dlg.DoModal(g_hinst, MAKEINTRESOURCE(IDD_USR_AUTOLOGON_DLG), hwnd) == IDCANCEL)
|
|
{
|
|
applyEffect = PSNRET_INVALID_NOCHANGEPAGE;
|
|
}
|
|
}
|
|
|
|
m_fAutologonCheckChanged = FALSE;
|
|
|
|
if (applyEffect == PSNRET_INVALID_NOCHANGEPAGE)
|
|
{
|
|
m_pData->Initialize(hwnd); // Reload the data and list
|
|
SetupList(hwnd);
|
|
}
|
|
|
|
return applyEffect;
|
|
}
|
|
|
|
void CUserlistPropertyPage::SetupList(HWND hwnd)
|
|
{
|
|
HWND hwndList = GetDlgItem(hwnd, IDC_USER_LIST);
|
|
|
|
// Disable autologon check box in the domain case where autologon isn't // enabled
|
|
HWND hwndCheck = GetDlgItem(hwnd, IDC_AUTOLOGON_CHECK);
|
|
if (m_pData->IsComputerInDomain() && !m_pData->IsAutologonEnabled())
|
|
{
|
|
ShowWindow(hwndCheck, SW_HIDE);
|
|
EnableWindow(hwndCheck, FALSE);
|
|
|
|
// Move most controls up a bit if the autologon is not visible
|
|
RECT rcBottom;
|
|
GetWindowRect(GetDlgItem(hwnd, IDC_LISTTITLE_STATIC), &rcBottom);
|
|
|
|
RECT rcTop;
|
|
GetWindowRect(hwndCheck, &rcTop);
|
|
|
|
int dy = rcTop.top - rcBottom.top;
|
|
|
|
OffsetControls(hwnd, rgidMoveOnNoAutologonCheck,
|
|
ARRAYSIZE(rgidMoveOnNoAutologonCheck), 0, dy);
|
|
|
|
// Grow the list by this amount also
|
|
RECT rcList;
|
|
GetWindowRect(hwndList, &rcList);
|
|
|
|
SetWindowPos(hwndList, NULL, 0, 0, rcList.right - rcList.left,
|
|
rcList.bottom - rcList.top - dy, SWP_NOZORDER|SWP_NOMOVE);
|
|
}
|
|
|
|
SendMessage(hwndCheck, BM_SETCHECK,
|
|
m_pData->IsAutologonEnabled() ? BST_UNCHECKED : BST_CHECKED, 0);
|
|
|
|
// Set the text in the set password group.
|
|
SetPageState(hwnd);
|
|
}
|
|
|
|
HRESULT CUserlistPropertyPage::LaunchSetPasswordDialog(HWND hwndParent)
|
|
{
|
|
CUserInfo* pUserInfo = GetSelectedUserInfo(GetDlgItem(hwndParent, IDC_USER_LIST));
|
|
if ((pUserInfo != NULL) && (pUserInfo->m_userType == CUserInfo::LOCALUSER))
|
|
{
|
|
CChangePasswordDlg dlg(pUserInfo);
|
|
dlg.DoModal(g_hinst, MAKEINTRESOURCE(IDD_USR_SETPASSWORD_DLG), hwndParent);
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
#define MAX_EXTRA_PAGES 10
|
|
|
|
HPSXA CUserlistPropertyPage::AddExtraUserPropPages(ADDPROPSHEETDATA* ppsd, PSID psid)
|
|
{
|
|
HPSXA hpsxa = NULL;
|
|
|
|
IDataObject *pdo;
|
|
HRESULT hr = CUserSidDataObject_CreateInstance(psid, &pdo);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hpsxa = SHCreatePropSheetExtArrayEx(HKEY_LOCAL_MACHINE, REGSTR_USERPROPERTIES_SHEET, MAX_EXTRA_PAGES, pdo);
|
|
if (hpsxa)
|
|
{
|
|
SHAddFromPropSheetExtArray(hpsxa, AddPropSheetPageCallback, (LPARAM) ppsd);
|
|
}
|
|
pdo->Release();
|
|
}
|
|
return hpsxa;
|
|
}
|
|
|
|
|
|
// ListCompare
|
|
// Compares list items in for sorting the listview by column
|
|
// lParamSort gets the 1-based column index. If lParamSort is negative
|
|
// it indicates that the given column should be sorted in reverse.
|
|
|
|
int CUserlistPropertyPage::ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
{
|
|
CUserInfo* pUserInfo1 = (CUserInfo*)lParam1;
|
|
CUserInfo* pUserInfo2 = (CUserInfo*)lParam2;
|
|
int iColumn = (int)lParamSort;
|
|
|
|
BOOL fReverse = FALSE;
|
|
if (iColumn < 0)
|
|
{
|
|
fReverse = TRUE;
|
|
iColumn = -iColumn;
|
|
}
|
|
|
|
int iResult = 0; // they match...
|
|
switch (iColumn)
|
|
{
|
|
case 1:
|
|
iResult = lstrcmpi(pUserInfo1->m_szUsername, pUserInfo2->m_szUsername);
|
|
break;
|
|
|
|
case 2:
|
|
iResult = lstrcmpi(pUserInfo1->m_szDomain, pUserInfo2->m_szDomain);
|
|
break;
|
|
|
|
case 3:
|
|
iResult = lstrcmpi(pUserInfo1->m_szGroups, pUserInfo2->m_szGroups);
|
|
break;
|
|
}
|
|
|
|
if (fReverse)
|
|
iResult = -iResult;
|
|
|
|
return iResult;
|
|
}
|
|
|
|
|
|
// The DoModal call for this dialog will return IDOK if the applet should be
|
|
// shown or IDCANCEL if the users.cpl should exit without displaying the applet.
|
|
|
|
// dsheldon - TODO: Remove this dialog and don't let non-admins runas the cpl
|
|
|
|
class CSecurityCheckDlg: public CDialog
|
|
{
|
|
public:
|
|
CSecurityCheckDlg(LPCTSTR pszDomainUser):
|
|
m_pszDomainUser(pszDomainUser)
|
|
{}
|
|
|
|
private:
|
|
virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL OnNotify(HWND hwnd, int id, NMHDR* pnmhdr);
|
|
HRESULT RelaunchAsUser(HWND hwnd);
|
|
|
|
LPCTSTR m_pszDomainUser;
|
|
};
|
|
|
|
|
|
// implementation
|
|
|
|
INT_PTR CSecurityCheckDlg::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(uMsg)
|
|
{
|
|
HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog);
|
|
HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hwndDlg, WM_NOTIFY, OnNotify);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CSecurityCheckDlg::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
// First we must check if the current user is a local administrator; if this is
|
|
// the case, our dialog doesn't even display
|
|
|
|
BOOL fIsLocalAdmin;
|
|
if (SUCCEEDED(IsUserLocalAdmin(NULL, &fIsLocalAdmin)))
|
|
{
|
|
if (fIsLocalAdmin)
|
|
{
|
|
EndDialog(hwnd, IDOK); // We want to continue and launch the applet (don't display the security check dlg)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
}
|
|
|
|
// Set the "can't launch User Options" message
|
|
TCHAR szUsername[MAX_USER + 1];
|
|
DWORD cchUsername = ARRAYSIZE(szUsername);
|
|
|
|
TCHAR szDomain[MAX_DOMAIN + 1];
|
|
DWORD cchDomain = ARRAYSIZE(szDomain);
|
|
if (GetCurrentUserAndDomainName(szUsername, &cchUsername, szDomain, &cchDomain))
|
|
{
|
|
TCHAR szDomainAndUsername[MAX_DOMAIN + MAX_USER + 2];
|
|
|
|
MakeDomainUserString(szDomain, szUsername, szDomainAndUsername, ARRAYSIZE(szDomainAndUsername));
|
|
|
|
TCHAR szMessage[256];
|
|
if (FormatMessageString(IDS_USR_CANTRUNCPL_FORMAT, szMessage, ARRAYSIZE(szMessage), szDomainAndUsername))
|
|
{
|
|
SetWindowText(GetDlgItem(hwnd, IDC_CANTRUNCPL_STATIC), szMessage);
|
|
}
|
|
|
|
TCHAR szAdministrator[MAX_USER + 1];
|
|
|
|
LoadString(g_hinst, IDS_ADMINISTRATOR, szAdministrator, ARRAYSIZE(szAdministrator));
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_USER), szAdministrator);
|
|
|
|
TCHAR szMachine[MAX_COMPUTERNAME + 1];
|
|
|
|
DWORD dwSize = ARRAYSIZE(szMachine);
|
|
::GetComputerName(szMachine, &dwSize);
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_DOMAIN), szMachine);
|
|
}
|
|
|
|
// Limit the text in the edit fields
|
|
HWND hwndUsername = GetDlgItem(hwnd, IDC_USER);
|
|
Edit_LimitText(hwndUsername, MAX_USER);
|
|
|
|
HWND hwndDomain = GetDlgItem(hwnd, IDC_DOMAIN);
|
|
Edit_LimitText(hwndDomain, MAX_DOMAIN);
|
|
|
|
HWND hwndPassword = GetDlgItem(hwnd, IDC_PASSWORD);
|
|
Edit_LimitText(hwndPassword, MAX_PASSWORD);
|
|
|
|
if (!IsComputerInDomain())
|
|
{
|
|
// Don't need domain box
|
|
EnableWindow(hwndDomain, FALSE);
|
|
ShowWindow(hwndDomain, SW_HIDE);
|
|
ShowWindow(GetDlgItem(hwnd, IDC_DOMAIN_STATIC), SW_HIDE);
|
|
|
|
// Move up the OK/Cancel buttons and text and shrink the dialog
|
|
RECT rcDomain;
|
|
GetWindowRect(hwndDomain, &rcDomain);
|
|
|
|
RECT rcPassword;
|
|
GetWindowRect(hwndPassword, &rcPassword);
|
|
|
|
int dy = (rcPassword.top - rcDomain.top);
|
|
// dy is negative
|
|
|
|
OffsetWindow(GetDlgItem(hwnd, IDOK), 0, dy);
|
|
OffsetWindow(GetDlgItem(hwnd, IDCANCEL), 0, dy);
|
|
OffsetWindow(GetDlgItem(hwnd, IDC_PASSWORD_STATIC), 0, dy);
|
|
OffsetWindow(GetDlgItem(hwnd, IDC_OTHEROPTIONS_LINK), 0, dy);
|
|
|
|
RECT rcDialog;
|
|
GetWindowRect(hwnd, &rcDialog);
|
|
|
|
rcDialog.bottom += dy;
|
|
|
|
MoveWindow(hwnd, rcDialog.left, rcDialog.top, rcDialog.right-rcDialog.left,
|
|
rcDialog.bottom-rcDialog.top, FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CSecurityCheckDlg::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case IDOK:
|
|
if (SUCCEEDED(RelaunchAsUser(hwnd)))
|
|
{
|
|
EndDialog(hwnd, IDCANCEL);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, IDCANCEL);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CSecurityCheckDlg::OnNotify(HWND hwnd, int id, NMHDR *pnmhdr)
|
|
{
|
|
BOOL fHandled = FALSE;
|
|
|
|
switch (pnmhdr->code)
|
|
{
|
|
// Handle link window clicks
|
|
case NM_CLICK:
|
|
case NM_RETURN:
|
|
{
|
|
if (IDC_OTHEROPTIONS_LINK == id)
|
|
{
|
|
// First link in the control is "manage passwords", second is "passport wizard"
|
|
|
|
NMLINKWND* pnm = (NMLINKWND*) pnmhdr;
|
|
if (0 == pnm->item.iLink)
|
|
{
|
|
// Launch "manage passwords"
|
|
CCertificateAPI::ManagePasswords(hwnd);
|
|
}
|
|
else if (1 == pnm->item.iLink)
|
|
{
|
|
// Launch passport wizard
|
|
IPassportWizard *pPW;
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_PassportWizard, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPassportWizard, &pPW))))
|
|
{
|
|
pPW->SetOptions(PPW_LAUNCHEDBYUSER);
|
|
pPW->Show(hwnd);
|
|
pPW->Release();
|
|
}
|
|
}
|
|
fHandled = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
};
|
|
|
|
return fHandled;
|
|
}
|
|
|
|
HRESULT CSecurityCheckDlg::RelaunchAsUser(HWND hwnd)
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
TCHAR szUsername[MAX_USER + 1];
|
|
FetchText(hwnd, IDC_USER, szUsername, ARRAYSIZE(szUsername));
|
|
|
|
TCHAR szDomain[MAX_DOMAIN + 1];
|
|
FetchText(hwnd, IDC_DOMAIN, szDomain, ARRAYSIZE(szDomain));
|
|
|
|
// If the user didn't type a domain
|
|
if (szDomain[0] == TEXT('\0'))
|
|
{
|
|
// Use this machine as the domain
|
|
DWORD cchComputername = ARRAYSIZE(szDomain);
|
|
::GetComputerName(szDomain, &cchComputername);
|
|
}
|
|
|
|
TCHAR szPassword[MAX_PASSWORD + 1];
|
|
GetWindowText(GetDlgItem(hwnd, IDC_PASSWORD), szPassword, ARRAYSIZE(szPassword));
|
|
|
|
// Now relaunch ourselves with this information
|
|
STARTUPINFO startupinfo = {0};
|
|
startupinfo.cb = sizeof (startupinfo);
|
|
|
|
WCHAR c_szCommandLineFormat[] = L"rundll32.exe netplwiz.dll,UsersRunDll %s";
|
|
|
|
// Put the "real" user name in the command-line so that we know what user is
|
|
// actually logged on to the machine even though we are re-launching in a different
|
|
// user context
|
|
WCHAR szCommandLine[ARRAYSIZE(c_szCommandLineFormat) + MAX_DOMAIN + MAX_USER + 2];
|
|
wnsprintf(szCommandLine, ARRAYSIZE(szCommandLine), c_szCommandLineFormat, m_pszDomainUser);
|
|
|
|
PROCESS_INFORMATION process_information;
|
|
if (CreateProcessWithLogonW(szUsername, szDomain, szPassword, LOGON_WITH_PROFILE, NULL,
|
|
szCommandLine, 0, NULL, NULL, &startupinfo, &process_information))
|
|
{
|
|
CloseHandle(process_information.hProcess);
|
|
CloseHandle(process_information.hThread);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
DisplayFormatMessage(hwnd, IDS_USR_APPLET_CAPTION, IDS_USR_CANTOPENCPLASUSER_ERROR, MB_OK|MB_ICONERROR);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Advanced Property Page
|
|
|
|
class CAdvancedPropertyPage: public CPropertyPage
|
|
{
|
|
public:
|
|
CAdvancedPropertyPage(CUserManagerData* pdata):
|
|
m_pData(pdata),
|
|
m_fRebootRequired(FALSE) {}
|
|
|
|
private:
|
|
virtual INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
BOOL OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam);
|
|
BOOL OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
BOOL OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh);
|
|
BOOL OnHelp(HWND hwnd, LPHELPINFO pHelpInfo);
|
|
BOOL OnContextMenu(HWND hwnd);
|
|
void ReadRequireCad(BOOL* pfRequireCad, BOOL* pfSetInPolicy);
|
|
void WriteRequireCad(BOOL fRequireCad);
|
|
|
|
CUserManagerData* m_pData;
|
|
BOOL m_fRebootRequired;
|
|
|
|
};
|
|
|
|
// Relevant regkeys/regvals
|
|
#define REGKEY_WINLOGON \
|
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon")
|
|
|
|
#define REGKEY_WINLOGON_POLICY \
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System")
|
|
|
|
#define REGVAL_DISABLE_CAD TEXT("DisableCAD")
|
|
|
|
void CAdvancedPropertyPage::ReadRequireCad(BOOL* pfRequireCad, BOOL* pfSetInPolicy)
|
|
{
|
|
HKEY hkey;
|
|
DWORD dwSize;
|
|
DWORD dwType;
|
|
BOOL fDisableCad;
|
|
NT_PRODUCT_TYPE nttype;
|
|
|
|
*pfRequireCad = TRUE;
|
|
*pfSetInPolicy = FALSE;
|
|
|
|
if (!RtlGetNtProductType(&nttype))
|
|
{
|
|
nttype = NtProductWinNt;
|
|
}
|
|
|
|
// By default, don't require CAD for workstations not
|
|
// on a domain only
|
|
if ((NtProductWinNt == nttype) && !IsComputerInDomain())
|
|
{
|
|
*pfRequireCad = FALSE;
|
|
}
|
|
|
|
// Read the setting from the machine preferences
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WINLOGON, 0,
|
|
KEY_READ, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(fDisableCad);
|
|
|
|
if (ERROR_SUCCESS == RegQueryValueEx (hkey, REGVAL_DISABLE_CAD, NULL, &dwType,
|
|
(LPBYTE) &fDisableCad, &dwSize))
|
|
{
|
|
*pfRequireCad = !fDisableCad;
|
|
}
|
|
|
|
RegCloseKey (hkey);
|
|
}
|
|
|
|
// Check if C-A-D is disabled via policy
|
|
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WINLOGON_POLICY, 0, KEY_READ,
|
|
&hkey) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(fDisableCad);
|
|
|
|
if (ERROR_SUCCESS == RegQueryValueEx (hkey, REGVAL_DISABLE_CAD, NULL, &dwType,
|
|
(LPBYTE) &fDisableCad, &dwSize))
|
|
{
|
|
*pfRequireCad = !fDisableCad;
|
|
*pfSetInPolicy = TRUE;
|
|
}
|
|
|
|
RegCloseKey (hkey);
|
|
}
|
|
}
|
|
|
|
void CAdvancedPropertyPage::WriteRequireCad(BOOL fRequireCad)
|
|
{
|
|
HKEY hkey;
|
|
DWORD dwDisp;
|
|
BOOL fDisableCad = !fRequireCad;
|
|
|
|
if (ERROR_SUCCESS == RegCreateKeyEx( HKEY_LOCAL_MACHINE, REGKEY_WINLOGON, 0,
|
|
NULL, 0, KEY_WRITE, NULL, &hkey, &dwDisp))
|
|
{
|
|
RegSetValueEx(hkey, REGVAL_DISABLE_CAD, 0, REG_DWORD,
|
|
(LPBYTE) &fDisableCad, sizeof(fDisableCad));
|
|
|
|
RegCloseKey (hkey);
|
|
}
|
|
}
|
|
|
|
static const DWORD rgAdvHelpIds[] =
|
|
{
|
|
IDC_ADVANCED_BUTTON, IDH_ADVANCED_BUTTON,
|
|
IDC_BOOT_ICON, IDH_SECUREBOOT_CHECK,
|
|
IDC_BOOT_TEXT, IDH_SECUREBOOT_CHECK,
|
|
IDC_REQUIRECAD, IDH_SECUREBOOT_CHECK,
|
|
IDC_MANAGEPWD_BUTTON, IDH_MANAGEPWD_BUTTON,
|
|
IDC_PASSPORTWIZARD, IDH_PASSPORTWIZARD,
|
|
IDC_CERT_ICON, (DWORD) -1,
|
|
IDC_CERT_TEXT, (DWORD) -1,
|
|
0, 0
|
|
};
|
|
|
|
INT_PTR CAdvancedPropertyPage::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwndDlg, WM_INITDIALOG, OnInitDialog);
|
|
HANDLE_MSG(hwndDlg, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hwndDlg, WM_NOTIFY, OnNotify);
|
|
case WM_HELP: return OnHelp(hwndDlg, (LPHELPINFO) lParam);
|
|
case WM_CONTEXTMENU: return OnContextMenu((HWND) wParam);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CAdvancedPropertyPage::OnNotify(HWND hwnd, int idCtrl, LPNMHDR pnmh)
|
|
{
|
|
BOOL fReturn = FALSE;
|
|
switch (pnmh->code)
|
|
{
|
|
case PSN_APPLY:
|
|
{
|
|
HWND hwndCheck = GetDlgItem(hwnd, IDC_REQUIRECAD);
|
|
BOOL fRequireCad = (BST_CHECKED == Button_GetCheck(hwndCheck));
|
|
|
|
// See if a change is really necessary
|
|
BOOL fOldRequireCad;
|
|
BOOL fDummy;
|
|
|
|
ReadRequireCad(&fOldRequireCad, &fDummy);
|
|
|
|
if (fRequireCad != fOldRequireCad)
|
|
{
|
|
WriteRequireCad(fRequireCad);
|
|
// m_fRebootRequired = TRUE;
|
|
// Uncomment the line above if it ever becomes necessary to reboot the machine - it isn't now.
|
|
}
|
|
|
|
// xxx->lParam == 0 means Ok as opposed to Apply
|
|
if ((((PSHNOTIFY*) pnmh)->lParam) && m_fRebootRequired)
|
|
{
|
|
PropSheet_RebootSystem(GetParent(hwnd));
|
|
}
|
|
|
|
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, PSNRET_NOERROR);
|
|
fReturn = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return fReturn;
|
|
}
|
|
|
|
BOOL CAdvancedPropertyPage::OnHelp(HWND hwnd, LPHELPINFO pHelpInfo)
|
|
{
|
|
WinHelp((HWND) pHelpInfo->hItemHandle, m_pData->GetHelpfilePath(),
|
|
HELP_WM_HELP, (ULONG_PTR) (LPTSTR)rgAdvHelpIds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CAdvancedPropertyPage::OnContextMenu(HWND hwnd)
|
|
{
|
|
WinHelp(hwnd, m_pData->GetHelpfilePath(), HELP_CONTEXTMENU, (ULONG_PTR) (LPTSTR)rgAdvHelpIds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CAdvancedPropertyPage::OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
// Do the required mucking for the Require C-A-D checkbox...
|
|
// Read the setting for the require CAD checkbox
|
|
BOOL fRequireCad;
|
|
BOOL fSetInPolicy;
|
|
|
|
ReadRequireCad(&fRequireCad, &fSetInPolicy);
|
|
|
|
HWND hwndCheck = GetDlgItem(hwnd, IDC_REQUIRECAD);
|
|
// Disable the check if set in policy
|
|
EnableWindow(hwndCheck, !fSetInPolicy);
|
|
|
|
// Set the check accordingly
|
|
Button_SetCheck(hwndCheck,
|
|
fRequireCad ? BST_CHECKED : BST_UNCHECKED);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CAdvancedPropertyPage::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch (id)
|
|
{
|
|
case IDC_MANAGEPWD_BUTTON:
|
|
{
|
|
CCertificateAPI::ManagePasswords(hwnd);
|
|
}
|
|
break;
|
|
|
|
case IDC_PASSPORTWIZARD:
|
|
{
|
|
IPassportWizard *pPW;
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_PassportWizard, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IPassportWizard, &pPW))))
|
|
{
|
|
pPW->SetOptions(PPW_LAUNCHEDBYUSER);
|
|
pPW->Show(hwnd);
|
|
pPW->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_ADVANCED_BUTTON:
|
|
{
|
|
// Launch the MMC local user manager
|
|
STARTUPINFO startupinfo = {0};
|
|
startupinfo.cb = sizeof (startupinfo);
|
|
|
|
PROCESS_INFORMATION process_information;
|
|
|
|
static const TCHAR szMMCCommandLine[] =
|
|
TEXT("mmc.exe %systemroot%\\system32\\lusrmgr.msc computername=localmachine");
|
|
|
|
TCHAR szExpandedCommandLine[MAX_PATH];
|
|
|
|
if (ExpandEnvironmentStrings(szMMCCommandLine, szExpandedCommandLine,
|
|
ARRAYSIZE(szExpandedCommandLine)) > 0)
|
|
{
|
|
if (CreateProcess(NULL, szExpandedCommandLine, NULL, NULL, FALSE, 0, NULL, NULL,
|
|
&startupinfo, &process_information))
|
|
{
|
|
CloseHandle(process_information.hProcess);
|
|
CloseHandle(process_information.hThread);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDC_REQUIRECAD:
|
|
PropSheet_Changed(GetParent(hwnd), hwnd);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// users control panel entry point
|
|
|
|
void APIENTRY UsersRunDll(HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szDomainUser[MAX_USER + MAX_DOMAIN + 2];
|
|
*szDomainUser = 0;
|
|
|
|
// Get the "real" user of this machine - this may be passed in the cmdline
|
|
if (0 == *pszCmdLine)
|
|
{
|
|
// user wasn't passed, assume its the currently logged on user
|
|
TCHAR szUser[MAX_USER + 1];
|
|
DWORD cchUser = ARRAYSIZE(szUser);
|
|
TCHAR szDomain[MAX_DOMAIN + 1];
|
|
DWORD cchDomain = ARRAYSIZE(szDomain);
|
|
|
|
if (0 != GetCurrentUserAndDomainName(szUser, &cchUser, szDomain, &cchDomain))
|
|
{
|
|
MakeDomainUserString(szDomain, szUser, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// User name was passed in, just copy it
|
|
MultiByteToWideChar(GetACP(), 0, pszCmdLine, -1, szDomainUser, ARRAYSIZE(szDomainUser));
|
|
}
|
|
|
|
// Initialize COM, but continue even if this fails.
|
|
BOOL fComInited = SUCCEEDED(CoInitialize(NULL));
|
|
|
|
// See if we're already running
|
|
TCHAR szCaption[256];
|
|
LoadString(g_hinst, IDS_USR_APPLET_CAPTION, szCaption, ARRAYSIZE(szCaption));
|
|
CEnsureSingleInstance ESI(szCaption);
|
|
|
|
if (!ESI.ShouldExit())
|
|
{
|
|
LinkWindow_RegisterClass();
|
|
|
|
// Create the security check dialog to ensure the logged-on user
|
|
// is a local admin
|
|
CSecurityCheckDlg dlg(szDomainUser);
|
|
|
|
if (dlg.DoModal(g_hinst, MAKEINTRESOURCE(IDD_USR_SECURITYCHECK_DLG), NULL) == IDOK)
|
|
{
|
|
// Create the shared user mgr object
|
|
CUserManagerData data(szDomainUser);
|
|
|
|
// Create the property sheet and page template
|
|
// Maximum number of pages
|
|
ADDPROPSHEETDATA ppd;
|
|
ppd.nPages = 0;
|
|
|
|
// Settings common to all pages
|
|
PROPSHEETPAGE psp = {0};
|
|
psp.dwSize = sizeof (psp);
|
|
psp.hInstance = g_hinst;
|
|
|
|
// Create the userlist property sheet page and its managing object
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERLIST_PAGE);
|
|
CUserlistPropertyPage userListPage(&data);
|
|
userListPage.SetPropSheetPageMembers(&psp);
|
|
ppd.rgPages[ppd.nPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_ADVANCED_PAGE);
|
|
CAdvancedPropertyPage advancedPage(&data);
|
|
advancedPage.SetPropSheetPageMembers(&psp);
|
|
ppd.rgPages[ppd.nPages++] = CreatePropertySheetPage(&psp);
|
|
|
|
HPSXA hpsxa = SHCreatePropSheetExtArrayEx(HKEY_LOCAL_MACHINE, REGSTR_USERSANDPASSWORDS_CPL, 10, NULL);
|
|
if (hpsxa != NULL)
|
|
SHAddFromPropSheetExtArray(hpsxa, AddPropSheetPageCallback, (LPARAM)&ppd);
|
|
|
|
// Create the prop sheet
|
|
PROPSHEETHEADER psh = {0};
|
|
psh.dwSize = sizeof (psh);
|
|
psh.dwFlags = PSH_DEFAULT;
|
|
psh.hwndParent = hwndStub;
|
|
psh.hInstance = g_hinst;
|
|
psh.pszCaption = szCaption;
|
|
psh.nPages = ppd.nPages;
|
|
psh.phpage = ppd.rgPages;
|
|
|
|
// Show the property sheet
|
|
int iRetCode = PropertySheetIcon(&psh, MAKEINTRESOURCE(IDI_USR_USERS));
|
|
|
|
if (hpsxa != NULL)
|
|
{
|
|
SHDestroyPropSheetExtArray(hpsxa);
|
|
}
|
|
|
|
if (iRetCode == -1)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
// Special case when we must restart or reboot
|
|
if (iRetCode == ID_PSREBOOTSYSTEM)
|
|
{
|
|
RestartDialogEx(NULL, NULL, EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG);
|
|
}
|
|
else if (iRetCode == ID_PSRESTARTWINDOWS)
|
|
{
|
|
RestartDialogEx(NULL, NULL, EWX_REBOOT, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG);
|
|
}
|
|
else if (data.LogoffRequired())
|
|
{
|
|
int iLogoff = DisplayFormatMessage(NULL, IDS_USERSANDPASSWORDS, IDS_LOGOFFREQUIRED, MB_YESNO | MB_ICONQUESTION);
|
|
|
|
if (iLogoff == IDYES)
|
|
{
|
|
// Tell explorer to log off the "real" logged on user. We need to do this
|
|
// since they may be running U&P as a different user.
|
|
HWND hwnd = FindWindow(TEXT("Shell_TrayWnd"), TEXT(""));
|
|
if ( hwnd )
|
|
{
|
|
UINT uMsg = RegisterWindowMessage(TEXT("Logoff User"));
|
|
|
|
PostMessage(hwnd, uMsg, 0,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Security check told us to exit; either another instance of the CPL is starting
|
|
// with admin priviledges or the user cancelled on the sec. check. dlg.
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (fComInited)
|
|
CoUninitialize();
|
|
}
|
|
|
|
|
|
// user property property page object
|
|
|
|
class CUserPropertyPages: public IShellExtInit, IShellPropSheetExt
|
|
{
|
|
public:
|
|
CUserPropertyPages();
|
|
~CUserPropertyPages();
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv);
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
STDMETHODIMP_(ULONG) Release();
|
|
|
|
// IShellExtInit
|
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pdo, HKEY hkeyProgID);
|
|
|
|
// IShellPropSheetExt
|
|
STDMETHODIMP AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam);
|
|
STDMETHODIMP ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplaceWith, LPARAM lParam)
|
|
{ return E_NOTIMPL; }
|
|
|
|
private:
|
|
LONG _cRef;
|
|
|
|
CUserInfo *_pUserInfo; // The user for the property sheet
|
|
CUsernamePropertyPage *_pUserNamePage; // Basic info page, only shown for local users
|
|
CGroupPropertyPage *_pGroupPage; // The group page, which is common to both local and domain users
|
|
CGroupInfoList _GroupList; // The group list, used by the group page
|
|
};
|
|
|
|
|
|
CUserPropertyPages::CUserPropertyPages() :
|
|
_cRef(1)
|
|
{
|
|
DllAddRef();
|
|
}
|
|
|
|
CUserPropertyPages::~CUserPropertyPages()
|
|
{
|
|
if (_pUserInfo)
|
|
delete _pUserInfo;
|
|
if (_pUserNamePage)
|
|
delete _pUserNamePage;
|
|
if (_pGroupPage)
|
|
delete _pGroupPage;
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
ULONG CUserPropertyPages::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CUserPropertyPages::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
HRESULT CUserPropertyPages::QueryInterface(REFIID riid, LPVOID* ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CUserPropertyPages, IShellExtInit), // IID_IShellExtInit
|
|
QITABENT(CUserPropertyPages, IShellPropSheetExt), // IID_IShellPropSheetExt
|
|
{0, 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
|
|
// IShellExtInit
|
|
|
|
HRESULT CUserPropertyPages::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pdo, HKEY hkeyProgID)
|
|
{
|
|
// Request the user's SID from the data object
|
|
FORMATETC fmt = {0};
|
|
fmt.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_USERPROPPAGESSID);
|
|
fmt.dwAspect = DVASPECT_CONTENT;
|
|
fmt.lindex = -1;
|
|
fmt.tymed = TYMED_HGLOBAL;
|
|
|
|
STGMEDIUM medium = { 0 };
|
|
HRESULT hr = pdo->GetData(&fmt, &medium);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// medium.hGlobal is the user's SID; make sure it isn't null and that
|
|
// we haven't already set our copy of the SID
|
|
if ((medium.hGlobal != NULL) && (_pUserInfo == NULL))
|
|
{
|
|
PSID psid = (PSID) GlobalLock(medium.hGlobal);
|
|
if (IsValidSid(psid))
|
|
{
|
|
// Create a user info structure to party on
|
|
_pUserInfo = new CUserInfo;
|
|
if (_pUserInfo)
|
|
{
|
|
hr = _pUserInfo->Load(psid, TRUE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _GroupList.Initialize(); // Get the groups
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED; // hGlobal was NULL or prop sheet was already init'ed
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// AddPages - handles adding the property pages
|
|
|
|
HRESULT CUserPropertyPages::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
|
|
{
|
|
PROPSHEETPAGE psp = {0};
|
|
psp.dwSize = sizeof (psp);
|
|
psp.hInstance = g_hinst;
|
|
|
|
if (_pUserInfo->m_userType == CUserInfo::LOCALUSER)
|
|
{
|
|
// Add the local user prop pages
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_USERNAME_PROP_PAGE);
|
|
|
|
_pUserNamePage = new CUsernamePropertyPage(_pUserInfo);
|
|
if (_pUserNamePage != NULL)
|
|
{
|
|
_pUserNamePage->SetPropSheetPageMembers(&psp);
|
|
lpfnAddPage(CreatePropertySheetPage(&psp), lParam);
|
|
}
|
|
}
|
|
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_USR_CHOOSEGROUP_PROP_PAGE);
|
|
|
|
_pGroupPage = new CGroupPropertyPage(_pUserInfo, &_GroupList);
|
|
if (_pGroupPage != NULL)
|
|
{
|
|
_pGroupPage->SetPropSheetPageMembers(&psp);
|
|
lpfnAddPage(CreatePropertySheetPage(&psp), lParam);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI CUserPropertyPages_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
CUserPropertyPages *pupp = new CUserPropertyPages();
|
|
if (!pupp)
|
|
{
|
|
*ppunk = NULL; // incase of failure
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = pupp->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
|
|
pupp->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// expose the SID for a user via an IDataObject
|
|
|
|
class CUserSidDataObject: public IDataObject
|
|
{
|
|
public:
|
|
CUserSidDataObject();
|
|
~CUserSidDataObject();
|
|
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv);
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
STDMETHODIMP_(ULONG) Release();
|
|
|
|
// IDataObject
|
|
STDMETHODIMP GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium);
|
|
STDMETHODIMP GetDataHere(FORMATETC* pFormatEtc, STGMEDIUM* pMedium)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP QueryGetData(FORMATETC* pFormatEtc)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC* pFormatetcIn, FORMATETC* pFormatetcOut)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP SetData(FORMATETC* pFormatetc, STGMEDIUM* pmedium, BOOL fRelease)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC ** ppenumFormatetc)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP DAdvise(FORMATETC* pFormatetc, DWORD advf, IAdviseSink* pAdvSink, DWORD * pdwConnection)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP DUnadvise(DWORD dwConnection)
|
|
{ return E_NOTIMPL; }
|
|
STDMETHODIMP EnumDAdvise(IEnumSTATDATA ** ppenumAdvise)
|
|
{ return E_NOTIMPL; }
|
|
|
|
HRESULT SetSid(PSID psid);
|
|
|
|
private:
|
|
LONG _cRef;
|
|
PSID _psid;
|
|
};
|
|
|
|
|
|
CUserSidDataObject::CUserSidDataObject() :
|
|
_cRef(1)
|
|
{
|
|
DllAddRef();
|
|
}
|
|
|
|
CUserSidDataObject::~CUserSidDataObject()
|
|
{
|
|
if (_psid)
|
|
LocalFree(_psid);
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
ULONG CUserSidDataObject::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CUserSidDataObject::Release()
|
|
{
|
|
ASSERT( 0 != _cRef );
|
|
ULONG cRef = InterlockedDecrement(&_cRef);
|
|
if ( 0 == cRef )
|
|
{
|
|
delete this;
|
|
}
|
|
return cRef;
|
|
}
|
|
|
|
HRESULT CUserSidDataObject::QueryInterface(REFIID riid, LPVOID* ppv)
|
|
{
|
|
static const QITAB qit[] =
|
|
{
|
|
QITABENT(CUserSidDataObject, IDataObject), // IID_IDataObject
|
|
{0, 0 },
|
|
};
|
|
return QISearch(this, qit, riid, ppv);
|
|
}
|
|
|
|
HRESULT CUserSidDataObject::GetData(FORMATETC* pFormatEtc, STGMEDIUM* pMedium)
|
|
{
|
|
HRESULT hr = QueryGetData(pFormatEtc);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pMedium->pUnkForRelease = (IDataObject*)this;
|
|
AddRef(); // reference to ourself
|
|
|
|
pMedium->tymed = TYMED_HGLOBAL;
|
|
pMedium->hGlobal = (HGLOBAL)_psid;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CUserSidDataObject::SetSid(PSID psid)
|
|
{
|
|
if (!psid)
|
|
return E_INVALIDARG;
|
|
|
|
if (_psid == NULL)
|
|
{
|
|
DWORD cbSid = GetLengthSid(psid);
|
|
|
|
_psid = (PSID)LocalAlloc(0, cbSid);
|
|
if (!_psid)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (CopySid(cbSid, _psid, psid))
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDAPI CUserSidDataObject_CreateInstance(PSID psid, IDataObject **ppdo)
|
|
{
|
|
CUserSidDataObject *pusdo = new CUserSidDataObject();
|
|
|
|
if (!pusdo)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pusdo->SetSid(psid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pusdo->QueryInterface(IID_PPV_ARG(IDataObject, ppdo));
|
|
}
|
|
pusdo->Release();
|
|
return hr;
|
|
}
|