简体   繁体   English

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

[英]How (best) to post WM_QUIT to a running process?

Goal: Shut down a running 32 bit GUI process under windows目标:关闭 Windows 下正在运行的 32 位 GUI 进程

  • I have access to the executable pathname.我可以访问可执行路径名。
  • There are potentially more than one copy of this software running, but only one started from a unique executable pathname.可能有多个此软件在运行,但只有一个从唯一的可执行路径名开始。
  • Because more than one instance of this executable can be running, a simple look at top level windows would need to distinguish between which executable pathname actually is responsible for that window...因为这个可执行文件的多个实例可以运行,简单地查看顶级窗口需要区分哪个可执行文件路径名实际上负责该窗口......

Possible Approaches:可能的方法:

Enumerate processes & threads, then use PostThreadMessage(thread, WM_QUIT, 0, 0)枚举进程和线程,然后使用PostThreadMessage(thread, WM_QUIT, 0, 0)

  • This makes sense, but I'm worried about what technique to use to distinguish "the main thread"这是有道理的,但我担心使用什么技术来区分“主线程”

There are examples of such an approach:有这种方法的例子:

Enumerate top level windows, obtain the process identity, and send the message to the window:枚举顶级窗口,获取进程标识,并将消息发送到窗口:

Other Thoughts:其他想法:

  • My target application is multilingual - so looking at the name of the top level window seems incorrect as well... since I won't know what it will say (it is also dynamic according to the user's settings).我的目标应用程序是多语言的 - 所以查看顶级窗口的名称似乎也不正确......因为我不知道它会说什么(根据用户的设置它也是动态的)。

Basically, what I want is a sure-fire way to tell my application - the specific instance that is started from a specific executable pathname (arguments don't matter - but path does), to shut down.基本上,我想要的是一种可靠的方式来告诉我的应用程序 - 从特定的可执行路径名(参数无关紧要 - 但路径重要)启动的特定实例,关闭。

Is there a better way entirely:有没有更好的方法:

  • Maybe creating a named semaphore to signal?也许创建一个命名的信号量来发出信号?
  • Registered Windows message broadcast (with the pathname passed through as an ATOM)?注册的 Windows 消息广播(路径名作为 ATOM 传递)?
  • Some other IPC mechanism?其他一些 IPC 机制?

Thanks in advance for any thoughts you might offer...预先感谢您提供的任何想法...

Here's how I solved it for myself, compatible with XP, and can deal with a process that has multiple top-level windows and multiple threads, assuming that the target process does correctly handle WM_QUIT for itself (which it certainly should!)这是我自己解决的方法,与 XP 兼容,并且可以处理具有多个顶级窗口和多个线程的进程,假设目标进程确实为自己正确处理了 WM_QUIT(它当然应该这样做!)

I'm targeting Win32 API from C++:我的目标是来自 C++ 的 Win32 API:

call Shutdown(filename);调用Shutdown(filename); That calls GetProcessID(filename) to get the process ID And then calls EnumerateWindowThreads(processID) in order to get the set of threads with top level windows (which we can assume are 'main' threads for the process), and uses PostThreadMessage(..., WM_QUIT, ...) to ask each of them to terminate.调用GetProcessID(filename)来获取进程 ID 然后调用EnumerateWindowThreads(processID)以获取具有顶级窗口的线程集(我们可以假设它们是进程的“主”线程),并使用PostThreadMessage(..., WM_QUIT, ...)要求他们每个人终止。

You can open a process handle on the process ID before posting the WM_QUIT messages if you want to call GetExitCodeProcess(process_handle, &exit_code) .如果您想调用GetExitCodeProcess(process_handle, &exit_code)您可以在发布WM_QUIT消息之前打开进程 ID 上的进程句柄。 Just make sure you obtain and hold open a process handle before/while you're posting the quits in order to ensure you have something to query after it is done...只需确保在发布退出之前/期间获得并保持打开一个进程句柄,以确保在完成后有一些东西要查询......

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 (!GetModuleFileNameEx(hProcess, nullptr, entry.szExeFile, countof(entry.szExeFile)))
                //throw CWin32APIErrorException(_T(__FUNCTION__), _T("GetModuleFileNameEx"));
                    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;
}

My apologies for using Toolbox::AutoHandle::AutoCloseHandle and my various exception classes.我很抱歉使用Toolbox::AutoHandle::AutoCloseHandle和我的各种异常类。 They're trivial - AutoCloseHandle is RAII for HANDLE, and the exception classes exist because our code base predates the standard library (and the standard library still can't deal with UNICODE exceptions anyway).它们是微不足道的 - AutoCloseHandle 是 HANDLE 的 RAII,并且存在异常类是因为我们的代码库早于标准库(而且标准库仍然无法处理 UNICODE 异常)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM