// Logon.cpp : Windows Logon application // #include "priv.h" using namespace DirectUI; // Logon.cpp : Windows Logon application // #include "logon.h" #include "Fx.h" #include "backend.h" #include "resource.h" #include "eballoon.h" #include "profileutil.h" #include "langicon.h" #include BOOL RunningInWinlogon(); // from backend.cpp UsingDUIClass(Element); UsingDUIClass(Button); UsingDUIClass(ScrollBar); UsingDUIClass(Selector); UsingDUIClass(ScrollViewer); UsingDUIClass(Edit); // Globals LogonFrame* g_plf = NULL; ILogonStatusHost *g_pILogonStatusHost = NULL; CErrorBalloon g_pErrorBalloon; BOOL g_fNoAnimations = false; WCHAR szLastSelectedName[UNLEN + sizeof('\0')] = { L'\0' }; HANDLE g_rgH[3] = {0}; #define MAX_COMPUTERDESC_LENGTH 255 #define RECTWIDTH(r) (r.right - r.left) // Resource string loading LPCWSTR LoadResString(UINT nID) { static WCHAR szRes[101]; szRes[0] = NULL; LoadStringW(g_plf->GetHInstance(), nID, szRes, DUIARRAYSIZE(szRes) - 1); return szRes; } void SetButtonLabel(Button* pButton, LPCWSTR pszLabel) { Element *pLabel= (Element*)pButton->FindDescendent(StrToID(L"label")); DUIAssert(pLabel, "Cannot find button label, check the UI file"); if (pLabel != NULL) { pLabel->SetContentString(pszLabel); } } //////////////////////////////////////// // // SetElementAccessability // // Set the accessibility information for an element. // ///////////////////////////////////////// void inline SetElementAccessability(Element* pe, bool bAccessible, int iRole, LPCWSTR pszAccName) { if (pe) { pe->SetAccessible(bAccessible); pe->SetAccRole(iRole); pe->SetAccName(pszAccName); } } //////////////////////////////////////// // // RunningUnderWinlogon // // Check to see if the logon message window is available. // ///////////////////////////////////////// BOOL RunningUnderWinlogon() { return (FindWindow(TEXT("Shell_TrayWnd"), NULL) == NULL); } // global storage of username associated with failed logon. Used for // restore wizard via ECSubClassProc WCHAR g_szUsername[UNLEN]; //////////////////////////////////////// // // Support code for balloon tip launch of the Password Reset Wizard // // Code in support of subclassing the Password Panel edit control // // The control is displayed by InsertPasswordPanel and undisplayed // by RemovePasswordPanel. The control is subclassed when displayed // and unsubclassed when removed. // //////////////////////////////////////// // Entirely randomly selected magic number for the edit control subclass operation #define ECMAGICNUM 3212 void ShowResetWizard(HWND hw) { // Show password restore wizard HMODULE hDll = LoadLibrary(L"keymgr.dll"); if (hDll) { RUNDLLPROC PRShowRestoreWizard; PRShowRestoreWizard = (RUNDLLPROC) GetProcAddress(hDll,(LPCSTR)"PRShowRestoreWizardW"); if (PRShowRestoreWizard) { PRShowRestoreWizard(hw,NULL,g_szUsername,0); } FreeLibrary(hDll); } return; } LRESULT CALLBACK ECSubClassProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,UINT_PTR uID, ULONG_PTR dwRefData) { UNREFERENCED_PARAMETER(uID); UNREFERENCED_PARAMETER(dwRefData); switch (uMsg) { case WM_NOTIFY: { LPNMHDR lph; lph = (LPNMHDR) lParam; if (TTN_LINKCLICK == lph->code) { g_pErrorBalloon.HideToolTip(); ShowResetWizard(hwnd); return 0; } } default: break; } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } BOOL SubClassTheEditBox(HWND he) { if (he) { SetWindowSubclass(he,ECSubClassProc,ECMAGICNUM,NULL); } return (he != NULL); } void UnSubClassTheEditBox(HWND he) { if (he) { RemoveWindowSubclass(he,ECSubClassProc,ECMAGICNUM); } } //////////////////////////////////////// // // BuildAccountList // // Add all user accounts. // // Out parameter ppla returns a user that should be selected automatically if there is one. // the caller should select this user. // // RETURNS // S_OK if everything works out. Failure HRESULT if not. You are pretty much hosed if this fails // ///////////////////////////////////////// HRESULT BuildAccountList(LogonFrame* plf, OUT LogonAccount **ppla) { HRESULT hr; if (ppla) { *ppla = NULL; } hr = BuildUserListFromGina(plf, ppla); if (SUCCEEDED(hr)) { g_plf->SetUserListAvailable(TRUE); } #ifdef GADGET_ENABLE_GDIPLUS plf->FxStartup(); #endif return hr; } //////////////////////////////////////// // // PokeComCtl32 // // Flush comctl32's notion of the atom table. This is so balloon tips will work correctly // after a logoff. // ///////////////////////////////////////// void PokeComCtl32() { INITCOMMONCONTROLSEX iccex = { sizeof(INITCOMMONCONTROLSEX), ICC_WINLOGON_REINIT | ICC_STANDARD_CLASSES | ICC_TREEVIEW_CLASSES}; InitCommonControlsEx(&iccex); } //////////////////////////////////////////////////////// // // LogonFrame // //////////////////////////////////////////////////////// int LogonFrame::_nDPI = 0; HRESULT LogonFrame::Create(OUT Element** ppElement) { UNREFERENCED_PARAMETER(ppElement); DUIAssertForce("Cannot instantiate an HWND host derived Element via parser. Must use substitution."); return E_NOTIMPL; } HRESULT LogonFrame::Create(HWND hParent, BOOL fDblBuffer, UINT nCreate, OUT Element** ppElement) { *ppElement = NULL; LogonFrame* plf = HNew(); if (!plf) return E_OUTOFMEMORY; HRESULT hr = plf->Initialize(hParent, fDblBuffer, nCreate); if (FAILED(hr)) { plf->Destroy(); return hr; } *ppElement = plf; return S_OK; } void LogonFrame::ResetTheme(void) { Parser *pParser = NULL; Value *pvScrollerSheet; Element *peListScroller = NULL; if (g_rgH[SCROLLBARHTHEME]) { CloseThemeData(g_rgH[SCROLLBARHTHEME]); g_rgH[SCROLLBARHTHEME] = NULL; } g_rgH[SCROLLBARHTHEME] = OpenThemeData(_pnhh->GetHWND(), L"Scrollbar"); Parser::Create(IDR_LOGONUI, g_rgH, LogonParseError, &pParser); if (pParser && !pParser->WasParseError()) { pvScrollerSheet = pParser->GetSheet(L"scroller"); if (pvScrollerSheet) { peListScroller = (Selector*)FindDescendent(StrToID(L"scroller")); peListScroller->SetValue(SheetProp, PI_Local, pvScrollerSheet); pvScrollerSheet->Release(); } pParser->Destroy(); } } void LogonFrame::NextFlagAnimate(DWORD dwFrame) { #ifndef ANIMATE_FLAG UNREFERENCED_PARAMETER(dwFrame); #else Element* pe; if( dwFrame >= MAX_FLAG_FRAMES || g_fNoAnimations) { return; } pe = FindDescendent(StrToID(L"product")); DUIAssertNoMsg(pe); if (pe) { HBITMAP hbm = NULL; HDC hdc; Value *pv = NULL; hdc = CreateCompatibleDC(_hdcAnimation); if (hdc) { pv = pe->GetValue(Element::ContentProp, PI_Local); if (pv) { hbm = (HBITMAP)pv->GetImage(false); } if (hbm) { _dwFlagFrame = dwFrame; if (_dwFlagFrame >= MAX_FLAG_FRAMES) { _dwFlagFrame = 0; } HBITMAP hbmSave = (HBITMAP)SelectObject(hdc, hbm); BitBlt(hdc, 0, 0, 137, 86, _hdcAnimation, 0, 86 * _dwFlagFrame,SRCCOPY); SelectObject(hdc, hbmSave); HGADGET hGad = pe->GetDisplayNode(); if (hGad) { InvalidateGadget(hGad); } } if (pv) { pv->Release(); } DeleteDC(hdc); } } #endif } //////////////////////////////////////// // // LogonFrame::Initialize // // Initialize the LogonFrame, create the notification window that is used by SHGina for // sending messages to logonui. Set initial state, etc. // // RETURNS // S_OK if everything works out. Failure HRESULT if not. You are pretty much hosed if this fails // ///////////////////////////////////////// HRESULT LogonFrame::Initialize(HWND hParent, BOOL fDblBuffer, UINT nCreate) { // Zero-init members _peAccountList = NULL; _peViewer = NULL; _peRightPanel = NULL; _peLeftPanel = NULL; _pbPower = NULL; _pbUndock = NULL; _peHelp = NULL; _peMsgArea = NULL; _peLogoArea = NULL; _pParser = NULL; _hwndNotification = NULL; _nStatusID = 0; _fPreStatusLock = FALSE; _nAppState = LAS_PreStatus; _pnhh = NULL; _fListAvailable = FALSE; _pvHotList = NULL; _pvList = NULL; _hdcAnimation = NULL; _dwFlagFrame = 0; _nColorDepth = 0; // Set up notification window _hwndNotification = CreateWindowEx(0, TEXT("LogonWnd"), TEXT("Logon"), WS_OVERLAPPED, 0, 0, 10, 10, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); if (SUCCEEDED(CoCreateInstance(CLSID_ShellLogonStatusHost, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILogonStatusHost, &g_pILogonStatusHost)))) { g_pILogonStatusHost->Initialize(GetModuleHandleW(NULL), _hwndNotification); } // In status (pre) state SetState(LAS_PreStatus); // Do base class initialization HRESULT hr; HDC hDC = NULL; hr = HWNDElement::Initialize(hParent, fDblBuffer ? true : false, nCreate); if (FAILED(hr)) { return hr; goto Failure; } if (!g_fNoAnimations) { // Initialize hDC = GetDC(NULL); _nDPI = GetDeviceCaps(hDC, LOGPIXELSY); _nColorDepth = GetDeviceCaps(hDC, BITSPIXEL); ReleaseDC(NULL, hDC); #ifdef ANIMATE_FLAG hDC = GetDC(hParent); _hdcAnimation = CreateCompatibleDC(hDC); if (_hdcAnimation) { _hbmpFlags = (HBITMAP)LoadImage(GetModuleHandleW(NULL), MAKEINTRESOURCE(IDB_FLAGSTRIP), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); if (_hbmpFlags) { HBITMAP hbmOld = (HBITMAP)SelectObject(_hdcAnimation, _hbmpFlags); DeleteObject(hbmOld); } else { DeleteDC(_hdcAnimation); _hdcAnimation = NULL; } } ReleaseDC(hParent, hDC); #endif } hr = SetActive(AE_MouseAndKeyboard); if (FAILED(hr)) goto Failure; return S_OK; Failure: return hr; } LogonFrame::~LogonFrame() { if (_pvHotList) _pvHotList->Release(); if (_pvList) _pvList->Release(); if (_hdcAnimation) DeleteDC(_hdcAnimation); g_plf = NULL; } // Tree is ready. Upon failure, exit which will casuse the app to shutdown HRESULT LogonFrame::OnTreeReady(Parser* pParser) { HRESULT hr; // Cache _pParser = pParser; // Cache important descendents _peAccountList = (Selector*)FindDescendent(StrToID(L"accountlist")); DUIAssert(_peAccountList, "Cannot find account list, check the UI file"); if (_peAccountList == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peLeftPanel = (Element*)FindDescendent(StrToID(L"leftpanel")); DUIAssert(_peLeftPanel, "Cannot find left panel, check the UI file"); if (_peLeftPanel == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peViewer = (ScrollViewer*)FindDescendent(StrToID(L"scroller")); DUIAssert(_peViewer, "Cannot find scroller list, check the UI file"); if (_peViewer == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peRightPanel = (Selector*)FindDescendent(StrToID(L"rightpanel")); DUIAssert(_peRightPanel, "Cannot find account list, check the UI file"); if (_peRightPanel == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peLogoArea = (Element*)FindDescendent(StrToID(L"logoarea")); DUIAssert(_peLogoArea, "Cannot find logo area, check the UI file"); if (_peLogoArea == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peMsgArea = (Element*)FindDescendent(StrToID(L"msgarea")); DUIAssert(_peMsgArea, "Cannot find welcome area, check the UI file"); if (_peMsgArea == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _pbPower = (Button*)FindDescendent(StrToID(L"power")); DUIAssert(_pbPower, "Cannot find power button, check the UI file"); if (_pbPower == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _pbUndock = (Button*)FindDescendent(StrToID(L"undock")); DUIAssert(_pbUndock, "Cannot find undock button, check the UI file"); if (_pbUndock == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peHelp = (Button*)FindDescendent(StrToID(L"help")); DUIAssert(_peHelp, "Cannot find help text, check the UI file"); if (_peHelp == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _peOptions = FindDescendent(StrToID(L"options")); DUIAssert(_peOptions, "Cannot find account list, check the UI file"); if (_peOptions == NULL) { hr = E_OUTOFMEMORY; goto Failure; } // check for small window or low color cases and hide some elements that will look bad then. HWND hwnd = _pnhh->GetHWND(); RECT rcClient; Element *pEle; HDC hDC = GetDC(hwnd); _nColorDepth = GetDeviceCaps(hDC, BITSPIXEL); _pvHotList = _pParser->GetSheet(L"hotaccountlistss"); _pvList = _pParser->GetSheet(L"accountlistss"); ReleaseDC(hwnd, hDC); GetClientRect(hwnd, &rcClient); if (RECTWIDTH(rcClient) < 780 || _nColorDepth <= 8) { //no animations g_fNoAnimations = true; // remove the clouds pEle = FindDescendent(StrToID(L"contentcontainer")); if (pEle) { pEle->RemoveLocalValue(ContentProp); if (_nColorDepth <= 8) { pEle->SetBackgroundColor(ORGB (96,128,255)); } } if (_nColorDepth <= 8) { pEle = FindDescendent(StrToID(L"product")); if (pEle) { pEle->SetBackgroundColor(ORGB (96,128,255)); } } } _peViewer->AddListener(this); _peAccountList->AddListener(this); // Setup frame labels SetPowerButtonLabel(LoadResString(IDS_POWER)); SetUndockButtonLabel(LoadResString(IDS_UNDOCK)); ShowLogoArea(); HideWelcomeArea(); return S_OK; Failure: return hr; } // Set the title element (welcome, please wait..) by string resource id void LogonFrame::SetTitle(UINT uRCID) { WCHAR sz[1024]; ZeroMemory(&sz, sizeof(sz)); if (_nStatusID != uRCID) { #ifdef DBG int cRead = 0; cRead = LoadStringW(_pParser->GetHInstance(), uRCID, sz, DUIARRAYSIZE(sz)); DUIAssert(cRead, "Could not locate string resource ID"); #else LoadStringW(_pParser->GetHInstance(), uRCID, sz, ARRAYSIZE(sz)); #endif SetTitle(sz); _nStatusID = uRCID; } } // Set the title element (welcome, please wait..) // slightly more involved because there is the shadow element that // needs to be changed as well void LogonFrame::SetTitle(LPCWSTR pszTitle) { Element *peTitle = NULL, *peShadow = NULL; peTitle= (Button*)FindDescendent(StrToID(L"welcome")); DUIAssert(peTitle, "Cannot find title text, check the UI file"); if (peTitle) { peShadow= (Button*)FindDescendent(StrToID(L"welcomeshadow")); DUIAssert(peShadow, "Cannot find title shadow text, check the UI file"); } if (peTitle && peShadow) { peTitle->SetContentString(pszTitle); peShadow->SetContentString(pszTitle); } } // Generic events void LogonFrame::OnEvent(Event* pEvent) { if (pEvent->nStage == GMF_BUBBLED) // Bubbled events { g_pErrorBalloon.HideToolTip(); if (pEvent->uidType == Button::Click) { if (pEvent->peTarget == _pbPower) { // Power button pressed OnPower(); pEvent->fHandled = true; return; } else if (pEvent->peTarget == _pbUndock) { // Undock button pressed OnUndock(); pEvent->fHandled = true; return; } } } HWNDElement::OnEvent(pEvent); } // PropertyChanged listened events from various descendents // Swap out property sheets for account list based on state of the list void LogonFrame::OnListenedPropertyChanged(Element* peFrom, PropertyInfo* ppi, int iIndex, Value* pvOld, Value* pvNew) { UNREFERENCED_PARAMETER(pvOld); UNREFERENCED_PARAMETER(pvNew); { if (((peFrom == _peAccountList) && IsProp(Selector::Selection)) || ((peFrom == _peViewer) && (IsProp(MouseWithin) || IsProp(KeyWithin)))) { bool bHot = false; // Move to "hot" account list sheet if mouse or key is within viewer or an item is selected if (GetState() == LAS_PreStatus || GetState() == LAS_Logon) { bHot = _peViewer->GetMouseWithin() || _peAccountList->GetSelection(); } if (!g_fNoAnimations) { KillFlagAnimation(); _peAccountList->SetValue(SheetProp, PI_Local, bHot ? _pvHotList : _pvList); } } } } // System events // Watch for input events. If the frame receives them, unselect the list and set keyfocus to it void LogonFrame::OnInput(InputEvent* pEvent) { if (pEvent->nStage == GMF_DIRECT || pEvent->nStage == GMF_BUBBLED) { if (pEvent->nDevice == GINPUT_KEYBOARD) { KeyboardEvent* pke = (KeyboardEvent*)pEvent; if (pke->nCode == GKEY_DOWN) { switch (pke->ch) { case VK_ESCAPE: g_pErrorBalloon.HideToolTip(); SetKeyFocus(); _peAccountList->SetSelection(NULL); pEvent->fHandled = true; return; case VK_UP: case VK_DOWN: if (UserListAvailable()) { if (_peAccountList->GetSelection() == NULL) { Value* pvChildren; ElementList* peList = _peAccountList->GetChildren(&pvChildren); if (peList) { LogonAccount* peAccount = (LogonAccount*)peList->GetItem(0); if (peAccount) { peAccount->SetKeyFocus(); _peAccountList->SetSelection(peAccount); } } pvChildren->Release(); pEvent->fHandled = true; return; } } break; } } } } HWNDElement::OnInput(pEvent); } void LogonFrame::OnPropertyChanged(PropertyInfo* ppi, int iIndex, Value* pvOld, Value* pvNew) { if (IsProp(KeyFocused)) { if (pvNew->GetBool()) { // Unselect items from account list if pressed on background _peAccountList->SetSelection(NULL); } } HWNDElement::OnPropertyChanged(ppi, iIndex, pvOld, pvNew); } Element* LogonFrame::GetAdjacent(Element* peFrom, int iNavDir, NavReference const* pnr, bool bKeyable) { Element* peFound = HWNDElement::GetAdjacent(peFrom, iNavDir, pnr, bKeyable); if ((peFound == this)) { // Don't let the frame show up in the tab order. Just repeat the search when we encounter the frame return HWNDElement::GetAdjacent(this, iNavDir, pnr, bKeyable); } return peFound; } // Add an account to the frame list HRESULT LogonFrame::AddAccount(LPCWSTR pszPicture, BOOL fPicRes, LPCWSTR pszName, LPCWSTR pszUsername, LPCWSTR pszHint, BOOL fLoggedOn, OUT LogonAccount **ppla) { HRESULT hr; LogonAccount* pla = NULL; if (!_pParser) { hr = E_FAIL; goto Failure; } // Build up an account and insert into selection list hr = _pParser->CreateElement(L"accountitem", NULL, (Element**)&pla); if (FAILED(hr)) goto Failure; hr = pla->OnTreeReady(pszPicture, fPicRes, pszName, pszUsername, pszHint, fLoggedOn, GetHInstance()); if (FAILED(hr)) goto Failure; hr = _peAccountList->Add(pla); if (FAILED(hr)) goto Failure; if (pla) { SetElementAccessability(pla, true, ROLE_SYSTEM_LISTITEM, pszUsername); } if (_nColorDepth <= 8) { pla->SetBackgroundColor(ORGB (96,128,255)); Element *pEle; pEle = pla->FindDescendent(StrToID(L"userpane")); if (pEle) { pEle->SetBorderColor(ORGB (96,128,255)); } } if (ppla) *ppla = pla; return S_OK; Failure: return hr; } // Passed authentication, log user on HRESULT LogonFrame::OnLogUserOn(LogonAccount* pla) { StartDefer(); #ifdef GADGET_ENABLE_GDIPLUS // Disable status so that it can't be clicked on anymore pla->DisableStatus(0); pla->DisableStatus(1); // Clear list of logon accounts except the one logging on Value* pvChildren; ElementList* peList = _peAccountList->GetChildren(&pvChildren); if (peList) { LogonAccount* peAccount; for (UINT i = 0; i < peList->GetSize(); i++) { peAccount = (LogonAccount*)peList->GetItem(i); if (peAccount != pla) { peAccount->SetLogonState(LS_Denied); } else { peAccount->SetLogonState(LS_Granted); peAccount->InsertStatus(0); peAccount->RemoveStatus(1); } // Account account items are disabled peAccount->SetEnabled(false); } } pvChildren->Release(); FxLogUserOn(pla); // Set frame status SetStatus(LoadResString(IDS_LOGGINGON)); #else // Set keyfocus back to frame so it isn't pushed anywhere when controls are removed. // This will also cause a remove of the password panel from the current account SetKeyFocus(); // Disable status so that it can't be clicked on anymore pla->DisableStatus(0); pla->DisableStatus(1); // Clear list of logon accounts except the one logging on Value* pvChildren; ElementList* peList = _peAccountList->GetChildren(&pvChildren); if (peList) { LogonAccount* peAccount; for (UINT i = 0; i < peList->GetSize(); i++) { peAccount = (LogonAccount*)peList->GetItem(i); if (peAccount != pla) { peAccount->SetLayoutPos(LP_None); peAccount->SetLogonState(LS_Denied); } else { peAccount->SetLogonState(LS_Granted); peAccount->InsertStatus(0); peAccount->RemoveStatus(1); } // Account account items are disabled peAccount->SetEnabled(false); } } pvChildren->Release(); // Hide option buttons HidePowerButton(); HideUndockButton(); // Set frame status SetStatus(LoadResString(IDS_LOGGINGON)); _peViewer->RemoveListener(this); _peAccountList->RemoveListener(this); #endif EndDefer(); return S_OK; } HRESULT LogonFrame::OnPower() { DUITrace("LogonUI: LogonFrame::OnPower()\n"); TurnOffComputer(); return S_OK; } HRESULT LogonFrame::OnUndock() { DUITrace("LogonUI: LogonFrame::OnUndock()\n"); UndockComputer(); return S_OK; } //////////////////////////////////////// // // LogonFrame::SetButtonLabels // // If there is a friendly name of the computer stored in the computer name description, // grab it and change the "Turn off" and "Undock" options to include the compute rname // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::SetButtonLabels() { WCHAR szComputerName[MAX_COMPUTERDESC_LENGTH + 1] = {0}; DWORD cchComputerName = MAX_COMPUTERDESC_LENGTH + 1; if ( _pbPower && SUCCEEDED(SHGetComputerDisplayName(NULL, SGCDNF_DESCRIPTIONONLY, szComputerName, cchComputerName))) { WCHAR szCommand[MAX_COMPUTERDESC_LENGTH + 50], szRes[50]; LoadStringW(g_plf->GetHInstance(), IDS_POWERNAME, szRes, DUIARRAYSIZE(szRes)); wsprintf(szCommand, szRes, szComputerName); SetPowerButtonLabel(szCommand); LoadStringW(g_plf->GetHInstance(), IDS_UNDOCKNAME, szRes, DUIARRAYSIZE(szRes)); wsprintf(szCommand, szRes, szComputerName); SetUndockButtonLabel(szCommand); } } //////////////////////////////////////////////////////// // Logon Application State Transitions //////////////////////////////////////// // // LogonFrame::EnterPreStatusMode // // SHGina has sent a message telling logonui to enter the pre-status // mode or we are starting up in status mode. Hide items that should // not be displayed when in this state (power off, account list, user // instructions, etc). // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::EnterPreStatusMode(BOOL fLock) { // If currently locked, ignore call if (IsPreStatusLock()) { DUIAssert(!fLock, "Receiving a lock while already within pre-Status lock"); return; } if (fLock) { LogonAccount *pAccount; // Entering pre-Status mode with "lock", cannot exit to logon state without an unlock _fPreStatusLock = TRUE; pAccount = static_cast(_peAccountList->GetSelection()); if (pAccount != NULL) { lstrcpynW(szLastSelectedName, pAccount->GetUsername(), ARRAYSIZE(szLastSelectedName)); } } if (GetState() == LAS_Hide) { _pnhh->ShowWindow(); SetWindowPos(_pnhh->GetHWND(), NULL, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SWP_NOMOVE | SWP_NOZORDER ); } StartDefer(); SetKeyFocus(); // Removes selection HidePowerButton(); HideUndockButton(); ShowLogoArea(); HideWelcomeArea(); HideAccountPanel(); Element *pe; pe = FindDescendent(StrToID(L"instruct")); DUIAssertNoMsg(pe); pe->SetVisible(FALSE); SetStatus(LoadResString(IDS_WINDOWSNAME)); EndDefer(); // Set state SetState(LAS_PreStatus); } //////////////////////////////////////// // // LogonFrame::EnterLogonMode // // SHGina has sent a message telling logonui to enter the logon mode. // this means to build and display the user list. If we are re-entering // logon mode from another mode, the user list will already exist and we // should just set everything back to the pending state. // // EnterLogonMode also sets up the undock and power off buttons based on // whether those options are allowed. // // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::EnterLogonMode(BOOL fUnLock) { // If currently locked, ignore call if not to unlock if (IsPreStatusLock()) { if (fUnLock) { // Exiting pre-Status mode lock _fPreStatusLock = FALSE; } else return; } else { DUIAssert(!fUnLock, "Receiving an unlock while not within pre-Status lock"); } DUIAssert(GetState() != LAS_Hide, "Cannot enter logon state from hidden state"); ResetTheme(); Element* pe; LogonAccount* plaAutoSelect = NULL; StartDefer(); PokeComCtl32(); // Retrieve data from backend if not populated if (UserListAvailable()) { ResetUserList(); } else { // Cache password field atoms for quicker identification (static) LogonAccount::idPwdGo = AddAtomW(L"go"); LogonAccount::idPwdInfo = AddAtomW(L"info"); // Create password panel Element* pePwdPanel; _pParser->CreateElement(L"passwordpanel", NULL, &pePwdPanel); DUIAssert(pePwdPanel, "Can't create password panel"); // Cache password panel edit control Edit* pePwdEdit = (Edit*)pePwdPanel->FindDescendent(StrToID(L"password")); DUIAssert(pePwdPanel, "Can't create password edit control"); // Cache password panel info button Button* pbPwdInfo = (Button*)pePwdPanel->FindDescendent(StrToID(L"info")); DUIAssert(pePwdPanel, "Can't create password info button"); // Cache password panel keyboard element Element* peKbdIcon = (Button*)pePwdPanel->FindDescendent(StrToID(L"keyboard")); DUIAssert(pePwdPanel, "Can't create password keyboard icon"); LogonAccount::InitPasswordPanel(pePwdPanel, pePwdEdit, pbPwdInfo, peKbdIcon ); } BuildAccountList(this, &plaAutoSelect); if (szLastSelectedName[0] != L'\0') { LogonAccount *pAccount; pAccount = InternalFindNamedUser(szLastSelectedName); if (pAccount != NULL) { plaAutoSelect = pAccount; } szLastSelectedName[0] = L'\0'; } if (IsShutdownAllowed()) { ShowPowerButton(); } else { HidePowerButton(); } if (IsUndockAllowed()) { ShowUndockButton(); } else { HideUndockButton(); } pe = FindDescendent(StrToID(L"instruct")); DUIAssertNoMsg(pe); pe->SetVisible(TRUE); pe = FindDescendent(StrToID(L"product")); DUIAssertNoMsg(pe); pe->StopAnimation(ANI_AlphaType); pe->RemoveLocalValue(BackgroundProp); // Account list viewer ShowAccountPanel(); SetTitle(IDS_WELCOME); SetStatus(LoadResString(IDS_BEGIN)); if (!plaAutoSelect) { SetKeyFocus(); } EndDefer(); // Set state SetState(LAS_Logon); // Set auto-select item, if exists if (plaAutoSelect) { plaAutoSelect->SetKeyFocus(); _peAccountList->SetSelection(plaAutoSelect); } SetButtonLabels(); SetForegroundWindow(_pnhh->GetHWND()); } //////////////////////////////////////// // // LogonFrame::EnterPostStatusMode // // SHGina has sent a message telling logonui that the authentication has succeeded // and we should now go into the post status mode. LogonFrame::OnLogUserOn has already // started the animations for this. // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::EnterPostStatusMode() { // Set state SetState(LAS_PostStatus); Element *pe; pe = FindDescendent(StrToID(L"instruct")); DUIAssertNoMsg(pe); pe->SetVisible(FALSE); //animation was started in OnLogUserOn ShowWelcomeArea(); HideLogoArea(); } //////////////////////////////////////// // // LogonFrame::EnterHideMode // // SHGina has sent a message telling logonui to hide. // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::EnterHideMode() { // Set state SetState(LAS_Hide); if (_pnhh) { _pnhh->HideWindow(); } } //////////////////////////////////////// // // LogonFrame::EnterDoneMode // // SHGina has sent a message telling logonui to exit. // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::EnterDoneMode() { // Set state SetState(LAS_Done); if (_pnhh) { _pnhh->DestroyWindow(); } } //////////////////////////////////////// // // LogonFrame::ResetUserList // // Delete all of the users in the user list so that it can be rebuilt // // RETURNS // nothing // ///////////////////////////////////////// void LogonFrame::ResetUserList() { if (UserListAvailable()) { // reset the candidate to NULL LogonAccount::ClearCandidate(); // remove of the password panel from the current account (if any) SetKeyFocus(); //fix up the existing list to get us back into logon mode Value* pvChildren; ElementList* peList = _peAccountList->GetChildren(&pvChildren); if (peList) { LogonAccount* peAccount; for (int i = peList->GetSize() - 1; i >= 0; i--) { peAccount = (LogonAccount*)peList->GetItem(i); peAccount->Destroy(); } } pvChildren->Release(); } } //////////////////////////////////////// // // LogonFrame::InteractiveLogonRequest // // SHGina has sent an InteractiveLogonRequest. We should look for the user // that was passed in and if found, try to log them in. // // RETURNS // LRESULT indicating success or failure of finding htem and logging them in. // ///////////////////////////////////////// LRESULT LogonFrame::InteractiveLogonRequest(LPCWSTR pszUsername, LPCWSTR pszPassword) { LRESULT lResult = 0; LogonAccount *pla; pla = FindNamedUser(pszUsername); if (pla) { if (pla->OnAuthenticateUser(pszPassword)) { lResult = ERROR_SUCCESS; } else { lResult = ERROR_ACCESS_DENIED; } } return(lResult); } //////////////////////////////////////// // // LogonFrame::InternalFindNamedUser // // Find a user in the LogonAccount list with the // provided username (logon name). // // RETURNS // The LogonAccount* for the indicated user or NULL if // not found // ///////////////////////////////////////// LogonAccount* LogonFrame::InternalFindNamedUser(LPCWSTR pszUsername) { LogonAccount *plaResult = NULL; Value* pvChildren; ElementList* peList = _peAccountList->GetChildren(&pvChildren); if (peList) { for (UINT i = 0; i < peList->GetSize(); i++) { DUIAssert(peList->GetItem(i)->GetClassInfo() == LogonAccount::Class, "Account list must contain LogonAccount objects"); LogonAccount* pla = (LogonAccount*)peList->GetItem(i); if (pla) { if (lstrcmpi(pla->GetUsername(), pszUsername) == 0) { plaResult = pla; break; } } } } pvChildren->Release(); return plaResult; } //////////////////////////////////////// // // LogonFrame::FindNamedUser // // Find a user in the LogonAccount list with the // provided username (logon name). // // RETURNS // The LogonAccount* for the indicated user or NULL if // not found // ///////////////////////////////////////// LogonAccount *LogonFrame::FindNamedUser(LPCWSTR pszUsername) { // Early out if: no user list available // not in logon mode (showing user list) if (!UserListAvailable() || (GetState() != LAS_Logon)) { return NULL; } else { return(InternalFindNamedUser(pszUsername)); } } //////////////////////////////////////// // // LogonFrame::UpdateUserStatus // // Iterate the list of user accounts and call LogonAccount::UpdateNotifications // for each one. This will result in them updating the unread mail count and // logon status for each of the logon accounts. // Pass fRefreshAll through to UpdateApplications // ///////////////////////////////////////// void LogonFrame::UpdateUserStatus(BOOL fRefreshAll) { Value* pvChildren; static fUpdating = false; // Early out if: no user list available // not in logon mode (showing user list) if (!UserListAvailable() || (GetState() != LAS_Logon) || fUpdating) return; fUpdating = true; StartDefer(); ElementList* peList = _peAccountList->GetChildren(&pvChildren); if (peList) { for (UINT i = 0; i < peList->GetSize(); i++) { DUIAssert(peList->GetItem(i)->GetClassInfo() == LogonAccount::Class, "Account list must contain LogonAccount objects"); LogonAccount* pla = (LogonAccount*)peList->GetItem(i); if (pla) { pla->UpdateNotifications(fRefreshAll); } } } if (IsUndockAllowed()) { ShowUndockButton(); } else { HideUndockButton(); } pvChildren->Release(); EndDefer(); fUpdating = false; } //////////////////////////////////////// // // LogonFrame::SelectUser // // // ///////////////////////////////////////// void LogonFrame::SelectUser(LPCWSTR pszUsername) { LogonAccount *pla; pla = FindNamedUser(pszUsername); if (pla != NULL) { pla->OnAuthenticatedUser(); } else { LogonAccount::ClearCandidate(); EnterPostStatusMode(); HidePowerButton(); HideUndockButton(); HideAccountPanel(); } } //////////////////////////////////////// // // LogonFrame::Resize // // // ///////////////////////////////////////// void LogonFrame::Resize() { RECT rc; SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0); SetWindowPos(_pnhh->GetHWND(), NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); } //////////////////////////////////////// // // LogonFrame::SetAnimations // // // ///////////////////////////////////////// void LogonFrame::SetAnimations(BOOL fAnimations) { g_fNoAnimations = !fAnimations; if (fAnimations) { EnableAnimations(); } else { DisableAnimations(); } } //////////////////////////////////////////////////////// // Property definitions //////////////////////////////////////////////////////// // ClassInfo (must appear after property definitions) // Define class info with type and base type, set static class pointer IClassInfo* LogonFrame::Class = NULL; HRESULT LogonFrame::Register() { return ClassInfo::Register(L"LogonFrame", NULL, 0); } //////////////////////////////////////////////////////// // HRESULT LogonAccountList::Create(OUT Element** ppElement) { *ppElement = NULL; LogonAccountList* plal = HNew(); if (!plal) return E_OUTOFMEMORY; HRESULT hr = plal->Initialize(); if (FAILED(hr)) { plal->Destroy(); return hr; } *ppElement = plal; return S_OK; } void LogonAccountList::OnPropertyChanged(PropertyInfo* ppi, int iIndex, Value* pvOld, Value* pvNew) { #ifdef GADGET_ENABLE_GDIPLUS if (IsProp(MouseWithin)) { if (pvNew->GetBool()) FxMouseWithin(fdIn); else FxMouseWithin(fdOut); } #endif // GADGET_ENABLE_GDIPLUS Selector::OnPropertyChanged(ppi, iIndex, pvOld, pvNew); } //////////////////////////////////////////////////////// // Property definitions //////////////////////////////////////////////////////// // ClassInfo (must appear after property definitions) // Define class info with type and base type, set static class pointer IClassInfo* LogonAccountList::Class = NULL; HRESULT LogonAccountList::Register() { return ClassInfo::Register(L"LogonAccountList", NULL, 0); } //////////////////////////////////////////////////////// // // LogonAccount // //////////////////////////////////////////////////////// ATOM LogonAccount::idPwdGo = NULL; ATOM LogonAccount::idPwdInfo = NULL; Element* LogonAccount::_pePwdPanel = NULL; Edit* LogonAccount::_pePwdEdit = NULL; Button* LogonAccount::_pbPwdInfo = NULL; Element* LogonAccount::_peKbdIcon = NULL; LogonAccount* LogonAccount::_peCandidate = NULL; HRESULT LogonAccount::Create(OUT Element** ppElement) { *ppElement = NULL; LogonAccount* pla = HNew(); if (!pla) return E_OUTOFMEMORY; HRESULT hr = pla->Initialize(); if (FAILED(hr)) { pla->Destroy(); return hr; } *ppElement = pla; return S_OK; } HRESULT LogonAccount::Initialize() { // Zero-init members _pbStatus[0] = NULL; _pbStatus[1] = NULL; _pvUsername = NULL; _pvHint = NULL; _fPwdNeeded = (BOOL)-1; // uninitialized _fLoggedOn = FALSE; _fHasPwdPanel = FALSE; // Do base class initialization HRESULT hr = Button::Initialize(AE_MouseAndKeyboard); if (FAILED(hr)) goto Failure; // Initialize // TODO: Additional LogonAccount initialization code here return S_OK; Failure: return hr; } LogonAccount::~LogonAccount() { // Free resources if (_pvUsername) { _pvUsername->Release(); _pvUsername = NULL; } if (_pvHint) { _pvHint->Release(); _pvHint = NULL; } // TODO: Account destruction cleanup } void LogonAccount::SetStatus(UINT nLine, LPCWSTR psz) { if (psz) { _pbStatus[nLine]->SetContentString(psz); SetElementAccessability(_pbStatus[nLine], true, ROLE_SYSTEM_LINK, psz); } } // Tree is ready HRESULT LogonAccount::OnTreeReady(LPCWSTR pszPicture, BOOL fPicRes, LPCWSTR pszName, LPCWSTR pszUsername, LPCWSTR pszHint, BOOL fLoggedOn, HINSTANCE hInst) { HRESULT hr; Element* pePicture = NULL; Element* peName = NULL; Value* pv = NULL; StartDefer(); // Cache important descendents _pbStatus[0] = (Button*)FindDescendent(StrToID(L"status0")); DUIAssert(_pbStatus[0], "Cannot find account list, check the UI file"); if (_pbStatus[0] == NULL) { hr = E_OUTOFMEMORY; goto Failure; } _pbStatus[1] = (Button*)FindDescendent(StrToID(L"status1")); DUIAssert(_pbStatus[1], "Cannot find account list, check the UI file"); if (_pbStatus[1] == NULL) { hr = E_OUTOFMEMORY; goto Failure; } // Locate descendents and populate pePicture = FindDescendent(StrToID(L"picture")); DUIAssert(pePicture, "Cannot find account list, check the UI file"); if (pePicture == NULL) { hr = E_OUTOFMEMORY; goto Failure; } // CreateGraphic handles NULL bitmaps pv = Value::CreateGraphic(pszPicture, GRAPHIC_NoBlend, 0, 0, 0, (fPicRes) ? hInst : 0); if (pv) { // Our preferred size is 1/2 inch (36pt) square. USHORT cx = (USHORT)LogonFrame::PointToPixel(36); USHORT cy = cx; Graphic* pg = pv->GetGraphic(); // If it's not square, scale the smaller dimension // to maintain the aspect ratio. if (pg->cx > pg->cy) { cy = (USHORT)MulDiv(cx, pg->cy, pg->cx); } else if (pg->cy > pg->cx) { cx = (USHORT)MulDiv(cy, pg->cx, pg->cy); } // Did anything change? if (cx != pg->cx || cy != pg->cy) { // Reload the graphic pv->Release(); pv = Value::CreateGraphic(pszPicture, GRAPHIC_NoBlend, 0, cx, cy, (fPicRes) ? hInst : 0); } } if (!pv) { // if we can't get the picture, use a default one pv = Value::CreateGraphic(MAKEINTRESOURCEW(IDB_USER0), GRAPHIC_NoBlend, 0, (USHORT)LogonFrame::PointToPixel(36), (USHORT)LogonFrame::PointToPixel(36), hInst); if (!pv) { hr = E_OUTOFMEMORY; goto Failure; } } hr = pePicture->SetValue(Element::ContentProp, PI_Local, pv); if (FAILED(hr)) goto Failure; pv->Release(); pv = NULL; // Name peName = FindDescendent(StrToID(L"username")); DUIAssert(peName, "Cannot find account list, check the UI file"); if (peName == NULL) { hr = E_OUTOFMEMORY; goto Failure; } hr = peName->SetContentString(pszName); if (FAILED(hr)) goto Failure; // Store members, will be released in destructor if (pszUsername) { _pvUsername = Value::CreateString(pszUsername); if (!_pvUsername) { hr = E_OUTOFMEMORY; goto Failure; } } if (pszHint) { _pvHint = Value::CreateString(pszHint); if (!_pvHint) { hr = E_OUTOFMEMORY; goto Failure; } } _fLoggedOn = fLoggedOn; EndDefer(); return S_OK; Failure: EndDefer(); if (pv) pv->Release(); return hr; } // Generic events void LogonAccount::OnEvent(Event* pEvent) { if (pEvent->nStage == GMF_DIRECT) // Direct events { // Watch for click events initiated by LogonAccounts only // if we are not logging someone on if (pEvent->uidType == Button::Click) { if (pEvent->peTarget == this) { if (IsPasswordBlank()) { // No password needed, attempt logon OnAuthenticateUser(); } pEvent->fHandled = true; return; } } } else if (pEvent->nStage == GMF_BUBBLED) // Bubbled events { if (pEvent->uidType == Button::Click) { if (idPwdGo && (pEvent->peTarget->GetID() == idPwdGo)) { // Attempt logon OnAuthenticateUser(); pEvent->fHandled = true; return; } else if (idPwdInfo && (pEvent->peTarget->GetID() == idPwdInfo)) { // Retrieve hint OnHintSelect(); pEvent->fHandled = true; return; } else if (pEvent->peTarget == _pbStatus[0]) { // Retrieve status info OnStatusSelect(0); pEvent->fHandled = true; return; } else if (pEvent->peTarget == _pbStatus[1]) { // Retrieve status info OnStatusSelect(1); pEvent->fHandled = true; return; } } else if (pEvent->uidType == Edit::Enter) { if (_pePwdEdit && pEvent->peTarget == _pePwdEdit) { // Attempt logon OnAuthenticateUser(); pEvent->fHandled = true; return; } } } Button::OnEvent(pEvent); } // System events void LogonAccount::OnInput(InputEvent* pEvent) { KeyboardEvent* pke = (KeyboardEvent*)pEvent; if (pke->nDevice == GINPUT_KEYBOARD && pke->nCode == GKEY_DOWN) { g_pErrorBalloon.HideToolTip(); } LayoutCheckHandler(LAYOUT_DEF_USER); Button::OnInput(pEvent); } void LogonAccount::OnPropertyChanged(PropertyInfo* ppi, int iIndex, Value* pvOld, Value* pvNew) { #ifdef GADGET_ENABLE_GDIPLUS // MouseWithin must be before Selected if (IsProp(MouseWithin)) { if (pvNew->GetBool()) FxMouseWithin(fdIn); else FxMouseWithin(fdOut); } #endif if (IsProp(Selected)) { if (pvNew->GetBool()) InsertPasswordPanel(); else RemovePasswordPanel(); } Button::OnPropertyChanged(ppi, iIndex, pvOld, pvNew); } BOOL LogonAccount::IsPasswordBlank() { if (_fPwdNeeded == (BOOL)-1) { // assume a password is needed _fPwdNeeded = TRUE; if (_pvUsername) { LPTSTR pszUsername; pszUsername = _pvUsername->GetString(); if (pszUsername) { ILogonUser* pUser; if (SUCCEEDED(GetLogonUserByLogonName(pszUsername, &pUser))) { VARIANT_BOOL vbPwdNeeded; if (RunningInWinlogon() && SUCCEEDED(pUser->get_passwordRequired(&vbPwdNeeded)) && (vbPwdNeeded == VARIANT_FALSE)) { _fPwdNeeded = FALSE; } pUser->Release(); } } } } return (_fPwdNeeded == FALSE); } HRESULT LogonAccount::InsertPasswordPanel() { HRESULT hr; // If already have it, or no password is available, or logon state is not pending if (_fHasPwdPanel || IsPasswordBlank() || (GetLogonState() != LS_Pending)) goto Done; StartDefer(); // Add password panel hr = Add(_pePwdPanel); if (FAILED(hr)) { EndDefer(); goto Failure; } SetElementAccessability(_pePwdEdit, true, ROLE_SYSTEM_STATICTEXT, _pvUsername->GetString()); _fHasPwdPanel = TRUE; #ifdef GADGET_ENABLE_GDIPLUS // Ensure that the Edit control is visible ShowEdit(); #endif // Hide hint button if no hint provided if (_pvHint && *(_pvHint->GetString()) != NULL) _pbPwdInfo->SetVisible(true); else _pbPwdInfo->SetVisible(false); // Hide status text (do not remove or insert) HideStatus(0); HideStatus(1); LayoutCheckHandler(LAYOUT_DEF_USER); // Push focus to edit control _pePwdEdit->SetKeyFocus(); EndDefer(); Done: return S_OK; Failure: return hr; } HRESULT LogonAccount::RemovePasswordPanel() { HRESULT hr; if (!_fHasPwdPanel) goto Done; StartDefer(); // Remove password panel hr = Remove(_pePwdPanel); if (FAILED(hr)) { EndDefer(); goto Failure; } // Clear out edit control _pePwdEdit->SetContentString(L""); UnSubClassTheEditBox(_pePwdEdit->GetHWND()); // Provide for trap of the TTN_LINKCLICK event // Unhide status text ShowStatus(0); ShowStatus(1); _fHasPwdPanel = FALSE; EndDefer(); Done: return S_OK; Failure: return hr; } // User has authenticated void LogonAccount::OnAuthenticatedUser() { // On success, log user on _peCandidate = this; g_plf->OnLogUserOn(this); g_plf->EnterPostStatusMode(); } // User is attempting to log on BOOL LogonAccount::OnAuthenticateUser(LPCWSTR pszInPassword) { HRESULT hr; // Logon requested on this account LPCWSTR pszPassword = L""; Value* pv = NULL; ILogonUser *pobjUser; VARIANT_BOOL vbLogonSucceeded = VARIANT_FALSE; WCHAR *pszUsername = _pvUsername->GetString(); if (pszUsername) { if (SUCCEEDED(hr = GetLogonUserByLogonName(pszUsername, &pobjUser))) { if (!IsPasswordBlank()) { if (pszInPassword) { pszPassword = pszInPassword; } else { if (_pePwdEdit) { pszPassword = _pePwdEdit->GetContentString(&pv); if (!pszPassword) pszPassword = L""; if (pv) { pv->Release(); } } } BSTR bstr = SysAllocString(pszPassword); pobjUser->logon(bstr, &vbLogonSucceeded); SysFreeString(bstr); } else { pobjUser->logon(L"", &vbLogonSucceeded); } pobjUser->Release(); } } if (vbLogonSucceeded == VARIANT_TRUE) { OnAuthenticatedUser(); } else { if (pszInPassword == NULL) { ShowPasswordIncorrectMessage(); } } return (vbLogonSucceeded == VARIANT_TRUE); } //////////////////////////////////////// // // LogonAccount::ShowPasswordIncorrectMessage // // Put up the balloon message that says that the password is incorrect. // ///////////////////////////////////////// void LogonAccount::ShowPasswordIncorrectMessage() { TCHAR szError[512], szTitle[128], szAccessible[640]; BOOL fBackupAvailable = false; BOOL fHint = false; DWORD dwResult; g_szUsername[0] = 0; SubClassTheEditBox(_pePwdEdit->GetHWND()); // Provide for trap of the TTN_LINKCLICK event if (0 < lstrlen(_pvUsername->GetString())) { wcscpy(g_szUsername,_pvUsername->GetString()); if (0 == PRQueryStatus(NULL,_pvUsername->GetString(),&dwResult)) { if (0 == dwResult) { fBackupAvailable = TRUE; } } } if (NULL != _pvHint && 0 < lstrlen(_pvHint->GetString())) { fHint = true; } LoadStringW(g_plf->GetHInstance(), IDS_BADPWDTITLE, szTitle, DUIARRAYSIZE(szTitle)); if (!fBackupAvailable && fHint) LoadStringW(g_plf->GetHInstance(), IDS_BADPWDHINT, szError, DUIARRAYSIZE(szError)); else if (fBackupAvailable && !fHint) LoadStringW(g_plf->GetHInstance(), IDS_BADPWDREST, szError, DUIARRAYSIZE(szError)); else if (fBackupAvailable && fHint) LoadStringW(g_plf->GetHInstance(), IDS_BADPWDHINTREST, szError, DUIARRAYSIZE(szError)); else LoadStringW(g_plf->GetHInstance(), IDS_BADPWD, szError, DUIARRAYSIZE(szError)); g_pErrorBalloon.ShowToolTip(GetModuleHandleW(NULL), _pePwdEdit->GetHWND(), szError, szTitle, TTI_ERROR, EB_WARNINGCENTERED | EB_MARKUP, 10000); lstrcpy(szAccessible, szTitle); lstrcat(szAccessible, szError); SetElementAccessability(_pePwdEdit, true, ROLE_SYSTEM_STATICTEXT, szAccessible); _pePwdEdit->RemoveLocalValue(ContentProp); _pePwdEdit->SetKeyFocus(); } //////////////////////////////////////// // // LogonAccount::OnHintSelect // // Put up the balloon message that contains the user's password hint. // ///////////////////////////////////////// void LogonAccount::OnHintSelect() { TCHAR szTitle[128]; DUIAssertNoMsg(_pbPwdInfo); // get the position of the link so we can target the balloon tip correctly POINT pt = {0,0}; CalcBalloonTargetLocation(g_plf->GetNativeHost()->GetHWND(), _pbPwdInfo, &pt); LoadStringW(g_plf->GetHInstance(), IDS_PASSWORDHINTTITLE, szTitle, DUIARRAYSIZE(szTitle)); g_pErrorBalloon.ShowToolTip(GetModuleHandleW(NULL), g_plf->GetHWND(), &pt, _pvHint->GetString(), szTitle, TTI_INFO, EB_WARNINGCENTERED, 10000); SetElementAccessability(_pePwdEdit, true, ROLE_SYSTEM_STATICTEXT, _pvHint->GetString()); _pePwdEdit->SetKeyFocus(); } //////////////////////////////////////// // // LogonAccount::OnStatusSelect // // The user clicked one of the notification links (unread mail, running programs, etc). // Dispatch that click to the right balloon tip display procs // ///////////////////////////////////////// void LogonAccount::OnStatusSelect(UINT nLine) { if (nLine == LASS_Email) { UnreadMailTip(); } else if (nLine == LASS_LoggedOn) { AppRunningTip(); } } //////////////////////////////////////// // // LogonAccount::AppRunningTip // // The user activated the link that shows how many programs are running. Show the tip that // basically says that running lots of programs can show the machine down // ///////////////////////////////////////// void LogonAccount::AppRunningTip() { TCHAR szTitle[256], szTemp[512]; Element* pe = FindDescendent(StrToID(L"username")); DUIAssertNoMsg(pe); Value* pv; LPCWSTR pszDisplayName = pe->GetContentString(&pv); if (!pszDisplayName) pszDisplayName = L""; if (_dwRunningApps == 0) { LoadStringW(g_plf->GetHInstance(), IDS_USERISLOGGEDON, szTemp, DUIARRAYSIZE(szTemp)); wsprintf(szTitle, szTemp, pszDisplayName, _dwRunningApps); } else { LoadStringW(g_plf->GetHInstance(), (_dwRunningApps == 1 ? IDS_USERRUNNINGPROGRAM : IDS_USERRUNNINGPROGRAMS), szTemp, DUIARRAYSIZE(szTemp)); wsprintf(szTitle, szTemp, pszDisplayName, _dwRunningApps); } pv->Release(); // get the position of the link so we can target the balloon tip correctly POINT pt = {0,0}; CalcBalloonTargetLocation(g_plf->GetNativeHost()->GetHWND(), _pbStatus[LASS_LoggedOn], &pt); LoadStringW(g_plf->GetHInstance(), (_dwRunningApps > 0 ? IDS_TOOMANYPROGRAMS : IDS_TOOMANYUSERS), szTemp, DUIARRAYSIZE(szTemp)); g_pErrorBalloon.ShowToolTip(GetModuleHandleW(NULL), g_plf->GetHWND(), &pt, szTemp, szTitle, TTI_INFO, EB_WARNINGCENTERED, 10000); } //////////////////////////////////////// // // LogonAccount::UnreadMailTip // // The user activated the link that shows how many unread email messages they have. // Show the tip that says how many messages each of their email accounts has. // // TODO -- speed this up. its really slow now because each call to SHGina's // ILogonUser::getMailAccountInfo load's the users' hive to get the next account from // the registry. // ///////////////////////////////////////// void LogonAccount::UnreadMailTip() { TCHAR szTitle[128], szMsg[1024], szTemp[512], szRes[128]; HRESULT hr = E_FAIL; ILogonUser *pobjUser = NULL; szMsg[0] = TEXT('\0'); Element* pe = FindDescendent(StrToID(L"username")); DUIAssertNoMsg(pe); Value* pv; LPCWSTR pszDisplayName = pe->GetContentString(&pv); if (!pszDisplayName) pszDisplayName = L""; WCHAR *pszUsername = _pvUsername->GetString(); DWORD dwAccountsAdded = 0; if (pszUsername) { if (SUCCEEDED(hr = GetLogonUserByLogonName(pszUsername, &pobjUser)) && pobjUser) { DWORD i, cMailAccounts; cMailAccounts = 5; for (i = 0; i < cMailAccounts; i++) { UINT cUnread; VARIANT varAcctName = {0}; hr = pobjUser->getMailAccountInfo(i, &varAcctName, &cUnread); if (FAILED(hr)) { break; } if (varAcctName.bstrVal && cUnread > 0) { if (dwAccountsAdded > 0) { lstrcat(szMsg, TEXT("\r\n")); } dwAccountsAdded++; LoadStringW(g_plf->GetHInstance(), IDS_UNREADMAILACCOUNT, szRes, DUIARRAYSIZE(szRes)); wsprintf(szTemp, szRes, varAcctName.bstrVal, cUnread); lstrcat(szMsg, szTemp); } VariantClear(&varAcctName); } pobjUser->Release(); } } LoadStringW(g_plf->GetHInstance(), (_dwUnreadMail == 1 ? IDS_USERUNREADEMAIL : IDS_USERUNREADEMAILS), szTemp, DUIARRAYSIZE(szTemp)); wsprintf(szTitle, szTemp, pszDisplayName, _dwUnreadMail); pv->Release(); // get the position of the link so we can target the balloon tip correctly POINT pt = {0,0}; CalcBalloonTargetLocation(g_plf->GetNativeHost()->GetHWND(), _pbStatus[LASS_Email], &pt); if (szMsg[0] == 0) { LoadStringW(g_plf->GetHInstance(), IDS_UNREADMAILTEMP, szMsg, DUIARRAYSIZE(szMsg)); } g_pErrorBalloon.ShowToolTip(GetModuleHandleW(NULL), g_plf->GetHWND(), &pt, szMsg, szTitle, TTI_INFO, EB_WARNINGCENTERED, 10000); } //////////////////////////////////////// // // LogonAccount::UpdateNotifications // // Update the notification links for this user. Check to see if they are logged on and // if so, find out how many applications they had open when they last switched away. // // Check the unread mail count for users who are logged on or for everyone if fCheckEverything is // true. Checking unread mail counts is slow because it has to load the user's registry hive. // Since no applications will update this value when the user is not logged on, there is no // need to check this when they are not logged on. The exception to this is when we are first // building the list since we need to always load it then, hence the fCheckEverything flag. // ///////////////////////////////////////// void LogonAccount::UpdateNotifications(BOOL fCheckEverything) { HRESULT hr = E_FAIL; ILogonUser *pobjUser = NULL; WCHAR szTemp[1024], sz[1024]; if (_fHasPwdPanel) return; WCHAR *pszUsername = _pvUsername->GetString(); if (pszUsername) { if (SUCCEEDED(hr = GetLogonUserByLogonName(pszUsername, &pobjUser)) && pobjUser) { VARIANT_BOOL vbLoggedOn; VARIANT varUnreadMail; BOOL fLoggedOn; int iUnreadMailCount = 0; DWORD dwProgramsRunning = 0; if (FAILED(pobjUser->get_isLoggedOn(&vbLoggedOn))) { vbLoggedOn = VARIANT_FALSE; } fLoggedOn = (vbLoggedOn == VARIANT_TRUE); if (fLoggedOn) { HKEY hKey; CUserProfile userProfile(pszUsername, NULL); if (ERROR_SUCCESS == RegOpenKeyEx(userProfile, TEXT("SessionInformation"), 0, KEY_QUERY_VALUE, &hKey)) { DWORD dwProgramsRunningSize = sizeof(dwProgramsRunning); RegQueryValueEx(hKey, TEXT("ProgramCount"), NULL, NULL, reinterpret_cast(&dwProgramsRunning), &dwProgramsRunningSize); RegCloseKey(hKey); } } SetRunningApps(dwProgramsRunning); if (fLoggedOn) { InsertStatus(LASS_LoggedOn); if (dwProgramsRunning != 0) { LoadStringW(g_plf->GetHInstance(), (dwProgramsRunning == 1 ? IDS_RUNNINGPROGRAM : IDS_RUNNINGPROGRAMS), szTemp, ARRAYSIZE(szTemp)); wsprintf(sz, szTemp, dwProgramsRunning); SetStatus(LASS_LoggedOn, sz); ShowStatus(LASS_LoggedOn); } else { LoadStringW(g_plf->GetHInstance(), IDS_USERLOGGEDON, szTemp, ARRAYSIZE(szTemp)); SetStatus(LASS_LoggedOn, szTemp); } } else { // if they are not logged on, clean up the logged on text and remove any padding RemoveStatus(LASS_LoggedOn); } if (fLoggedOn || fCheckEverything) { varUnreadMail.uintVal = 0; if (FAILED(pobjUser->get_setting(L"UnreadMail", &varUnreadMail))) { varUnreadMail.uintVal = 0; } iUnreadMailCount = varUnreadMail.uintVal; SetUnreadMail((DWORD)iUnreadMailCount); if (iUnreadMailCount != 0) { InsertStatus(LASS_Email); LoadStringW(g_plf->GetHInstance(), (iUnreadMailCount == 1 ? IDS_UNREADMAIL : IDS_UNREADMAILS), szTemp, ARRAYSIZE(szTemp)); wsprintf(sz, szTemp, iUnreadMailCount); SetStatus(LASS_Email, sz); ShowStatus(LASS_Email); } else { RemoveStatus(LASS_Email); } } pobjUser->Release(); } } } #ifdef GADGET_ENABLE_GDIPLUS void LogonAccount::ShowEdit() { HWND hwndEdit = _pePwdEdit->GetHWND(); HWND hwndHost = ::GetParent(hwndEdit); SetWindowPos(hwndHost, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); EnableWindow(hwndEdit, TRUE); SetFocus(hwndEdit); } void LogonAccount::HideEdit() { HWND hwndEdit = _pePwdEdit->GetHWND(); HWND hwndHost = ::GetParent(hwndEdit); EnableWindow(hwndEdit, FALSE); SetWindowPos(hwndHost, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW); } #endif // GADGET_ENABLE_GDIPLUS //////////////////////////////////////////////////////// // Property definitions //////////////////////////////////////////////////////// // ClassInfo (must appear after property definitions) // LogonState property static int vvLogonState[] = { DUIV_INT, -1 }; static PropertyInfo impLogonStateProp = { L"LogonState", PF_Normal, 0, vvLogonState, NULL, Value::pvIntZero /*LS_Pending*/ }; PropertyInfo* LogonAccount::LogonStateProp = &impLogonStateProp; //////////////////////////////////////////////////////// // ClassInfo (must appear after property definitions) // Class properties static PropertyInfo* _aPI[] = { LogonAccount::LogonStateProp, }; // Define class info with type and base type, set static class pointer IClassInfo* LogonAccount::Class = NULL; HRESULT LogonAccount::Register() { return ClassInfo::Register(L"LogonAccount", _aPI, DUIARRAYSIZE(_aPI)); } //////////////////////////////////////////////////////// // Logon Parser void CALLBACK LogonParseError(LPCWSTR pszError, LPCWSTR pszToken, int dLine) { WCHAR buf[201]; if (dLine != -1) swprintf(buf, L"%s '%s' at line %d", pszError, pszToken, dLine); else swprintf(buf, L"%s '%s'", pszError, pszToken); MessageBoxW(NULL, buf, L"Parser Message", MB_OK); } void DoFadeWindow(HWND hwnd) { HDC hdc; int i; RECT rcFrame; COLORREF rgbFinal = RGB(90,126,220); hdc = GetDC(hwnd); GetClientRect(hwnd, &rcFrame); COLORREF crCurr; HBRUSH hbrFill; crCurr = RGB(0,0,0); // draw the left bar for (i = 0; i < 16; i++) { RECT rcCurrFrame; rcCurrFrame = rcFrame; crCurr = RGB((GetRValue(rgbFinal) / 16)*i, (GetGValue(rgbFinal) / 16)*i, (GetBValue(rgbFinal) / 16)*i); hbrFill = CreateSolidBrush(crCurr); if (hbrFill) { FillRect(hdc, &rcCurrFrame, hbrFill); DeleteObject(hbrFill); } GdiFlush(); } ReleaseDC(hwnd, hdc); } //////////////////////////////////////////////////////// // Logon entry point int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(pCmdLine); UNREFERENCED_PARAMETER(nCmdShow); WNDCLASSEX wcx = {0}; BOOL fStatusLaunch = false; BOOL fShutdownLaunch = false; BOOL fWait = false; CBackgroundWindow backgroundWindow(hInst); ZeroMemory(g_rgH, sizeof(g_rgH)); SetErrorHandler(); InitCommonControls(); // Register logon notification window wcx.cbSize = sizeof(WNDCLASSEX); wcx.lpfnWndProc = LogonWindowProc; wcx.hInstance = GetModuleHandleW(NULL); wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wcx.lpszClassName = TEXT("LogonWnd"); wcx.hCursor = LoadCursor(NULL, IDC_ARROW); RegisterClassEx(&wcx); fStatusLaunch = (StrStrIA(pCmdLine, "/status") != NULL); fShutdownLaunch = (StrStrIA(pCmdLine, "/shutdown") != NULL); fWait = (StrStrIA(pCmdLine, "/wait") != NULL); g_fNoAnimations = (StrStrIA(pCmdLine, "/noanim") != NULL); // Create frame Parser* pParser = NULL; NativeHWNDHost* pnhh = NULL; // DirectUI init process if (FAILED(InitProcess())) goto Failure; // Register classes if (FAILED(LogonFrame::Register())) goto Failure; if (FAILED(LogonAccountList::Register())) goto Failure; if (FAILED(LogonAccount::Register())) goto Failure; // DirectUI init thread if (FAILED(InitThread())) goto Failure; if (FAILED(CoInitialize(NULL))) goto Failure; #ifdef GADGET_ENABLE_GDIPLUS if (FAILED(FxInitGuts())) goto Failure; #endif #ifndef DEBUG if (!RunningUnderWinlogon()) goto Failure; #endif DisableAnimations(); // Create host HMONITOR hMonitor; POINT pt; MONITORINFO monitorInfo; // Determine initial size of the host. This is desired to be the entire // primary monitor resolution because the host always runs on the secure // desktop. If magnifier is brought up it will call SHAppBarMessage which // will change the work area and we will respond to it appropriately from // the listener in shgina that sends us HM_DISPLAYRESIZE messages. pt.x = pt.y = 0; hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); DUIAssert(hMonitor != NULL, "NULL HMONITOR returned from MonitorFromPoint"); monitorInfo.cbSize = sizeof(monitorInfo); if (GetMonitorInfo(hMonitor, &monitorInfo) == FALSE) { SystemParametersInfo(SPI_GETWORKAREA, 0, &monitorInfo.rcMonitor, 0); } NativeHWNDHost::Create(L"Windows Logon", backgroundWindow.Create(), NULL, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, 0, WS_POPUP, NHHO_IgnoreClose, &pnhh); // NativeHWNDHost::Create(L"Windows Logon", NULL, NULL, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, // 800, 600, 0, WS_POPUP, NHHO_IgnoreClose, &pnhh); if (!pnhh) goto Failure; // Populate handle list for theme style parsing g_rgH[0] = hInst; // Default HINSTANCE g_rgH[SCROLLBARHTHEME] = OpenThemeData(pnhh->GetHWND(), L"Scrollbar"); // Frame creation Parser::Create(IDR_LOGONUI, g_rgH, LogonParseError, &pParser); if (!pParser) goto Failure; if (!pParser->WasParseError()) { Element::StartDefer(); // Always double buffer LogonFrame::Create(pnhh->GetHWND(), true, 0, (Element**)&g_plf); if (!g_plf) { Element::EndDefer(); goto Failure; } g_plf->SetNativeHost(pnhh); Element* pe; pParser->CreateElement(L"main", g_plf, &pe); if (pe) // Fill contents using substitution { // Frame tree is built if (FAILED(g_plf->OnTreeReady(pParser))) { Element::EndDefer(); goto Failure; } if (fShutdownLaunch || fWait) { g_plf->SetTitle(IDS_PLEASEWAIT); } if (!fStatusLaunch) { // Build contents of account list g_plf->EnterLogonMode(false); } else { g_plf->EnterPreStatusMode(false); } // Host pnhh->Host(g_plf); g_plf->SetButtonLabels(); Element *peLogoArea = g_plf->FindDescendent(StrToID(L"product")); if (!g_fNoAnimations) { pnhh->ShowWindow(); DoFadeWindow(pnhh->GetHWND()); if (peLogoArea) { peLogoArea->SetAlpha(0); } } // Set visible and focus g_plf->SetVisible(true); g_plf->SetKeyFocus(); Element::EndDefer(); // Do initial show pnhh->ShowWindow(); if (!g_fNoAnimations) { EnableAnimations(); } if (peLogoArea) { peLogoArea->SetAlpha(255); } StartMessagePump(); // psf will be deleted by native HWND host when destroyed } else Element::EndDefer(); } Failure: if (pnhh) pnhh->Destroy(); if (pParser) pParser->Destroy(); ReleaseStatusHost(); FreeLayoutInfo(LAYOUT_DEF_USER); if (g_rgH[SCROLLBARHTHEME]) // Scrollbar { CloseThemeData(g_rgH[SCROLLBARHTHEME]); } CoUninitialize(); UnInitThread(); UnInitProcess(); // Free cached atom list if (LogonAccount::idPwdGo) DeleteAtom(LogonAccount::idPwdGo); if (LogonAccount::idPwdInfo) DeleteAtom(LogonAccount::idPwdInfo); EndHostProcess(0); return 0; } void LogonAccount::SetKeyboardIcon(HICON hIcon) { HICON hIconCopy = NULL; if (hIcon) { hIconCopy = CopyIcon(hIcon); } if (_peKbdIcon && hIconCopy) { Value* pvIcon = Value::CreateGraphic(hIconCopy); _peKbdIcon->SetValue(Element::ContentProp, PI_Local, pvIcon); // Element takes owners _peKbdIcon->SetPadding(0, 5, 0, 7); pvIcon->Release(); } }