我最终确实找到了答案,而且非常简单:问题是与“查找与给定进程 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;
}
Only the top app window of a WPF will respond to the CLOSE message.