// -------------------------------------------------------------------------- // Module Name: Compatibility.cpp // // Copyright (c) 2000, Microsoft Corporation // // Module to handle compatibility problems in general. // // History: 2000-08-03 vtan created // -------------------------------------------------------------------------- #include "StandardHeader.h" #include "Compatibility.h" #include #include #include "KernelResources.h" #include "RegistryResources.h" #include "SingleThreadedExecution.h" // -------------------------------------------------------------------------- // CCompatibility::HasEnoughMemoryForNewSession // // Purpose: LPC port to server // // History: 2000-11-02 vtan created // -------------------------------------------------------------------------- HANDLE CCompatibility::s_hPort = INVALID_HANDLE_VALUE; // -------------------------------------------------------------------------- // CCompatibility::HasEnoughMemoryForNewSession // // Arguments: // // Returns: bool // // Purpose: Currently unused. Was originally intended to be used to stop // disconnects if there isn't enough memory. Algorithm and/or // usage still to be decided. // // History: 2000-08-03 vtan created // -------------------------------------------------------------------------- bool CCompatibility::HasEnoughMemoryForNewSession (void) { return(true); } // -------------------------------------------------------------------------- // CCompatibility::DropSessionProcessesWorkSets // // Arguments: // // Returns: // // Purpose: Iterates all the processes in the session (of the calling // process) and drops their working sets. This is in preparation // for a disconnect when typically the session is idle. // // History: 2000-08-03 vtan created // -------------------------------------------------------------------------- void CCompatibility::DropSessionProcessesWorkingSets (void) { (bool)EnumSessionProcesses(NtCurrentPeb()->SessionId, CB_DropSessionProcessesWorkingSetsProc, NULL); } // -------------------------------------------------------------------------- // CCompatibility::TerminateNonCompliantApplications // // Arguments: // // Returns: NTSTATUS // // Purpose: Requests disconnect capability from the Bad Application // Manager service. This will check the session to be // disconnected (this process -> the client) and walk its list // of processes registered as type 2 (terminate on disconnect). // // If any of those processes cannot be identified as being // terminated gracefully then the disconnect is failed. // // If the BAM is down then allow the call. // // History: 2000-09-08 vtan created // 2000-11-02 vtan rework to call BAM service // -------------------------------------------------------------------------- NTSTATUS CCompatibility::TerminateNonCompliantApplications (void) { NTSTATUS status; if (s_hPort == INVALID_HANDLE_VALUE) { status = ConnectToServer(); } else if (s_hPort != NULL) { status = STATUS_SUCCESS; } else { status = STATUS_OBJECT_NAME_NOT_FOUND; } if (NT_SUCCESS(status)) { status = RequestSwitchUser(); // If the port is disconnected because the service was stopped and // restarted then dump the current handle and re-establish a new // connection. if (status == STATUS_PORT_DISCONNECTED) { ReleaseHandle(s_hPort); s_hPort = INVALID_HANDLE_VALUE; } } else { status = STATUS_SUCCESS; } return(status); } // -------------------------------------------------------------------------- // CCompatibility::MinimizeWindowsOnDisconnect // // Arguments: // // Returns: // // Purpose: Creates a thread to walk the windows on WinSta0\Default and // minimize them. This is required because // user32!SetThreadDesktop doesn't work on the main thread of // winlogon due to the SAS window. // // History: 2001-04-13 vtan created // -------------------------------------------------------------------------- void CCompatibility::MinimizeWindowsOnDisconnect (void) { (BOOL)QueueUserWorkItem(CB_MinimizeWindowsWorkItem, NULL, WT_EXECUTEDEFAULT); } // -------------------------------------------------------------------------- // CCompatibility::RestoreWindowsOnReconnect // // Arguments: // // Returns: // // Purpose: Walks the array of minimized windows for this session and // restores them. Deletes the array for the next time. // // History: 2001-04-13 vtan created // -------------------------------------------------------------------------- void CCompatibility::RestoreWindowsOnReconnect (void) { (BOOL)QueueUserWorkItem(CB_RestoreWindowsWorkItem, NULL, WT_EXECUTEDEFAULT); } // -------------------------------------------------------------------------- // CCompatibility::StaticInitialize // // Arguments: // // Returns: NTSTATUS // // Purpose: // // History: 2001-06-22 vtan created // -------------------------------------------------------------------------- NTSTATUS CCompatibility::StaticInitialize (void) { return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CCompatibility::StaticTerminate // // Arguments: // // Returns: NTSTATUS // // Purpose: Release resources used by the module. // // History: 2001-06-22 vtan created // -------------------------------------------------------------------------- NTSTATUS CCompatibility::StaticTerminate (void) { if ((s_hPort != INVALID_HANDLE_VALUE) && (s_hPort != NULL)) { TBOOL(CloseHandle(s_hPort)); s_hPort = INVALID_HANDLE_VALUE; } return(STATUS_SUCCESS); } // -------------------------------------------------------------------------- // CCompatibility::ConnectToServer // // Arguments: // // Returns: NTSTATUS // // Purpose: Connects to the Bad Application Manager server if no // connection has been established. // // History: 2000-11-02 vtan created // -------------------------------------------------------------------------- NTSTATUS CCompatibility::ConnectToServer (void) { ULONG ulConnectionInfoLength; UNICODE_STRING portName; SECURITY_QUALITY_OF_SERVICE sqos; WCHAR szConnectionInfo[32]; ASSERTMSG(s_hPort == INVALID_HANDLE_VALUE, "Attempt to call CCompatibility::ConnectToServer more than once"); RtlInitUnicodeString(&portName, FUS_PORT_NAME); sqos.Length = sizeof(sqos); sqos.ImpersonationLevel = SecurityImpersonation; sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; sqos.EffectiveOnly = TRUE; lstrcpyW(szConnectionInfo, FUS_CONNECTION_REQUEST); ulConnectionInfoLength = sizeof(szConnectionInfo); return(NtConnectPort(&s_hPort, &portName, &sqos, NULL, NULL, NULL, szConnectionInfo, &ulConnectionInfoLength)); } // -------------------------------------------------------------------------- // CCompatibility::RequestSwitchUser // // Arguments: // // Returns: NTSTATUS // // Purpose: Request the BAM server to do BAM2. // // History: 2001-03-08 vtan created // -------------------------------------------------------------------------- NTSTATUS CCompatibility::RequestSwitchUser (void) { NTSTATUS status; FUSAPI_PORT_MESSAGE portMessageIn, portMessageOut; ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiBAM.apiGeneric.ulAPINumber = API_BAM_REQUESTSWITCHUSER; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_BAM); portMessageIn.portMessage.u1.s1.TotalLength = static_cast(sizeof(FUSAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiBAM.apiGeneric.status; if (NT_SUCCESS(status)) { if (portMessageOut.apiBAM.apiSpecific.apiRequestSwitchUser.out.fAllowSwitch) { status = STATUS_SUCCESS; } else { status = STATUS_ACCESS_DENIED; } } } return(status); } // -------------------------------------------------------------------------- // CCompatibility::CB_DropSessionProcessesWorkingSetsProc // // Arguments: dwProcessID = Process ID for this enumeration. // pV = User data pointer. // // Returns: bool // // Purpose: Attempts to open the given process ID to change the quotas. // This will drop the working set when set to -1. // // History: 2000-08-07 vtan created // -------------------------------------------------------------------------- bool CCompatibility::CB_DropSessionProcessesWorkingSetsProc (DWORD dwProcessID, void *pV) { UNREFERENCED_PARAMETER(pV); HANDLE hProcess; ASSERTMSG(pV == NULL, "Unexpected pV passed to CCompatibility::CB_DropSessionProcessesWorkingSetsProc"); hProcess = OpenProcess(PROCESS_SET_QUOTA, FALSE, dwProcessID); if (hProcess != NULL) { TBOOL(SetProcessWorkingSetSize(hProcess, static_cast(-1), static_cast(-1))); TBOOL(CloseHandle(hProcess)); } return(true); } // -------------------------------------------------------------------------- // CCompatibility::EnumSessionProcesses // // Arguments: dwSessionID = Session ID to enumerate processes of. // pfnCallback = Callback procedure address. // pV = User defined data to pass to callback. // // Returns: bool // // Purpose: Enumerates all processes on the system looking only for those // in the given session ID. Once a process ID is found it passes // that back to the callback. The callback may return false to // terminate the loop and return a false result to the caller of // this function. // // History: 2000-08-07 vtan created // -------------------------------------------------------------------------- bool CCompatibility::EnumSessionProcesses (DWORD dwSessionID, PFNENUMSESSIONPROCESSESPROC pfnCallback, void *pV) { bool fResult; ULONG ulLengthToAllocate, ulLengthReturned; SYSTEM_PROCESS_INFORMATION spi, *pSPI; fResult = false; (NTSTATUS)NtQuerySystemInformation(SystemProcessInformation, &spi, sizeof(spi), &ulLengthToAllocate); pSPI = reinterpret_cast(LocalAlloc(LMEM_FIXED, ulLengthToAllocate)); if (pSPI != NULL) { SYSTEM_PROCESS_INFORMATION *pAllocatedSPI; pAllocatedSPI = pSPI; if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessInformation, pSPI, ulLengthToAllocate, &ulLengthReturned))) { fResult = true; while (fResult && (pSPI != NULL)) { if (pSPI->SessionId == dwSessionID) { fResult = pfnCallback(HandleToUlong(pSPI->UniqueProcessId), pV); } if (pSPI->NextEntryOffset != 0) { pSPI = reinterpret_cast(reinterpret_cast(pSPI) + pSPI->NextEntryOffset); } else { pSPI = NULL; } } } (HLOCAL)LocalFree(pAllocatedSPI); } return(fResult); } // -------------------------------------------------------------------------- // CCompatibility::CB_MinimizeWindowsWorkItem // // Arguments: pV = User data. // // Returns: DWORD // // Purpose: Separate thread to handle switching to the default desktop and // enumerating the windows on it and minimizing them. // // History: 2001-04-13 vtan created // -------------------------------------------------------------------------- DWORD WINAPI CCompatibility::CB_MinimizeWindowsWorkItem (void *pV) { UNREFERENCED_PARAMETER(pV); CDesktop desktop; if (NT_SUCCESS(desktop.Set(TEXT("Default")))) { HWND hwndTray; hwndTray = FindWindow(TEXT("Shell_TrayWnd"), NULL); if (hwndTray != NULL) { // can be a post since we don't care how long it takes for the windows // to be minimized PostMessage(hwndTray, WM_COMMAND, 415 /* IDM_MINIMIZEALL */, 0); } } return(0); } // -------------------------------------------------------------------------- // CCompatibility::CB_RestoreWindowsWorkItem // // Arguments: pV = User data. // // Returns: DWORD // // Purpose: Separate thread to handle switching to the default desktop and // enumerating the windows on it and minimizing them. // // History: 2001-04-25 vtan created // -------------------------------------------------------------------------- DWORD WINAPI CCompatibility::CB_RestoreWindowsWorkItem (void *pV) { UNREFERENCED_PARAMETER(pV); CDesktop desktop; if (NT_SUCCESS(desktop.Set(TEXT("Default")))) { HWND hwndTray; hwndTray = FindWindow(TEXT("Shell_TrayWnd"), NULL); if (hwndTray != NULL) { // use SendMessage to make this happen more quickly, otherwise the user // might wonder where all of their apps went SendMessage(hwndTray, WM_COMMAND, 416 /* IDM_UNDO */, 0); } } return(0); }