老派 Win32 Message Only Window 无法接收消息

Old school Win32 Message Only Window cannot receive messages

我意识到控制台 win32 应用程序没有完全退出,所以我尝试切换到仅消息 windows。我正在从另一个进程启动该应用程序并试图将其彻底终止。

这是 win32 应用程序,它在启动和干净关闭时生成 calc.exe,它应该会杀死 calc.exe

LRESULT CALLBACK    WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_USER:   PostQuitMessage (0); break;
    default: return DefWindowProc (hWnd, message, wParam, lParam);break;
    }
    return 0;
}

int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int show)
{
    WNDCLASSEX wc = { 0 };
    wc.cbSize        = sizeof (WNDCLASSEX);
    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = L"WindowClass1";

    RegisterClassEx (&wc);

    HWND hWnd = CreateWindowEx (NULL,
                L"WindowClass1",    // name of the window class
                L"Our First Windowed Program",   // title of the window
                0,
                //WS_OVERLAPPEDWINDOW,    // window style
                300,300,500,400,
                HWND_MESSAGE,
                //NULL,    // parent window, NULL
                NULL, hInstance, NULL);

    //ShowWindow (hWnd, SW_HIDE);

    PROCESS_INFORMATION pi;
    CreateWindowProcess (L"calc.exe", pi); // helper class to createprocess

    MSG msg = { 0 };
    while (true)
    {
        if (PeekMessage (&msg, 0, 0, 0, PM_REMOVE))
        {
            if (WM_QUIT == msg.message)
                break;
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        }
    }

    // terminate the spawn process, this is not called cleanly
    TerminateProcess (pi.hProcess, 0);
    return (int)msg.wParam;
}

我通过发送 WM_QUIT/WM_CLOSE/WM_USER 消息为 start/kill 应用程序创建了一个 c# 程序(calc.exe 被破坏)。除非 window 可见(WS_OVERLAPPED 且 ShowWindow 为真),否则 Win32 应用程序不会接收消息。收到 PostMessage WM_QUIT 但 calc.exe 没有被销毁,这意味着它不是一个干净的出口。

我应该如何从 C# 应用程序中彻底杀死它?

    class Program
    {
        [DllImport ("user32.dll")]
        public static extern bool    PostMessage (IntPtr hwnd, uint msg, int wparam, int lparam);

        [DllImport ("User32.dll")]
        public static extern int     SendMessage (IntPtr hWnd, uint uMsg, int wParam, int lParam);

        static void                                                             Main (string [] args)
        {
            try
            {
                Process myProcess;
                myProcess = Process.Start ("My.exe");

                // Display physical memory usage 5 times at intervals of 2 seconds.
                for (int i = 0; i < 3; i++)
                {
                    if (myProcess.HasExited) break;
                    else
                    {
                        // Discard cached information about the process.
                        myProcess.Refresh ();
                        Thread.Sleep (4000);

                        Console.WriteLine ("Sending Message");

                        const int WM_USER = 0x0400;
                        const int WM_CLOSE = 0xF060;      // Command code for close window
                        const int WM_QUIT = 0x0012;

                        // Received only when windows is visible
                        //int result = SendMessage (myProcess.MainWindowHandle, WM_USER, 0, 0);

                        // not clean exit
                        PostMessage (myProcess.MainWindowHandle, WM_QUIT, 0, 0);
                        // doesn't receive
                        SendMessage (myProcess.MainWindowHandle, WM_QUIT, 0, 0);
                    }
                }
            }
    }
}

Windows 上的进程实际上没有 "MainWindow"。 Process.MainWindowHandle 是 C# 进行的猜测,它通过查看所讨论的进程是否具有带焦点的 window 来进行猜测 - 它只会找到可见的 windows。使用 FindWindowEx 找到您要关闭的 window 句柄。

接下来,当一个window关闭时,它不会自动尝试退出当前线程的消息循环。您需要处理 WM_DESTROY 才能调用 PostQuitMessage.

如果您不做任何其他工作,请在您的消息循环中使用 GetMessage 而不是 PeekMessage PeekMessage returns 如果没有消息意味着应用程序线程将永远没有机会休眠。

有了这些更改,您应该可以简单地 post 一个 WM_CLOSE 到有效的 window 句柄,因为它将被销毁,post 本身是一个 WM-QUIT消息退出消息循环,正确终止计算过程。