WPF应用程序不响应WM_CLOSE

WPF app does not respond to WM_CLOSE

本文关键字:CLOSE WM 不响应 应用程序 WPF      更新时间:2023-10-16

我试图从c++应用程序关闭c# . net 4 WPF应用程序。c++应用程序使用枚举窗口的标准技术,找到与给定进程ID对应的窗口,通过PostMessage向窗口发送WM_CLOSE,然后WaitForSingleObject(pid, 5000)。然而,我的WPF应用程序从未关闭,即WaitForSingleObject超时。

我的WPF应用程序覆盖了Window::OnClosed():

  • 如果我手动关闭WPF应用程序点击窗口的X,这个方法被调用。
  • 类似地,如果在Windows的任务管理器中的应用程序选项卡中,我在WPF进程上做"结束任务",则该方法被调用(显然在该选项卡上使用WM_CLOSE消息,而在进程选项卡上,结束任务使用WM_QUIT消息)。
  • 当我的c++应用程序发送WM_CLOSE时,这个方法永远不会被调用
  • 当我的c++应用程序发送WM_QUIT代替(所以所有c++源代码不变,除了发送的消息),我的WPF应用程序终止。
  • 我已经尝试在WPF应用程序中创建自己的WndProc()处理程序,如果我将鼠标移到WPF GUI上,方法确实会被调用,但是当我的c++应用程序发送WM_CLOSE时,这个方法永远不会被调用,这几乎就像我的WPF应用程序没有得到WM_CLOSE消息。
  • 我已经创建了一个c#应用程序,从那里我可以使用Process.GetProcessById(pid)和proc.CloseMainWindow(),这应该做与WM_CLOSE相同。这个是有效的:调用OnClosed()方法。

PostMessage(hwnd, WM_CLOSE)是从c++应用程序优雅地关闭WPF应用程序的正确方式吗?

我最终找到了答案,而且很简单:问题是与"找到与给定进程ID对应的那个"相关的代码。问题是,我不做太多低级的win32的东西,所以我错过了一个重要的细节。下面是发生的事情和解决方案,以防对某人有所帮助:

c++函数closeProc()打开一个必须关闭的现有进程的句柄,并为win32函数EnumWindows找到的每个窗口调用回调函数requestMainWindowClose(),并假设requestMainWindowClose()已经将关闭消息发送给感兴趣的进程,因此它等待进程退出。如果进程没有在一定时间内退出,它将尝试通过TerminateProcess()强制终止它。如果仍然不起作用,它就会放弃。closeProc()看起来像这样:

void closeProc()
{
    HANDLE ps = OpenProcess( SYNCHRONIZE | PROCESS_TERMINATE, FALSE, dwProcessId );
    if (ps == NULL)
        throw std::runtime_error(...);
    EnumWindows( requestMainWindowClose, dwProcessId );
    static const int MAX_WAIT_MILLISEC = 5000; 
    const DWORD result = WaitForSingleObject(ps, MAX_WAIT_MILLISEC);
    if (result != WAIT_OBJECT_0)
    {
        if (result == WAIT_TIMEOUT)
        {
            LOGF_ERROR("Could not clcose proc (PID %s): did not exit within %s ms",
                dwProcessId << MAX_WAIT_MILLISEC);
        }
        else
        {
            LOGF_ERROR("Could not close proc (PID %s): %s", 
                dwProcessId << getLastWin32Error());
        }
        LOGF_ERROR("Trying to *terminate* proc (PID %s)", dwProcessId);
        if (TerminateProcess(ps, 0))
            exited = true;
        }
    }
    CloseHandle( ps ) ;
}

问题是在requestMainWindowClose,这里是原始代码:

BOOL CALLBACK 
requestMainWindowClose( HWND nextWindow, LPARAM closePid )
{
    DWORD windowPid;
    GetWindowThreadProcessId(nextWindow, &windowPid);
    if ( windowPid==(DWORD)closePid )
    {
        ::PostMessage( nextWindow, WM_CLOSE, 0, 0 );
        return false;
    }
    return true;
}

如上所述,回调函数确定EnumWindows()给出的窗口句柄的进程ID (nextWindow),并与我们想要关闭的期望进程(closePid)进行比较。如果存在匹配,则该函数发送一个CLOSE消息并返回。

到目前为止一切都很好。问题是它返回false,因此EnumWindows()只将消息发送到进程的一个窗口,并且看起来WPF应用程序有多个窗口:即使您的代码只创建一个窗口,隐藏窗口也会在后台由WPF创建。它们都是通过EnumWindows找到的;但是第一个窗口很少是应用程序的主窗口。所以requestMainWindowClose()从来没有发送CLOSE到我的WPF应用程序的主窗口,从来没有机会。

实际上修复就是这么简单,即不返回false:

BOOL CALLBACK 
requestMainWindowClose( HWND nextWindow, LPARAM closePid )
{
    DWORD windowPid;
    GetWindowThreadProcessId( nextWindow, &windowPid );
    if ( windowPid==(DWORD)closePid )  
        ::PostMessage( nextWindow, WM_CLOSE, 0, 0 );
    return true; 
}

只有WPF的顶部应用窗口会响应CLOSE消息。

与系统菜单上的Close命令直接对应的是:

PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);

你可以试试这个