如何(最好)将WM_QUIT发布到正在运行的进程?

目标:在Windows下closures正在运行的32位GUI进程

  • 我有权访问可执行文件的path名。
  • 可能有多个此软件的副本运行,但只有一个从唯一的可执行文件path名开始。
  • 因为这个可执行文件有多个实例可以运行,所以在顶层窗口中简单的看一下就需要区分哪个可执行文件名实际上是对这个窗口负责的。

可能的方法:

枚举进程和线程,然后使用PostThreadMessage(thread, WM_QUIT, 0, 0)

  • 这是有道理的,但我担心用什么技术来区分“主线”

有这样一个方法的例子:

  • 这假设具有第一个创build时间的线程是主要的。
  • 这使用一个32位唯一的技巧来取消主线程。

枚举顶层窗口,获取进程标识,并将消息发送到窗口:

  • 这假定只有一个顶级窗口。

其他想法:

  • 我的目标应用程序是多语言的 – 所以看顶级窗口的名称似乎也不正确,因为我不知道它会说什么(它也是dynamic的,根据用户的设置)。

基本上,我想要的是一种确定的方式来告诉我的应用程序 – 从特定的可执行文件path名(参数不重要,但path)启动的特定实例closures。

有没有更好的办法:

  • 也许创build一个命名的信号来发信号?
  • 已注册的Windows消息广播(通过作为ATOM传递的path名)?
  • 其他一些IPC机制?

预先感谢您提供的任何想法…

下面是我为自己解决这个问题,与XP兼容,并且可以处理具有多个顶级窗口和多个线程的进程,假设目标进程正确地为自己处理WM_QUIT(它当然应该!)

我的目标是从C ++的Win32 API:

调用Shutdown(filename); 它调用GetProcessID(filename)来获取进程ID然后调用EnumerateWindowThreads(processID)以获得具有顶级窗口的线程集合(我们可以假设这是进程的“主要”线程),并使用PostThreadMessage(..., WM_QUIT, ...)要求每个人终止。

如果要调用GetExitCodeProcess(process_handle, &exit_code)可以在发布WM_QUIT消息之前打开进程ID的进程句柄。 只要确保在发布退出之前获取并保持打开一个进程句柄,以确保在完成之后您可以查询某些内容。

 DWORD Shutdown(const TCHAR * executable) { // assumption: zero id == not currently running... if (DWORD dwProcessID = GetProcessID(executable)) { for (DWORD dwThreadID : EnumerateWindowThreads(dwProcessID)) VERIFY(PostThreadMessage(dwThreadID, WM_QUIT, 0, 0)); } } // retrieves the (first) process ID of the given executable (or zero if not found) DWORD GetProcessID(const TCHAR * pszExePathName) { // attempt to create a snapshot of the currently running processes 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)) //throw CWin32APIErrorException(_T(__FUNCTION__), _T("QueryFullProcessImageName")); continue; } #else // since we require elevation, go ahead and try to read what we need directly out of the process' virtual memory if (auto hProcess = Toolbox::AutoHandle::AutoCloseHandle(OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, entry.th32ProcessID))) { if (!GetmoduleeFileNameEx(hProcess, nullptr, entry.szExeFile, countof(entry.szExeFile))) //throw CWin32APIErrorException(_T(__FUNCTION__), _T("GetmoduleeFileNameEx")); continue; } #endif if (compare_no_case(entry.szExeFile, pszExePathName) == STRCMP_EQUAL) return entry.th32ProcessID; // FOUND } return 0; // NOT FOUND } // returns the set of threads that have top level windows for the given process 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异常)。