这是我自己解决问题的方法,可以与XP兼容,并处理具有多个顶层窗口和多个线程的进程,假设目标进程正确地处理了自身的WM_QUIT(这当然应该这样做!)。
我使用C ++从Win32 API中选择:
调用
Shutdown(filename);
。它会调用
GetProcessID(filename)
以获取进程ID,然后调用
EnumerateWindowThreads(processID)
来获取具有顶级窗口的线程集(我们可以认为是进程的“主线程”),并使用
PostThreadMessage(...,WM_QUIT,...)
要求它们终止。
如果你想调用
GetExitCodeProcess(process_handle,&exit_code)
,可以在发送
WM_QUIT
消息之前打开一个进程句柄。只需确保在发布退出消息之前/同时获得并保持进程句柄,以确保在完成后有可查询的东西...
DWORD Shutdown(const TCHAR * executable) {
if (DWORD dwProcessID = GetProcessID(executable)) {
for (DWORD dwThreadID : EnumerateWindowThreads(dwProcessID))
VERIFY(PostThreadMessage(dwThreadID, WM_QUIT, 0, 0));
}
}
DWORD GetProcessID(const TCHAR * pszExePathName) {
Toolbox::AutoHandle::AutoCloseFile snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
if (!snapshot)
throw CWin32APIErrorException(_T(__FUNCTION__), _T("CreateToolhelp32Snapshot"));
PROCESSENTRY32 entry = { sizeof(PROCESSENTRY32), 0 };
for (BOOL bContinue = Process32First(snapshot, &entry); bContinue; bContinue = Process32Next(snapshot, &entry)) {
#if (_WIN32_WINNT >= 0x0600)
static const BOOL isWow64 = IsWow64();
if (isWow64) {
Toolbox::AutoHandle::AutoCloseHandle hProcess(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, entry.th32ProcessID));
DWORD dwSize = countof(entry.szExeFile);
if (!QueryFullProcessImageName(hProcess, 0, entry.szExeFile, dwSize))
continue;
}
#else
if (auto hProcess = Toolbox::AutoHandle::AutoCloseHandle(OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, entry.th32ProcessID))) {
if (!GetModuleFileNameEx(hProcess, nullptr, entry.szExeFile, countof(entry.szExeFile)))
continue;
}
#endif
if (compare_no_case(entry.szExeFile, pszExePathName) == STRCMP_EQUAL)
return entry.th32ProcessID;
}
return 0;
}
std::set<DWORD> EnumerateWindowThreads(DWORD dwProcessID) {
if (!dwProcessID)
throw CLabeledException(_T(__FUNCTION__) _T(" invalid process id (0)"));
std::set<DWORD> threads;
for (HWND hwnd = GetTopWindow(NULL); hwnd; hwnd = ::GetNextWindow(hwnd, GW_HWNDNEXT)) {
DWORD dwWindowProcessID;
DWORD dwThreadID = ::GetWindowThreadProcessId(hwnd, &dwWindowProcessID);
if (dwWindowProcessID == dwProcessID)
threads.emplace(dwThreadID);
}
return threads;
}
对于我使用的Toolbox :: AutoHandle :: AutoCloseHandle
和各种异常类,我深表歉意。它们都很简单- AutoCloseHandle是HANDLE的RAII实现,异常类的存在是因为我们的代码库早于标准库(而且标准库仍然无法处理UNICODE异常)。
WM_QUIT
消息发送给应用程序的主窗口而不是线程。期望应用程序知道如何释放自己的资源并关闭自己的线程。 - John DiblingWM_QUIT
消息。在同一线程中运行的多个窗口将只是将冗余消息放入相同的队列中,但只需要其中一个消息就可以结束该线程的消息循环。 - Remy Lebeau