I am trying to close a C# .NET 4 WPF application from a c++ application. The C++ app uses the standard technique of enumerating windows, finding the one that corresponds to a gi
I did find the answer eventually, and it was quite simple: the problem was the code related to "finding the one that corresponds to a given process ID". Issue is that I don't do much low level win32 stuff, so I missed an important detail. Here is what was happening and solution, in case it helps someone:
The C++ function closeProc() opens a handle to the existing process that must be closed, and causes a callback function requestMainWindowClose() to be called for each Window found by a win32 function EnumWindows, and assumes that requestMainWindowClose() has sent the close message to the process of interest, so it waits for the process to exit. If the process doesn't exit within a certain time, it will try to terminate it forcefully via TerminateProcess(). If that still doesn't work, it gives up. The closeProc() looks like this:
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 ) ;
}
The problem was in requestMainWindowClose, here was the original code:
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;
}
As defined above the callback function determines the process ID of the Window handle given to it (nextWindow) by EnumWindows() and compares to the desired process we want to close (closePid). If there is a match, the function sends it a CLOSE message and returns.
All is good so far. The problem is that it returns false, so EnumWindows() only ever sends the message to one window of the process, AND it looks like WPF applications have multiple windows: even if your code only creates one window, hidden windows get created behind the scenes by WPF. They are all found by EnumWindows; but the first one is rarely if ever the main application window. So requestMainWindowClose() was never sending the CLOSE to the main window of my WPF app, never got a chance.
Indeed the fix was that simple, ie don't return 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.