为什么我的 Windows API 回调函数 运行 没有?

Why doesn't my Windows API callback function run?

我写了一个终止 egui.exe 的代码。但是在处理 WM_CLOSE 消息后,我的回调函数不会 运行 。为什么?

#include <iostream>
#include <windows.h>
#include <psapi.h>

using namespace std;

HWND ESETWindow = 0;    //Variable to store a handle to the main window
                        // of ESET Smart Security
HWND ConfirmationWindow = 0;    //Variable to store a handle to the
                                // confirmation dialog box that appears
                                // when a WM_CLOSE message is sent to
                                // the ESET window.
DWORD EGUIPID = 0;  //Variable to store the process identifier of egui.exe
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam);
VOID SendAsyncProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult);

int main()
{
    cout << "Terminating ESET Smart Security..." << endl;

    //Obtain an array of process identifiers including the PID of egui.exe
    DWORD PIDs[1024]; //The array of process identifiers
    DWORD bytesReturned;
    if (!EnumProcesses(PIDs, sizeof(PIDs), &bytesReturned))
    {
        cerr << "Unable to enumerate the processes." << endl;
        return 0;
    }

    //Enumerate the PIDs array to find the process of egui.exe.
    DWORD nProcesses = bytesReturned / sizeof(DWORD);
    for (DWORD i = 0;i < nProcesses;i++)
    {
        //Open the process to examine its executable file name.
        HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PIDs[i]);
        //Create a buffer for the name of the executable file.
        PSTR processName = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) 10, MEM_COMMIT, PAGE_READWRITE);
        //Get the name of the executable file name.
        GetModuleBaseName(process, NULL, processName, 10);
        //Check if the executable file name is egui.exe.
        if (!lstrcmpi(processName, TEXT("egui.exe")))
        {
            EGUIPID = PIDs[i];
            break;
        }
        VirtualFree(processName, 0, MEM_RELEASE);
        CloseHandle(process);
    }

    //Display an error message if the process identifier of egui.exe
    //could not be found.
    if (!EGUIPID)
    {
        cerr << "Unable to find process identifier of egui.exe" << endl;
        return 0;
    }

    //Enumerate the top-level windows to find the main window of
    // ESET Smart Security and store a handle to that window in the
    // ESETWindow variable.
    EnumWindows(EnumWindowsProc, 0);
    //Display an error message if the window could not be found.
    if (!ESETWindow)
    {
        cerr << "Unable to find the primary window of ESET Smart Security." << endl;
        return 0;
    }

    //Send a WM_CLOSE message to the main window of egui.exe in order
    // to display a confirmation dialog box.
    if (!SendMessageCallback(ESETWindow, WM_CLOSE, 0, 0, SendAsyncProc, 0))
    {
        cerr << "Unable to send a WM_CLOSE message to the primary window of ESET Smart Security." << endl;
        return 0;
    }

    //Wait a second for the confirmation dialog box to appear...
    Sleep(1000);
    //Enumerate the windows again to find the confirmation dialog box.
    EnumWindows(EnumWindowsProc, 1);
    //Display an error message if the confirmation dialog box
    // could not be found.
    if (!ConfirmationWindow)
    {
        cerr << "Unable to find confirmation message." << endl;
        cout << "If you have ESET NOD32, it is probably terminated successfully.";
        cin.get();
    }

    //Find the Yes button in the confirmation dialog box and display
    // an error message if failed.
    HWND button = FindWindowEx(ConfirmationWindow, NULL, NULL, TEXT("&Yes"));
    if (!button)
    {
        cerr << "Unable to find Yes button in the message box." << endl;
        return 0;
    }

    //Activate the confirmation dialog box and simulate a mouse click
    // on the Yes button.
    SetActiveWindow(ConfirmationWindow);
    SendMessage(button, BM_CLICK, 0, 0);
    cout << "ESET Smart Security was successfully terminated!";

    //Keep the program running until the user presses Enter
    // in the console window.
    cin.get();
    return 0;
}

//If lParam is 0, the function below finds the main window of
//ESET Smart Security, otherwise, it finds the confirmation
//dialog box.
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    //Check if hwnd belongs to a window of egui.exe.
    DWORD PID = 0;
    GetWindowThreadProcessId(hwnd, &PID);
    if (PID != EGUIPID)
        return TRUE; //Exit function and continue the enumeration.

    //Check if the title of the window is "ESET Smart Security"
    // or "ESET Node32 Antivirus".
    int len = GetWindowTextLength(hwnd);
    PSTR title = (PSTR) VirtualAlloc((LPVOID) NULL, (DWORD) (len + 1), MEM_COMMIT, PAGE_READWRITE);
    GetWindowText(hwnd, title, (len + 1));
    if ((lstrcmp(title, TEXT("ESET Smart Security"))) && (lstrcmp(title, TEXT("ESET NOD32 Antivirus"))))
        return TRUE; //Exit function and continue the enumeration.

    if (lParam)
    {
        //If lParam is nonzero and hwnd refers to the main
        // window of egui.exe, exit function and continue
        // the enumeration.
        if (hwnd == ESETWindow)
            return TRUE;
        //Otherwise hwnd refers to the confirmation dialog box.
        //So store it in the ConfirmationWindow variable.
        ConfirmationWindow = hwnd;
        return FALSE; //Exit function and stop the enumeration.
    }
    else
    {
        //hwnd refers to the main window of ESET Smart Security.
        //So store it in the ESETWindow variable.
        ESETWindow = hwnd;
        return FALSE; //Exit function and stop the enumeration.
    }
}
VOID SendAsyncProc(HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult)
{
    MessageBox(0, TEXT("ESET Smart Security was successfully terminated."), TEXT("Result"), MB_OK);
}

虽然这个程序完美地完成了它的任务,但回调函数 SendAsyncProc 不会在 WM_CLOSE 消息处理完并且 egui.exe 终止后 运行。你能告诉我为什么吗?

SendMessageCallback 的文档告诉您,为什么您的回调永远不会被调用:

If the target window belongs to a different thread from the caller, then the callback function is called only when the thread that called SendMessageCallback also calls GetMessage, PeekMessage, or WaitMessage.

目标window显然属于不同的线程,因为它运行在不同的进程中。您的代码不执行任何消息检索。没有回调。