如何(最好)将 WM_QUIT 发布到正在运行的进程? [英] How (best) to post WM_QUIT to a running process?

查看:41
本文介绍了如何(最好)将 WM_QUIT 发布到正在运行的进程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  • 我可以访问可执行路径名.
  • 可能有多个此软件在运行,但只有一个从唯一的可执行路径名开始.
  • 因为可以运行该可执行文件的多个实例,所以简单地查看顶级窗口需要区分哪个可执行文件路径名实际上负责该窗口...

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

Enumerate processes & threads, then use PostThreadMessage(thread, WM_QUIT, 0, 0)

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

有这种方法的例子:

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

Enumerate top level windows, obtain the process identity, and send the message to the window:

  • 我的目标应用程序是多语言的 - 因此查看顶级窗口的名称似乎也不正确...因为我不知道它会说什么(根据用户的设置,它也是动态的).

基本上,我想要的是一种可靠的方式来告诉我的应用程序 - 从特定的可执行路径名启动的特定实例(参数无关紧要 - 但路径有关系),关闭.

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.

有没有更好的方法:

  • 也许创建一个命名的信号量来发出信号?
  • 注册的 Windows 消息广播(路径名作为 ATOM 传递)?
  • 其他一些 IPC 机制?

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

Thanks in advance for any thoughts you might offer...

推荐答案

这里是我自己解决的,兼容XP,可以处理一个有多个顶层窗口和多个线程的进程,假设目标进程确实为自己正确处理了 WM_QUIT(它当然应该这样做!)

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!)

我的目标是来自 C++ 的 Win32 API:

I'm targeting Win32 API from C++:

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

call 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.

如果您想调用 GetExitCodeProcess(process_handle, &exit_code),您可以在发布 WM_QUIT 消息之前打开进程 ID 上的进程句柄.只需确保您在发布退出之前/期间获得并保持打开一个进程句柄,以确保您在完成后可以查询...

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). 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;
}

对于使用 Toolbox::AutoHandle::AutoCloseHandle 和我的各种异常类,我深表歉意.它们很简单 - AutoCloseHandle 是 HANDLE 的 RAII,并且存在异常类是因为我们的代码库早于标准库(而且标准库仍然无法处理 UNICODE 异常).

My apologies for using Toolbox::AutoHandle::AutoCloseHandle and my various exception classes. 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).

这篇关于如何(最好)将 WM_QUIT 发布到正在运行的进程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆