GenerateConsoleCtrlEvent creates "zombie" process handle in conhost.exe #511

Open
opened 2026-01-30 21:54:00 +00:00 by claunia · 2 comments
Owner

Originally created by @cimclees on GitHub (Jan 11, 2019).

  • Windows build number: 10.0.17763.195

  • What you're doing and what's happening: I have an application that starts and stops many sub processes. We use 'GenerateConsoleCtrlEvent' to stop sub processes. Every time we use this function, the conhost.exe process associated with our main / parent process acquires a "zombie" process handle to the stopped sub process.

Here is sample code illustrating the issue which opens notepad, sends a Ctrl-C signal, and then terminates it:

#include <Windows.h>

int main(int argc, char *argv[])
{
    STARTUPINFO startInfo;
    PROCESS_INFORMATION processInfo;
    ZeroMemory(&startInfo, sizeof(startInfo));
    startInfo.cb = sizeof(startInfo);
    ZeroMemory(&processInfo, sizeof(processInfo));

    WCHAR *f = L"c:\\windows\\notepad.exe";

    BOOL bres = CreateProcess(f, nullptr, nullptr, nullptr, FALSE, CREATE_NEW_PROCESS_GROUP, nullptr, nullptr, &startInfo, &processInfo);

    DWORD  dw = GenerateConsoleCtrlEvent(CTRL_C_EVENT, processInfo.dwProcessId);

    TerminateProcess(processInfo.hProcess, 0);

    DWORD status = WaitForSingleObject(processInfo.hProcess, INFINITE);

    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);

    // Observe zombie process handle in Process Explorer, owned by conhost.exe process associated with this process

    Sleep(1000 * 60 * 2);

    return 0;
}

If we open Process Explorer and view the handles owned by the conhost.exe process associated with the sample program ("Sandbox.exe" in my case):
image

We see that there is a "zombie" process handle to the terminated notepad process:
image

If you step through the sample code, you can see that the process handle is opened on the call to 'GenerateConsoleCtrlEvent'.

  • What's wrong / what should be happening instead: 'GenerateConsoleCtrlEvent' should not leave an open process handle to the target process after invocation.
Originally created by @cimclees on GitHub (Jan 11, 2019). * Windows build number: 10.0.17763.195 * What you're doing and what's happening: I have an application that starts and stops many sub processes. We use 'GenerateConsoleCtrlEvent' to stop sub processes. Every time we use this function, the conhost.exe process associated with our main / parent process acquires a "zombie" process handle to the stopped sub process. Here is sample code illustrating the issue which opens notepad, sends a Ctrl-C signal, and then terminates it: ``` #include <Windows.h> int main(int argc, char *argv[]) { STARTUPINFO startInfo; PROCESS_INFORMATION processInfo; ZeroMemory(&startInfo, sizeof(startInfo)); startInfo.cb = sizeof(startInfo); ZeroMemory(&processInfo, sizeof(processInfo)); WCHAR *f = L"c:\\windows\\notepad.exe"; BOOL bres = CreateProcess(f, nullptr, nullptr, nullptr, FALSE, CREATE_NEW_PROCESS_GROUP, nullptr, nullptr, &startInfo, &processInfo); DWORD dw = GenerateConsoleCtrlEvent(CTRL_C_EVENT, processInfo.dwProcessId); TerminateProcess(processInfo.hProcess, 0); DWORD status = WaitForSingleObject(processInfo.hProcess, INFINITE); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); // Observe zombie process handle in Process Explorer, owned by conhost.exe process associated with this process Sleep(1000 * 60 * 2); return 0; } ``` If we open Process Explorer and view the handles owned by the conhost.exe process associated with the sample program ("Sandbox.exe" in my case): ![image](https://user-images.githubusercontent.com/8868121/51007609-fcf8f900-14fc-11e9-9da9-3145e7cf4fcb.png) We see that there is a "zombie" process handle to the terminated notepad process: ![image](https://user-images.githubusercontent.com/8868121/51007576-dcc93a00-14fc-11e9-8564-805b2bbdf3e5.png) If you step through the sample code, you can see that the process handle is opened on the call to 'GenerateConsoleCtrlEvent'. * What's wrong / what should be happening instead: 'GenerateConsoleCtrlEvent' should not leave an open process handle to the target process after invocation.
claunia added the Product-ConhostIssue-BugArea-Server labels 2026-01-30 21:54:01 +00:00
Author
Owner

@eryksun commented on GitHub (Jan 11, 2019):

See #335 for more information about this long-standing bug , which I think dates back to Windows XP. We don't need CREATE_NEW_PROCESS_GROUP to observe the problem. GenerateConsoleCtrlEvent will succeed without it because the notepad.exe process is a child of a process that's attached to the console. On the other hand, if the notepad.exe process is created as a new group by a parent that's not attached to the console, then the call fails with ERROR_INVALID_PARAMETER. That's as it should be. The call should always fail if no process is attached to the console that belongs to the given console process group.

@eryksun commented on GitHub (Jan 11, 2019): See #335 for more information about this long-standing bug , which I think dates back to Windows XP. We don't need `CREATE_NEW_PROCESS_GROUP` to observe the problem. `GenerateConsoleCtrlEvent` will succeed without it because the notepad.exe process is a child of a process that's attached to the console. On the other hand, if the notepad.exe process is created as a new group by a parent that's not attached to the console, then the call fails with `ERROR_INVALID_PARAMETER`. That's as it should be. The call should always fail if no process is attached to the console that belongs to the given console process group.
Author
Owner

@cimclees commented on GitHub (Jan 11, 2019):

Interesting. Our child processes are indeed console-less background services. We use SetConsoleCtrlHandler in the child processes to implement our own handling of control signals. This has worked fine besides this handle leak issue, but I guess it is not the right approach. We will look in to alternative ways to communicate between processes.

@cimclees commented on GitHub (Jan 11, 2019): Interesting. Our child processes are indeed console-less background services. We use `SetConsoleCtrlHandler` in the child processes to implement our own handling of control signals. This has worked fine besides this handle leak issue, but I guess it is not the right approach. We will look in to alternative ways to communicate between processes.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#511