How is Ctrl-C message delivered to a process running on windows?
Asked Answered
F

2

7

I created a C# console application to handle Ctrl-C by subscribing it to Console.CancelKeyPress event. When I executed (debug version) the application, there were 14 threads already created in the process. When I pressed Ctrl-C a new, 15th thread was created and my handler was invoked. I used process explorer from sysinternals to view the state of the process.

I am curious to know the internals on how the Ctrl-C message is delivered to a process and how the additional thread gets created? I guess, even if I do not subscribe to an event, it will still create an additional thread and exit the process. How is the default mechanism for handling Ctrl-C is setup for an application.

I am a .net developer but want to understand on how the windows operating system works under the hood. The above question is only out of curiosity to learn windows operating system.

Farmhouse answered 4/12, 2011 at 16:27 Comment(0)
C
3

When Windows needs to notify a console program of an external event, there is no window message loop to send the notification to, so Windows will create a thread in the target process to execute whatever callback function is defined. The default handler for the CTRL+C event just calls ExitProcess, but hooking the CancelKeyPress event calls the Win32 SetConsoleCtrlHandler function with a handler function.

The documentation for the handler function explains how it works:

An application-defined function used with the SetConsoleCtrlHandler function. A console process uses this function to handle control signals received by the process. When the signal is received, the system creates a new thread in the process to execute the function.

Note that the thread that Windows injects into your process has a fairly small stack, so the CLR handler routine actually queues up a Threadpool work item to execute your event handler. This means that the thread injected by Windows and a worker thread could both be created, causing you to see up to 2 additional threads during the processing of the CTRL+C event.

Congratulation answered 4/12, 2011 at 17:10 Comment(7)
+1 - Useful. In absence of message loop how is the new thread injected in a process?Farmhouse
@AnandPatel: The CreateRemoteThread API creates a new thread in another process.Congratulation
Setting your response as answer to my question. I value the response from Hans Passant, but you were precise in the information that I was looking for. Could you share more information on why the thread that is injected has a small stack. Why not default of 1 MB thread stack size as other .net threads?Farmhouse
I found documentation that explained that the stack may be too small to run exception handlers on 64-bit systems, but I didn't see anything that said why the stack is so small. I would imagine that the idea is to use the fewest resources possible. Would you want CTRL+C to not work because there wasn't a contiguous 1MB block available in your address space?Congratulation
I set a break point in my CancelKeyPress event handler, and called AppDomain.GetCurrentThreadID() to get the unmanaged thread ID. I did a lookup of that thread in VMMap and found that reserved and committed size of stack for that thread is 1MB. I am not sure on how it is small stack size thread as compared to thread pool thread. I guess, the thread pool thread will be of same size. I did the above experiment on 32 bit system.Farmhouse
As I stated, your event handler always runs in a thread pool so you get a 1MB stack. The code that runs on the thread created by CreateRemoteThread is not your code. That said, the documentation I saw may not be accurate. It could be out of date, or only apply to architectures other than x64 or x86.Congratulation
My Mistake. Actually, I wanted to get the stack size of a thread that is injected by the system. The thread which queues an item on the thread pool. I need to experiment again for that.Farmhouse
B
1

Yes, Windows starts up a thread to call the handler that's registered by SetConsoleCtrlHandler(). Which is called by the Hook() method of a little internal helper class named ControlCHooker. Which is called by the add() accessor of the Cancel.CancelKeyPress event. The Windows callback makes your event handler run.

A good disassembler like Reflector or ILSpy as well as the Reference Source can help you discover these implementation details.

Band answered 4/12, 2011 at 16:48 Comment(2)
I know that CLR creates few threads for doing house keeping. For e.g. thread for doing garbage collection. If I am not wrong, those threads are created at the start of the application and not in the middle of a running application. I want to know on how that new thread (for handling Ctrl-C) is injected/created in a process?Farmhouse
Nope, both the CLR and Windows create threads on-the-fly as needed. CreateThread() and QueueUserWorkItem() are the primary winapi functions. Getting a thread created by Windows to execute managed code is supported at a low level by Marshal.GetFunctionPointerForDelegate().Band

© 2022 - 2024 — McMap. All rights reserved.