gpt4 book ai didi

c# - WPF 应用程序不响应 WM_CLOSE

转载 作者:行者123 更新时间:2023-11-30 04:20:31 26 4
gpt4 key购买 nike

我正在尝试从 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 应用的正确方法吗?

最佳答案

最终我确实找到了答案,而且很简单:问题是与“找到与给定进程 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() 提供给它的窗口句柄 (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 消息。

关于c# - WPF 应用程序不响应 WM_CLOSE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15236912/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com