// backend.cpp : Windows Logon application // // backend logic for communicating with SHGina and winlogon #include "priv.h" using namespace DirectUI; #include "resource.h" #include "backend.h" #include "shgina.h" #include "profileutil.h" #include "uihostipc.h" //////////////////////////////// #include "eballoon.h" #define MAX_COMPUTERDESC_LENGTH 255 static WCHAR g_szGuestName[UNLEN + sizeof('\0')] = L"Guest"; #define TIMER_REFRESHTIPS 1014 #define TIMER_ANIMATEFLAG 1015 #define TOTAL_FLAG_FRAMES (FLAG_ANIMATION_COUNT * MAX_FLAG_FRAMES) UINT_PTR g_puTimerId = 0; UINT_PTR g_puFlagTimerId = 0; DWORD sTimerCount = 0; extern CErrorBalloon g_pErrorBalloon; extern LogonFrame* g_plf; extern ILogonStatusHost *g_pILogonStatusHost; const TCHAR CBackgroundWindow::s_szWindowClassName[] = TEXT("LogonUIBackgroundWindowClass"); // -------------------------------------------------------------------------- // CBackgroundWindow::CBackgroundWindow // // Arguments: hInstance = HINSTANCE of the process. // // Returns: // // Purpose: Constructor for CBackgroundWindow. This registers the window // class. // // History: 2001-03-27 vtan created // -------------------------------------------------------------------------- CBackgroundWindow::CBackgroundWindow (HINSTANCE hInstance) : _hInstance(hInstance), _hwnd(NULL) { WNDCLASSEX wndClassEx; ZeroMemory(&wndClassEx, sizeof(wndClassEx)); wndClassEx.cbSize = sizeof(wndClassEx); wndClassEx.lpfnWndProc = WndProc; wndClassEx.hInstance = hInstance; wndClassEx.lpszClassName = s_szWindowClassName; wndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW); _atom = RegisterClassEx(&wndClassEx); } // -------------------------------------------------------------------------- // CBackgroundWindow::~CBackgroundWindow // // Arguments: // // Returns: // // Purpose: Destructor for CBackgroundWindow. This destroys the window // and unregisters the window class. // // History: 2001-03-27 vtan created // -------------------------------------------------------------------------- CBackgroundWindow::~CBackgroundWindow (void) { if (_hwnd != NULL) { (BOOL)DestroyWindow(_hwnd); } if (_atom != 0) { TBOOL(UnregisterClass(MAKEINTRESOURCE(_atom), _hInstance)); } } // -------------------------------------------------------------------------- // CBackgroundWindow::Create // // Arguments: // // Returns: HWND // // Purpose: Creates the window. It's created WS_EX_TOPMOST and covers the // entire screen. It's not created on CHK builds of logonui.exe // // History: 2001-03-27 vtan created // -------------------------------------------------------------------------- HWND CBackgroundWindow::Create (void) { HWND hwnd; #if DEBUG hwnd = NULL; #else hwnd = CreateWindowEx(0, s_szWindowClassName, NULL, WS_POPUP, GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN), GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN), NULL, NULL, _hInstance, this); if (hwnd != NULL) { (BOOL)ShowWindow(hwnd, SW_SHOW); TBOOL(SetForegroundWindow(hwnd)); (BOOL)EnableWindow(hwnd, FALSE); } #endif return(hwnd); } // -------------------------------------------------------------------------- // CBackgroundWindow::WndProc // // Arguments: See the platform SDK under WindowProc. // // Returns: See the platform SDK under WindowProc. // // Purpose: WindowProc for the background window. This just passes the // messages thru to DefWindowProc. // // History: 2001-03-27 vtan created // -------------------------------------------------------------------------- LRESULT CALLBACK CBackgroundWindow::WndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lResult; CBackgroundWindow *pThis; pThis = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); switch (uMsg) { case WM_CREATE: { CREATESTRUCT *pCreateStruct; pCreateStruct = reinterpret_cast(lParam); pThis = reinterpret_cast(pCreateStruct->lpCreateParams); (LONG_PTR)SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pThis)); lResult = 0; break; } case WM_PAINT: { HDC hdcPaint; PAINTSTRUCT ps; hdcPaint = BeginPaint(hwnd, &ps); TBOOL(FillRect(ps.hdc, &ps.rcPaint, reinterpret_cast(GetStockObject(BLACK_BRUSH)))); TBOOL(EndPaint(hwnd, &ps)); lResult = 0; break; } default: lResult = DefWindowProc(hwnd, uMsg, wParam, lParam); break; } return(lResult); } //////////////////////////////////////////////////////// // Logon Utilities //////////////////////////////////////////////////////// //////////////////////////////////////// // // TurnOffComputer // // Call SHGina to bring up the "Turn Off Computer" dialog and handle the request. // In debug builds, holding down the shift key and clicking the turn off button // will exit logonui. // // RETURNS // HRESULT indicating whether it worked or not. // ///////////////////////////////////////// HRESULT TurnOffComputer() { ILocalMachine *pobjLocalMachine; HRESULT hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILocalMachine, &pobjLocalMachine)); if (SUCCEEDED(hr)) { hr = pobjLocalMachine->TurnOffComputer(); pobjLocalMachine->Release(); } return hr; } //////////////////////////////////////// // // UndockComputer // // Tell SHGina to undock the computer // // RETURNS // HRESULT indicating whether it worked or not. // ///////////////////////////////////////// HRESULT UndockComputer() { ILocalMachine *pobjLocalMachine; HRESULT hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILocalMachine, &pobjLocalMachine)); if (SUCCEEDED(hr)) { hr = pobjLocalMachine->UndockComputer(); pobjLocalMachine->Release(); } return hr; } //////////////////////////////////////// // // CalcBalloonTargetLocation // // Given a DirectUI element, figure out a good place to have a balloon tip pointed to // in parent window coordinates. // // RETURNS // nothing // ///////////////////////////////////////// void CalcBalloonTargetLocation(HWND hwndParent, Element *pe, POINT *ppt) { Value* pv; BOOL fIsRTL = (GetWindowLong(hwndParent, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0; DUIAssertNoMsg(pe); DUIAssertNoMsg(ppt); // get the position of the link so we can target the balloon tip correctly POINT pt = {0,0}; const SIZE *psize = pe->GetExtent(&pv); pt.y += psize->cy / 2; if (psize->cx < 100) { pt.x += psize->cx / 2; } else { if (fIsRTL) { pt.x = (pt.x + psize->cx) - 50; } else { pt.x += 50; } } pv->Release(); while (pe) { const POINT* ppt = pe->GetLocation(&pv); pt.x += ppt->x; pt.y += ppt->y; pv->Release(); pe = pe->GetParent(); } *ppt = pt; } //////////////////////////////////////// // // DetermineGuestAccountName // // // Get the localized guest account name by matching the // local list of user account SIDs for the guest RID. // This code is lifted directly from msgina\userlist.cpp // In the event of failure this is initialized to the english "Guest". // // // RETURNS // nothing. // ///////////////////////////////////////// void DetermineGuestAccountName() { NET_API_STATUS nasCode; DWORD dwPreferredSize, dwEntriesRead; NET_DISPLAY_USER *pNDU; static const int iMaximumUserCount = 100; dwPreferredSize = (sizeof(NET_DISPLAY_USER) + (3 * UNLEN) * iMaximumUserCount); pNDU = NULL; nasCode = NetQueryDisplayInformation(NULL, 1, 0, iMaximumUserCount, dwPreferredSize, &dwEntriesRead, reinterpret_cast(&pNDU)); if ((ERROR_SUCCESS == nasCode) || (ERROR_MORE_DATA == nasCode)) { int iIndex; for (iIndex = static_cast(dwEntriesRead - 1); iIndex >= 0; --iIndex) { BOOL fResult; DWORD dwSIDSize, dwDomainSize; SID_NAME_USE eUse; PSID pSID; WCHAR wszDomain[DNLEN + sizeof('\0')]; // Iterate the user list and look up the SID for each user in the // list regardless of whether they are disabled or not. dwSIDSize = dwDomainSize = 0; fResult = LookupAccountNameW(NULL, pNDU[iIndex].usri1_name, NULL, &dwSIDSize, NULL, &dwDomainSize, &eUse); pSID = static_cast(LocalAlloc(LMEM_FIXED, dwSIDSize)); if (pSID != NULL) { dwDomainSize = DUIARRAYSIZE(wszDomain); fResult = LookupAccountNameW(NULL, pNDU[iIndex].usri1_name, pSID, &dwSIDSize, wszDomain, &dwDomainSize, &eUse); // Ensure that only user SIDs are checked. if ((fResult != FALSE) && (SidTypeUser == eUse)) { unsigned char ucSubAuthorityCount; int iSubAuthorityIndex; ucSubAuthorityCount = *GetSidSubAuthorityCount(pSID); for (iSubAuthorityIndex = 0; iSubAuthorityIndex < ucSubAuthorityCount; ++iSubAuthorityIndex) { DWORD dwSubAuthority; dwSubAuthority = *GetSidSubAuthority(pSID, iSubAuthorityIndex); if (DOMAIN_USER_RID_GUEST == dwSubAuthority) { lstrcpyW(g_szGuestName, pNDU[iIndex].usri1_name); } } } (HLOCAL)LocalFree(pSID); } } } (NET_API_STATUS)NetApiBufferFree(pNDU); } //////////////////////////////////////// // // GetLogonUserByLogonName // // Given a username, CoCreate the ILogonUser for that name. // // RETURNS // HRESULT -- failure if the user could not be created. // ///////////////////////////////////////// HRESULT GetLogonUserByLogonName(LPWSTR pszUsername, ILogonUser **ppobjUser) { VARIANT var; ILogonEnumUsers *pobjEnum; if (ppobjUser) { *ppobjUser = NULL; } var.vt = VT_BSTR; var.bstrVal = pszUsername; HRESULT hr = CoCreateInstance(CLSID_ShellLogonEnumUsers, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILogonEnumUsers, &pobjEnum)); if (SUCCEEDED(hr)) { hr = pobjEnum->item(var, ppobjUser); pobjEnum->Release(); } return hr; } //////////////////////////////////////// // // ReleaseStatusHost // // Clean up the logon status host object. // // RETURNS // nothing // ///////////////////////////////////////// void ReleaseStatusHost() { if (g_pILogonStatusHost != NULL) { g_pILogonStatusHost->UnInitialize(); g_pILogonStatusHost->Release(); g_pILogonStatusHost = NULL; } } //////////////////////////////////////// // // EndHostProcess // // Clean up the logon status host object and if uiExitCode is anything other than 0, // then terminate the process immediately. // // RETURNS // nothing // ///////////////////////////////////////// void EndHostProcess(UINT uiExitCode) { ReleaseStatusHost(); if (uiExitCode != 0) { ExitProcess(uiExitCode); } } //////////////////////////////////////// // // GetRegistryNumericValue // // Given a registry HKEY and a value return the numeric value // // RETURNS // the numeric value from the registry // ///////////////////////////////////////// int GetRegistryNumericValue(HKEY hKey, LPCTSTR pszValueName) { int iResult; DWORD dwType, dwDataSize; iResult = 0; if (ERROR_SUCCESS == RegQueryValueEx(hKey, pszValueName, NULL, &dwType, NULL, NULL)) { if (REG_DWORD == dwType) { DWORD dwData; dwDataSize = sizeof(dwData); if (ERROR_SUCCESS == RegQueryValueEx(hKey, pszValueName, NULL, NULL, reinterpret_cast(&dwData), &dwDataSize)) { iResult = static_cast(dwData); } } else if (REG_SZ == dwType) { TCHAR szData[1024]; dwDataSize = sizeof(szData); if (ERROR_SUCCESS == RegQueryValueEx(hKey, pszValueName, NULL, NULL, reinterpret_cast(szData), &dwDataSize)) { char szAnsiData[1024]; (int)WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)szData, -1, szAnsiData, sizeof(szAnsiData), NULL, NULL); iResult = atoi(szAnsiData); } } } return(iResult); } //////////////////////////////////////// // // IsShutdownAllowed // // Firstly (firstly??)... if the machine is remote then don't allow shut down. // Determine the local machine policy for shutdown from the logon screen. // This is stored in two places as two different types (REG_DWORD and REG_SZ). // Always check the policy location AFTER the normal location to ensure that // policy overrides normal settings. // // RETURNS // TRUE if the machine is allowed to be shut down from logonui. FALSE otherwise // ///////////////////////////////////////// BOOL IsShutdownAllowed() { BOOL fResult = FALSE; ILocalMachine *pobjLocalMachine; HRESULT hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILocalMachine, &pobjLocalMachine)); if (SUCCEEDED(hr)) { VARIANT_BOOL vbCanShutdown = VARIANT_FALSE; hr = pobjLocalMachine->get_isShutdownAllowed(&vbCanShutdown); if (SUCCEEDED(hr)) { fResult = (vbCanShutdown == VARIANT_TRUE); } pobjLocalMachine->Release(); } return fResult; } //////////////////////////////////////// // // IsUndockAllowed // // Check with SHGina to see if we are allowed to undock the PC. // // RETURNS // TRUE if the machine is allowed to be undocked from logonui. FALSE otherwise // ///////////////////////////////////////// BOOL IsUndockAllowed() { BOOL fResult = FALSE; #if 0 ILocalMachine *pobjLocalMachine; HRESULT hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILocalMachine, &pobjLocalMachine)); if (SUCCEEDED(hr)) { VARIANT_BOOL vbCanUndock = VARIANT_FALSE; hr = pobjLocalMachine->get_isUndockEnabled(&vbCanUndock); if (SUCCEEDED(hr)) { fResult = (vbCanUndock == VARIANT_TRUE); } pobjLocalMachine->Release(); } #endif return fResult; } #ifndef TESTDATA LONG WINAPI LogonUIUnhandledExceptionFilter (struct _EXCEPTION_POINTERS *ExceptionInfo) { return(RtlUnhandledExceptionFilter(ExceptionInfo)); } #endif // !TESTDATA void SetErrorHandler (void) { #ifndef TESTDATA SYSTEM_KERNEL_DEBUGGER_INFORMATION kdInfo; (NTSTATUS)NtQuerySystemInformation(SystemKernelDebuggerInformation, &kdInfo, sizeof(kdInfo), NULL); if (kdInfo.KernelDebuggerEnabled || NtCurrentPeb()->BeingDebugged) { (LPTOP_LEVEL_EXCEPTION_FILTER)SetUnhandledExceptionFilter(LogonUIUnhandledExceptionFilter); } #endif // !TESTDATA } //////////////////////////////////////// // // RunningInWinlogon // // Checks to see if logonui is running in the winlogon context. There are some things // that don't work well when we are not in winlogon so this makes debugging easier. // // RETURNS // TRUE if the running in winlogon (actually if it can find the GINA Logon window). FALSE otherwise // ///////////////////////////////////////// BOOL RunningInWinlogon() { #if DEBUG return (FindWindow(NULL, TEXT("GINA Logon")) != NULL); #else return true; #endif } //////////////////////////////////////// // // BuildUserListFromGina // // Enumerate all of the users that SHGina tells us we care about and add them to the accounts list. // Find out of they require a password and their current state for notifications. // // If there is only 1 user or if there are 2 users, but one of them is guest, then ppAccount will // contain a pointer to the only user on this machine. The caller can then automatically select // that user to avoid them from having to click that user given that there is no one else to click. // // // RETURNS // HRESULT -- if not a success code, we are hosed. // ///////////////////////////////////////// HRESULT BuildUserListFromGina(LogonFrame* plf, OUT LogonAccount** ppAccount) { if (ppAccount) { *ppAccount = NULL; } DetermineGuestAccountName(); int iGuestId = -1; WCHAR szPicturePath[MAX_PATH]; ILogonEnumUsers *pobjEnum; LogonAccount* plaLastNormal = NULL; // load the ILogonEnumUsers object from SHGina.dll HRESULT hr = CoCreateInstance(CLSID_ShellLogonEnumUsers, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ILogonEnumUsers, &pobjEnum)); if (SUCCEEDED(hr)) { int iUser,cUsers; UINT uiUsers; ILogonUser *pobjUser; // get the number of users hr = pobjEnum->get_length(&uiUsers); if (FAILED(hr)) goto done; cUsers = (int)uiUsers; for (iUser = 0; iUser < cUsers; iUser++) { VARIANT var, varUnreadMail, varPicture = {0}, varUsername = {0}, varHint = {0}; VARIANT_BOOL vbLoggedOn, vbInteractiveLogonAllowed; var.vt = VT_I4; var.lVal = iUser; hr = pobjEnum->item( var, &pobjUser); if (SUCCEEDED(hr) && pobjUser) { if (SUCCEEDED(pobjUser->get_interactiveLogonAllowed(&vbInteractiveLogonAllowed)) && (vbInteractiveLogonAllowed != VARIANT_FALSE)) { // get the display name for the user pobjUser->get_setting(L"DisplayName" ,&var); pobjUser->get_setting(L"LoginName", &varUsername); // if the display name is blank, we will use the login name. This is the case for Guest if (var.bstrVal && lstrlenW(var.bstrVal) == 0) { VariantClear(&var); pobjUser->get_setting(L"LoginName" ,&var); } if (FAILED(pobjUser->get_isLoggedOn(&vbLoggedOn))) { vbLoggedOn = VARIANT_FALSE; } if (FAILED(pobjUser->get_setting(L"UnreadMail", &varUnreadMail))) { varUnreadMail.uintVal = 0; } lstrcpyW(szPicturePath, L""); // get the path to their picture if (SUCCEEDED(pobjUser->get_setting(L"Picture" ,&varPicture))) { if (lstrlenW(varPicture.bstrVal) != 0) // in the case of defaultUser, lets just use the user icons we have. { lstrcpynW(szPicturePath, &(varPicture.bstrVal[7]), MAX_PATH); } VariantClear(&varPicture); } VariantClear(&varHint); hr = pobjUser->get_setting(L"Hint", &varHint); if (FAILED(hr) || varHint.vt != VT_BSTR) { VariantClear(&varHint); } if (lstrcmpi(g_szGuestName, var.bstrVal) == 0) { iGuestId = iUser; } LogonAccount* pla = NULL; // If no picture is available, default to one hr = plf->AddAccount(*szPicturePath ? szPicturePath : MAKEINTRESOURCEW(IDB_USER0), *szPicturePath == NULL, var.bstrVal, varUsername.bstrVal, varHint.bstrVal, FALSE, // always assume a password present, and check later (see IsPasswordBlank()) (vbLoggedOn == VARIANT_TRUE), &pla); // pla->UpdateNotifications(true); if (SUCCEEDED(hr) && (iGuestId != iUser)) { plaLastNormal = pla; } VariantClear(&var); VariantClear(&varHint); VariantClear(&varUsername); } pobjUser->Release(); } } // if there is only one user, select them by default. Ignore guest. if (ppAccount && plaLastNormal && (cUsers == 1 || (cUsers == 2 && iGuestId != -1))) { *ppAccount = plaLastNormal; } done: pobjEnum->Release(); } // User logon list is now available plf->SetUserListAvailable(true); //DUITrace("LOGONUI: UserList is now available\n"); return hr; } //////////////////////////////////////// // // KillFlagAnimation // // Stop the flag from animating immediately. What it actually does is check to // see if we are still animating the flag and if we are, then set the frame // counter to the end and set the bitmap to the first frame in the animation. // The next time the timer fires, it will see that we are done and actually // kill the timer. // ///////////////////////////////////////// void KillFlagAnimation() { #ifdef ANIMATE_FLAG if (sTimerCount > 0 && sTimerCount < TOTAL_FLAG_FRAMES) { sTimerCount = TOTAL_FLAG_FRAMES + 1; if (g_plf != NULL) { g_plf->NextFlagAnimate(0); } } #endif } //////////////////////////////////////// // // LogonWindowProc // // This is the notification window that SHGina uses to communicate with logonui. // Send all messages through the helper in logonstatushost and check for our // own messages on this window. This is where all of the SHGina notifications come. // //////////////////////////////////////// LRESULT CALLBACK LogonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static BOOL sResetTimer = false; LogonFrame *pLogonFrame; pLogonFrame = g_plf; if (g_pILogonStatusHost != NULL) { VARIANT varWParam, varLParam; varWParam.uintVal = static_cast(wParam); varLParam.lVal = static_cast(lParam); if (SUCCEEDED(g_pILogonStatusHost->WindowProcedureHelper(hwnd, uMsg, varWParam, varLParam))) { return 0; } } switch (uMsg) { case WM_TIMER: if ((pLogonFrame != NULL) && (pLogonFrame->GetState() == LAS_Logon) && (wParam == TIMER_REFRESHTIPS)) { BOOL fRefreshAll = false; if (!sResetTimer) { fRefreshAll = true; sResetTimer = true; KillTimer(hwnd, g_puTimerId); g_puTimerId = SetTimer(hwnd, TIMER_REFRESHTIPS, 15000, NULL); // update the values 15 seconds #ifdef ANIMATE_FLAG g_puFlagTimerId = SetTimer(hwnd, TIMER_ANIMATEFLAG, 20, NULL); // start the flag animation #endif } pLogonFrame->UpdateUserStatus(fRefreshAll); return 0; } #ifdef ANIMATE_FLAG if (wParam == TIMER_ANIMATEFLAG) { if (sTimerCount > TOTAL_FLAG_FRAMES) { sTimerCount = 0; KillTimer(hwnd, g_puFlagTimerId); pLogonFrame->NextFlagAnimate(0); } else { sTimerCount ++; pLogonFrame->NextFlagAnimate(sTimerCount % MAX_FLAG_FRAMES); } return 0; } #endif break; case WM_UIHOSTMESSAGE: switch (wParam) { case HM_SWITCHSTATE_STATUS: // LAS_PreStatus if (pLogonFrame != NULL) { pLogonFrame->EnterPreStatusMode(lParam != 0); } break; case HM_SWITCHSTATE_LOGON: // LAS_Logon if (pLogonFrame != NULL) { pLogonFrame->EnterLogonMode(lParam != 0); } if (g_puTimerId != 0) { sResetTimer = false; KillTimer(hwnd, g_puTimerId); } g_puTimerId = SetTimer(hwnd, TIMER_REFRESHTIPS, 250, NULL); // update the values in 1 second break; case HM_SWITCHSTATE_LOGGEDON: // LAS_PostStatus if (pLogonFrame != NULL) { pLogonFrame->EnterPostStatusMode(); } break; case HM_SWITCHSTATE_HIDE: // LAS_Hide if (pLogonFrame != NULL) { if (LogonAccount::GetCandidate()) { LogonAccount* pla = LogonAccount::GetCandidate(); pla->InsertStatus(0); pla->SetStatus(0, L""); pla->ShowStatus(0); } else { pLogonFrame->SetStatus(L""); } pLogonFrame->EnterHideMode(); } goto killTimer; break; case HM_SWITCHSTATE_DONE: // LAS_Done if (pLogonFrame != NULL) { pLogonFrame->EnterDoneMode(); } killTimer: if (g_puTimerId != 0) { KillTimer(hwnd, g_puTimerId); g_puTimerId = 0; } break; case HM_NOTIFY_WAIT: if (pLogonFrame != NULL) { pLogonFrame->SetTitle(IDS_PLEASEWAIT); } break; case HM_SELECT_USER: if (pLogonFrame != NULL) { pLogonFrame->SelectUser(reinterpret_cast(lParam)->szUsername); } break; case HM_SET_ANIMATIONS: if (pLogonFrame != NULL) { pLogonFrame->SetAnimations(lParam != 0); } break; case HM_DISPLAYSTATUS: if ((pLogonFrame != NULL) && (lParam != NULL)) { if (pLogonFrame->GetState() == LAS_PostStatus) { if (LogonAccount::GetCandidate()) { LogonAccount* pla = LogonAccount::GetCandidate(); pla->InsertStatus(0); pla->SetStatus(0, (WCHAR*)lParam); pla->ShowStatus(0); } else { pLogonFrame->SetStatus((WCHAR*)lParam); } } else { pLogonFrame->SetStatus((WCHAR*)lParam); } } break; case HM_DISPLAYREFRESH: if (pLogonFrame != NULL) { pLogonFrame->UpdateUserStatus(true); } break; case HM_DISPLAYRESIZE: if (pLogonFrame != NULL) { pLogonFrame->Resize((BOOL)lParam); } break; case HM_INTERACTIVE_LOGON_REQUEST: return((pLogonFrame != NULL) && pLogonFrame->InteractiveLogonRequest(reinterpret_cast(lParam)->szUsername, reinterpret_cast(lParam)->szPassword)); } break; } return DefWindowProc(hwnd, uMsg,wParam, lParam); }