WPF应用程序不响应WM_CLOSE

时间:2013-03-06 00:40:21

标签: c# c++ wpf

我正在尝试从c ++应用程序中关闭C#.NET 4 WPF应用程序。 C ++应用程序使用枚举窗口的标准技术,找到与给定进程ID对应的那个,通过PostMessage向窗口发送WM_CLOSE,然后使用WaitForSingleObject(pid,5000)。但是,我的WPF应用程序永远不会关闭,即WaitForSingleObject超时。

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

  • 如果我通过单击窗口的X手动关闭WPF应用程序,则会调用此方法。
  • 同样,如果在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应用程序的正确方法吗?

2 个答案:

答案 0 :(得分:1)

系统菜单上的“关闭”命令的直接等效项为:

PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);

你可以试试。

答案 1 :(得分:1)

我最终确实找到了答案,而且非常简单:问题是与“找到与给定进程ID相对应的代码”相关的代码。问题是我没有做太多低级别的win32,所以我错过了一个重要的细节。以下是发生的事情和解决方案,以防有人帮助:

C ++函数closeProc()打开一个必须关闭的现有进程的句柄,并为win32函数EnumWindows找到的每个Window调用一个回调函数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()给它的Window句柄(nextWindow)的进程ID,并与我们想要关闭的所需进程(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消息。