/*++ Copyright (c) 1994-1998 Microsoft Corporation Module Name: common.c Abstract: This module contains common apis used by tlist & kill. Author: Wesley Witt (wesw) 20-May-1994 Environment: User Mode --*/ #include "pch.h" #pragma hdrstop // // global variables // PUCHAR CommonLargeBuffer; ULONG CommonLargeBufferSize = 64*1024; // // prototypes // BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam ); BOOL CALLBACK EnumWindowStationsFunc( LPSTR lpstr, LPARAM lParam ); BOOL CALLBACK EnumDesktopsFunc( LPSTR lpstr, LPARAM lParam ); DWORD GetServiceProcessInfo( LPENUM_SERVICE_STATUS_PROCESS* ppInfo ) /*++ Routine Description: Provides an API for getting a list of process information for Win 32 services that are running at the time of the API call. Arguments: ppInfo - address of a pointer to return the information. *ppInfo points to memory allocated with malloc. Return Value: Number of ENUM_SERVICE_STATUS_PROCESS structures pointed at by *ppInfo. --*/ { DWORD dwNumServices = 0; SC_HANDLE hScm; typedef BOOL (__stdcall * PFN_ENUMSERVICSESTATUSEXA) ( SC_HANDLE hSCManager, SC_ENUM_TYPE InfoLevel, DWORD dwServiceType, DWORD dwServiceState, LPBYTE lpServices, DWORD cbBufSize, LPDWORD pcbBytesNeeded, LPDWORD lpServicesReturned, LPDWORD lpResumeHandle, LPCSTR pszGroupName); PFN_ENUMSERVICSESTATUSEXA p_EnumServicesStatusEx; HINSTANCE hAdv = LoadLibrary("advapi32.dll"); // Initialize the output parmeter. *ppInfo = NULL; if (hAdv) { p_EnumServicesStatusEx = (PFN_ENUMSERVICSESTATUSEXA) GetProcAddress(hAdv, "EnumServicesStatusExA"); if (!p_EnumServicesStatusEx) { return 0; } } else { return 0; } // Connect to the service controller. // hScm = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); if (hScm) { LPENUM_SERVICE_STATUS_PROCESS pInfo = NULL; DWORD cbInfo = 4 * 1024; DWORD cbExtraNeeded = 0; DWORD dwErr; DWORD dwResume; DWORD cLoop = 0; const DWORD cLoopMax = 2; // First pass through the loop allocates from an initial guess. (4K) // If that isn't sufficient, we make another pass and allocate // what is actually needed. (We only go through the loop a // maximum of two times.) // do { free (pInfo); cbInfo += cbExtraNeeded; pInfo = (LPENUM_SERVICE_STATUS_PROCESS)malloc(cbInfo); if (!pInfo) { dwErr = ERROR_OUTOFMEMORY; break; } dwErr = ERROR_SUCCESS; dwResume = 0; if (!p_EnumServicesStatusEx( hScm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_ACTIVE, (LPBYTE)pInfo, cbInfo, &cbExtraNeeded, &dwNumServices, &dwResume, NULL)) { dwErr = GetLastError(); } } while ((ERROR_MORE_DATA == dwErr) && (++cLoop < cLoopMax)); if ((ERROR_SUCCESS == dwErr) && dwNumServices) { *ppInfo = pInfo; } else { free (pInfo); dwNumServices = 0; } CloseServiceHandle(hScm); } return dwNumServices; } DWORD GetTaskListEx( PTASK_LIST pTask, DWORD dwNumTasks, BOOL fThreadInfo, DWORD dwNumServices, const ENUM_SERVICE_STATUS_PROCESS* pServiceInfo ) /*++ Routine Description: Provides an API for getting a list of tasks running at the time of the API call. This function uses internal NT apis and data structures. This api is MUCH faster that the non-internal version that uses the registry. Arguments: pTask - Array of TASK_LIST structures to fill. dwNumTasks - Maximum number of tasks that the pTask array can hold. fThreadInfo - TRUE if thread information is desired. dwNumServices - Maximum number of entries in pServiceInfo. pServiceInfo - Array of service status structures to reference for supporting services in processes. Return Value: Number of tasks placed into the pTask array. --*/ { PSYSTEM_PROCESS_INFORMATION ProcessInfo; NTSTATUS status; ANSI_STRING pname; PCHAR p; ULONG TotalOffset; ULONG totalTasks = 0; retry: if (CommonLargeBuffer == NULL) { CommonLargeBuffer = VirtualAlloc (NULL, CommonLargeBufferSize, MEM_COMMIT, PAGE_READWRITE); if (CommonLargeBuffer == NULL) { return 0; } } status = NtQuerySystemInformation( SystemProcessInformation, CommonLargeBuffer, CommonLargeBufferSize, NULL ); if (status == STATUS_INFO_LENGTH_MISMATCH) { CommonLargeBufferSize += 8192; VirtualFree (CommonLargeBuffer, 0, MEM_RELEASE); CommonLargeBuffer = NULL; goto retry; } ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) CommonLargeBuffer; TotalOffset = 0; while (TRUE) { pname.Buffer = NULL; if ( ProcessInfo->ImageName.Buffer ) { RtlUnicodeStringToAnsiString(&pname,(PUNICODE_STRING)&ProcessInfo->ImageName,TRUE); if (pname.Buffer) { p = strrchr(pname.Buffer,'\\'); if ( p ) { p++; } else { p = pname.Buffer; } } else { p = ""; } } else { p = "System Process"; } strncpy( pTask->ProcessName, p, PROCESS_SIZE ); pTask->ProcessName[PROCESS_SIZE-1] = '\0'; pTask->flags = 0; pTask->dwProcessId = (DWORD)(DWORD_PTR)ProcessInfo->UniqueProcessId; pTask->dwInheritedFromProcessId = (DWORD)(DWORD_PTR)ProcessInfo->InheritedFromUniqueProcessId; pTask->CreateTime.QuadPart = (ULONGLONG)ProcessInfo->CreateTime.QuadPart; pTask->PeakVirtualSize = ProcessInfo->PeakVirtualSize; pTask->VirtualSize = ProcessInfo->VirtualSize; pTask->PageFaultCount = ProcessInfo->PageFaultCount; pTask->PeakWorkingSetSize = ProcessInfo->PeakWorkingSetSize; pTask->WorkingSetSize = ProcessInfo->WorkingSetSize; pTask->NumberOfThreads = ProcessInfo->NumberOfThreads; if (fThreadInfo) { if (pTask->pThreadInfo = malloc(pTask->NumberOfThreads * sizeof(THREAD_INFO))) { UINT nThread = pTask->NumberOfThreads; PTHREAD_INFO pThreadInfo = pTask->pThreadInfo; PSYSTEM_THREAD_INFORMATION pSysThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1); while (nThread--) { pThreadInfo->ThreadState = pSysThreadInfo->ThreadState; pThreadInfo->UniqueThread = pSysThreadInfo->ClientId.UniqueThread; pThreadInfo++; pSysThreadInfo++; } } } else { pTask->pThreadInfo = NULL; } pTask->MtsPackageNames[0] = 0; // Initialize the ServiceNames if this task hosts any. // *pTask->ServiceNames = 0; if (dwNumServices) { // For each service with this process id, append it's service // name to the buffer. Separate each with a comma. // BOOL fFirstTime = TRUE; DWORD iSvc; size_t cchRemain = SERVICENAMES_SIZE - 1; size_t cch; for (iSvc = 0; iSvc < dwNumServices; iSvc++) { if (pTask->dwProcessId == pServiceInfo[iSvc].ServiceStatusProcess.dwProcessId) { cch = strlen(pServiceInfo[iSvc].lpServiceName); if (fFirstTime) { fFirstTime = FALSE; strncpy( pTask->ServiceNames, pServiceInfo[iSvc].lpServiceName, cchRemain); // strncpy may not terminate the string if // cchRemain <= cch so we do it regardless. // pTask->ServiceNames[cchRemain] = 0; } else if (cchRemain > 1) { // ensure room for the comma strncat( pTask->ServiceNames, ",", cchRemain--); strncat( pTask->ServiceNames, pServiceInfo[iSvc].lpServiceName, cchRemain); } // Counts are unsigned so we have to check before // subtracting. // if (cchRemain < cch) { // No more room for any more. break; } else { cchRemain -= cch; } } } } pTask++; totalTasks++; if (totalTasks == dwNumTasks) { break; } if (ProcessInfo->NextEntryOffset == 0) { break; } TotalOffset += ProcessInfo->NextEntryOffset; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&CommonLargeBuffer[TotalOffset]; } return totalTasks; } DWORD GetTaskList( PTASK_LIST pTask, DWORD dwNumTasks ) { return GetTaskListEx(pTask, dwNumTasks, FALSE, 0, NULL); } void AddMtsPackageNames( PTASK_LIST Tasks, DWORD NumTasks ) { HRESULT Hr; IMtsGrp* MtsGroup; long Packages; long i; if ((Hr = CoInitialize(NULL)) != S_OK) { return; } if ((Hr = CoCreateInstance(&CLSID_MtsGrp, NULL, CLSCTX_ALL, &IID_IMtsGrp, (void **)&MtsGroup)) != S_OK) { goto Uninit; } if ((Hr = MtsGroup->lpVtbl->Refresh(MtsGroup)) != S_OK || (Hr = MtsGroup->lpVtbl->get_Count(MtsGroup, &Packages)) != S_OK) { goto ReleaseGroup; } for (i = 0; i < Packages; i++) { IUnknown* Unk; IMtsEvents* Events; BSTR Name; DWORD Pid; DWORD TaskIdx; if ((Hr = MtsGroup->lpVtbl->Item(MtsGroup, i, &Unk)) != S_OK) { continue; } Hr = Unk->lpVtbl->QueryInterface(Unk, &IID_IMtsEvents, (void **)&Events); Unk->lpVtbl->Release(Unk); if (Hr != S_OK) { continue; } Hr = Events->lpVtbl->GetProcessID(Events, (PLONG)&Pid); if (Hr == S_OK) { Hr = Events->lpVtbl->get_PackageName(Events, &Name); } Events->lpVtbl->Release(Events); if (Hr != S_OK) { continue; } for (TaskIdx = 0; TaskIdx < NumTasks; TaskIdx++) { if (Tasks[TaskIdx].dwProcessId == Pid) { break; } } if (TaskIdx < NumTasks) { PSTR Str; int Conv; Str = Tasks[TaskIdx].MtsPackageNames + strlen(Tasks[TaskIdx].MtsPackageNames); if (Str > Tasks[TaskIdx].MtsPackageNames) { *Str++ = ','; } Conv = WideCharToMultiByte( CP_ACP, 0, Name, -1, Str, MTS_PACKAGE_NAMES_SIZE - (DWORD)(Str - Tasks[TaskIdx].MtsPackageNames) - 2, NULL, NULL ); SysFreeString(Name); if (Conv == 0 && Str > Tasks[TaskIdx].MtsPackageNames && *(Str - 1) == ',') { *(Str - 1) = 0; } } } ReleaseGroup: MtsGroup->lpVtbl->Release(MtsGroup); Uninit: CoUninitialize(); return; } BOOL DetectOrphans( PTASK_LIST pTask, DWORD dwNumTasks ) { DWORD i, j; BOOL Result = FALSE; for (i=0; ihwnd) { hProcess1 = OpenProcess( PROCESS_ALL_ACCESS, FALSE, tlist->dwProcessId ); if (hProcess1) { hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, tlist->dwProcessId ); if (hProcess == NULL) { CloseHandle(hProcess1); return FALSE; } if (!TerminateProcess( hProcess, 1 )) { CloseHandle( hProcess ); CloseHandle( hProcess1 ); return FALSE; } CloseHandle( hProcess ); CloseHandle( hProcess1 ); return TRUE; } } // // save the current windowstation // hwinstaSave = GetProcessWindowStation(); // // save the current desktop // hdeskSave = GetThreadDesktop( GetCurrentThreadId() ); // // open the windowstation // hwinsta = OpenWindowStation( tlist->lpWinsta, FALSE, MAXIMUM_ALLOWED ); if (!hwinsta) { return FALSE; } // // change the context to the new windowstation // SetProcessWindowStation( hwinsta ); // // open the desktop // hdesk = OpenDesktop( tlist->lpDesk, 0, FALSE, MAXIMUM_ALLOWED ); if (!hdesk) { return FALSE; } // // change the context to the new desktop // SetThreadDesktop( hdesk ); // // kill the process // PostMessage( tlist->hwnd, WM_CLOSE, 0, 0 ); // // restore the previous desktop // if (hdesk != hdeskSave) { SetThreadDesktop( hdeskSave ); CloseDesktop( hdesk ); } // // restore the context to the previous windowstation // if (hwinsta != hwinstaSave) { SetProcessWindowStation( hwinstaSave ); CloseWindowStation( hwinsta ); } return TRUE; } VOID GetWindowTitles( PTASK_LIST_ENUM te ) { // // enumerate all windows and try to get the window // titles for each task // EnumWindowStations( EnumWindowStationsFunc, (LPARAM)te ); } BOOL CALLBACK EnumWindowStationsFunc( LPSTR lpstr, LPARAM lParam ) /*++ Routine Description: Callback function for windowstation enumeration. Arguments: lpstr - windowstation name lParam - ** not used ** Return Value: TRUE - continues the enumeration --*/ { PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam; HWINSTA hwinsta; HWINSTA hwinstaSave; // // open the windowstation // hwinsta = OpenWindowStation( lpstr, FALSE, MAXIMUM_ALLOWED ); if (!hwinsta) { return FALSE; } // // save the current windowstation // hwinstaSave = GetProcessWindowStation(); // // change the context to the new windowstation // SetProcessWindowStation( hwinsta ); te->lpWinsta = _strdup( lpstr ); // // enumerate all the desktops for this windowstation // EnumDesktops( hwinsta, EnumDesktopsFunc, lParam ); // // restore the context to the previous windowstation // if (hwinsta != hwinstaSave) { SetProcessWindowStation( hwinstaSave ); CloseWindowStation( hwinsta ); } // // continue the enumeration // return TRUE; } BOOL CALLBACK EnumMessageWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam ) /*++ Routine Description: Enumarates message windows (which are not enumarated by EnumWindows) Arguments: lpEnumFunc - Callback function lParam - Caller data Return Value: TRUE --*/ { HWND hwnd = NULL; do { hwnd = FindWindowEx(HWND_MESSAGE, hwnd, NULL, NULL); if (hwnd != NULL) { if (!(*lpEnumFunc)(hwnd, lParam)) { break; } } } while (hwnd != NULL); return TRUE; } BOOL CALLBACK EnumDesktopsFunc( LPSTR lpstr, LPARAM lParam ) /*++ Routine Description: Callback function for desktop enumeration. Arguments: lpstr - desktop name lParam - ** not used ** Return Value: TRUE - continues the enumeration --*/ { PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam; HDESK hdeskSave; HDESK hdesk; // // open the desktop // hdesk = OpenDesktop( lpstr, 0, FALSE, MAXIMUM_ALLOWED ); if (!hdesk) { return FALSE; } // // save the current desktop // hdeskSave = GetThreadDesktop( GetCurrentThreadId() ); // // change the context to the new desktop // SetThreadDesktop( hdesk ); te->lpDesk = _strdup( lpstr ); // // enumerate all windows in the new desktop // ((PTASK_LIST_ENUM)lParam)->bFirstLoop = TRUE; EnumWindows( (WNDENUMPROC)EnumWindowsProc, lParam ); EnumMessageWindows( (WNDENUMPROC)EnumWindowsProc, lParam ); ((PTASK_LIST_ENUM)lParam)->bFirstLoop = FALSE; EnumWindows( (WNDENUMPROC)EnumWindowsProc, lParam ); EnumMessageWindows( (WNDENUMPROC)EnumWindowsProc, lParam ); // // restore the previous desktop // if (hdesk != hdeskSave) { SetThreadDesktop( hdeskSave ); CloseDesktop( hdesk ); } return TRUE; } BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam ) /*++ Routine Description: Callback function for window enumeration. Arguments: hwnd - window handle lParam - pte Return Value: TRUE - continues the enumeration --*/ { DWORD pid = 0; DWORD i; CHAR buf[TITLE_SIZE]; PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam; PTASK_LIST tlist = te->tlist; DWORD numTasks = te->numtasks; // // Use try/except block when enumerating windows, // as a window may be destroyed by another thread // when being enumerated. // try { // // get the processid for this window // if (!GetWindowThreadProcessId( hwnd, &pid )) { return TRUE; } if ((GetWindow( hwnd, GW_OWNER )) || (!(GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)) && te->bFirstLoop) { // // not a top level window // return TRUE; } // // look for the task in the task list for this window // If this is the second time let invisible windows through if we don't // have a window already // for (i=0; ibFirstLoop || (tlist[i].hwnd == 0))) { tlist[i].hwnd = hwnd; tlist[i].lpWinsta = te->lpWinsta; tlist[i].lpDesk = te->lpDesk; // // we found the task no lets try to get the // window text // if (GetWindowText( tlist[i].hwnd, buf, sizeof(buf) )) { // // go it, so lets save it // strcpy( tlist[i].WindowTitle, buf ); } break; } } } except(EXCEPTION_EXECUTE_HANDLER) { } // // continue the enumeration // return TRUE; } BOOL MatchPattern( PUCHAR String, PUCHAR Pattern ) { INT c, p, l; for (; ;) { switch (p = *Pattern++) { case 0: // end of pattern return *String ? FALSE : TRUE; // if end of string TRUE case '*': while (*String) { // match zero or more char if (MatchPattern (String++, Pattern)) return TRUE; } return MatchPattern (String, Pattern); case '?': if (*String++ == 0) // match any one char return FALSE; // not end of string break; case '[': if ( (c = *String++) == 0) // match char set return FALSE; // syntax c = toupper(c); l = 0; while (p = *Pattern++) { if (p == ']') // if end of char set, then return FALSE; // no match found if (p == '-') { // check a range of chars? p = *Pattern; // get high limit of range if (p == 0 || p == ']') return FALSE; // syntax if (c >= l && c <= p) break; // if in range, move on } l = p; if (c == p) // if char matches this element break; // move on } while (p && p != ']') // got a match in char set p = *Pattern++; // skip to end of set break; default: c = *String++; if (toupper(c) != p) // check for exact char return FALSE; // not a match break; } } } BOOL EmptyProcessWorkingSet( DWORD pid ) { HANDLE hProcess; SIZE_T dwMinimumWorkingSetSize; SIZE_T dwMaximumWorkingSetSize; hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid ); if (hProcess == NULL) { return FALSE; } if (!GetProcessWorkingSetSize( hProcess, &dwMinimumWorkingSetSize, &dwMaximumWorkingSetSize )) { CloseHandle( hProcess ); return FALSE; } SetProcessWorkingSetSize( hProcess, 0xffffffff, 0xffffffff ); CloseHandle( hProcess ); return TRUE; } BOOL EmptySystemWorkingSet( VOID ) { SYSTEM_FILECACHE_INFORMATION info; NTSTATUS status; info.MinimumWorkingSet = 0xffffffff; info.MaximumWorkingSet = 0xffffffff; if (!NT_SUCCESS (status = NtSetSystemInformation( SystemFileCacheInformation, &info, sizeof (info)))) { return FALSE; } return TRUE; }