While programming in a desktop application which uses a legacy COM object to communicate with IVR, a colleague encountered the need to determine “End Task” call for his program to cleanup after a RCW interop and catch respective exceptions.
As defined in MSKB 77135 “If the user chooses End Task in the Windows Task Manager, the Task Manager posts a WM_CLOSE message to the application's main window” and “People need to remember that the .NET Framework's OnClosing() is the managed version of Win32's WM_CLOSE and not WM_DESTROY.” It leads to a conclusion that a .NET application needs to encounter the message queue as Win32 SDK app would.
Microsoft provides an example in .NET Framework Class Library Reference, Control.WndProc Method. It overrides the WndProc method and handles the window messages to know how an application become active. Here's the source from MSDN.
protected override void WndProc(ref Message m) { // Listen for operating system messages. switch (m.Msg) { // The WM_ACTIVATEAPP message occurs when the application // becomes the active application or becomes inactive. case WM_ACTIVATEAPP: // The WParam value identifies what is occurring. appActive = (((int)m.WParam != 0)); // Invalidate to get new text painted. this.Invalidate(); break; } base.WndProc(ref m); }
Extending this implementation, one can handle further windows messages in a managed code application like in traditional win32 SDK App. Here’s a Win32 scenario of handling window Messages and getting over WM_Close conditionally.
End Task and End process acts differently; for further details of process termination handling in C#, this thread (How to take actions upon program termination) will be helpful.
“The main function of the message pump is the GetMessage fuction, it gets the message from the queue (placing in a MSG struct). And returns a non-zero value, however if the message is WM_QUIT it returns 0 (thus breaking the loop). And with the loop broken the message never gets translated or dispatched (to the callback procedure). (That's the way it works in c(++) code, none of this fancy "managed code")
But once out of the loop, execution continues normally. Which means that you could nest the message pump (loop) inside of another while loop so it would start right back up.
It is true that the NT Task Manager sends the WM_CLOSE message to the window, but that is only on the Applications tab. On the Processes tab it uses the (appearently unstoppable) TerminateProcess function which performs a karate chop on the process (without the use of messages).
Here is what happens (from msdn):
Terminating a process causes the following:
1. All object handles opened by the process are closed.
2. All threads in the process terminate their execution. Each thread exits when all its pending I/O has been completed or canceled. The process exits after all its threads exit.
3. The state of the process object becomes signaled, satisfying any threads that had been waiting for the process to terminate. The process object is deleted when the last handle to the process is closed.
4. The states of all threads of the process become signaled, satisfying any threads that had been waiting for the threads to terminate.
5. The termination status of the process changes from STILL_ACTIVE to the exit value of the process.
Further the info for TerminateProcess says:
"Neither the process nor any DLLs attached to the process are notified that the process is terminating. A process cannot prevent itself from being terminated.
Terminating a process does not generate notifications for WH_CBT hook procedures."
The following listing explains it further; text and listing courtesy Ken Nign.
/* Include some standard headers */
#include <WINDOWS.H>
#include <STDIO.H>
#include <STRING.H>
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
case WM_CLOSE:
MessageBox(NULL, "WM_CLOSE", "Testing", 0);
PostQuitMessage(WM_QUIT);
return 0;
default:
//MessageBox(NULL, strMsg, "Testing", 0);
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 1;
/* Our main entry point */
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
int i;
MSG Msg;
WNDCLASSEX WndClsEx;
HWND hWnd;
/* Init the winclass for the main window */
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProc;
WndClsEx.cbClsExtra = 0;
WndClsEx.cbWndExtra = 0;
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
//WndClsEx.hIcon = LoadIcon(hInstance, "A_VPICON");
WndClsEx.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
WndClsEx.hInstance = hInstance;
WndClsEx.lpszMenuName = "IDR_MAINFRAME";
WndClsEx.lpszClassName = "Msgtest";
//WndClsEx.hIconSm = LoadIcon(hInstance, "A_VPICON");
RegisterClassEx(&WndClsEx);
hWnd = CreateWindow("Msgtest",
"Win cap",
WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE,
0, 0,
490, 200,
NULL,
hInstance,
NULL);
/* Finally show the window */
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
printf("hi low from above the message pump\n\n");
//MessageBox(NULL, "testing", "MB", 0);
while( GetMessage(&Msg, NULL, 0, 0) )
printf("%li\n", Msg.message);
TranslateMessage(&Msg);
DispatchMessage(&Msg);
} // while's
MessageBox(NULL, "testing below the pump", "MB", 0);
ExitProcess(0);
Rui Reis's also have demonstrated handling of Windows Messages in Managed code which would be a useful reading.