Created
March 30, 2024 14:56
-
-
Save asmichi/a37622e5cb7117e2870df68f750cb69b to your computer and use it in GitHub Desktop.
Pseudo Console: Application error in a hosted child when the parent is killed before the child gets the time to initialize
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define WIN32_LEAN_AND_MEAN | |
#define NOMIMNAX | |
#include <Windows.h> | |
#include <processthreadsapi.h> | |
// If this parent process is killed before the hosted process gets the time to initialize (especially when the system is busy), | |
// the "Application Error" dialog will pop up with error code 0xc0000142 (STATUS_DLL_INIT_FAILED) | |
// as noted in the Note: | |
// | |
// https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-hosted-process | |
// > Note | |
// > | |
// > Closing the pseudoconsole session while the hosted process is still starting up | |
// > and connecting can result in an error dialog being shown by the client application. | |
// > The same error dialog is shown if the hosted process is given an invalid pseudoconsole | |
// > handle for startup. To the hosted process initialization code, the two circumstances | |
// > are identical. The pop-up dialog from the hosted client application on failure will read | |
// > 0xc0000142 with a localized message detailing failure to initialize. | |
// | |
// See the TerminateProcess call below just after CreateProcessW. | |
HRESULT SetUpPseudoConsole(COORD size); | |
HRESULT PrepareStartupInformation(HPCON hpc, STARTUPINFOEX* psi); | |
int wmain() | |
{ | |
#if 0 | |
// To mitigate the application error dialog, | |
// kill our child process when this process is killed | |
// by assigning the child process to a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. | |
// | |
// Hopefully our child process will be killed before accessing the closed pseudoconsole. | |
// | |
// For simplicity, have the child process inherit this job object | |
// instead of adding PROC_THREAD_ATTRIBUTE_JOB_LIST. | |
HANDLE hJob = CreateJobObjectW(NULL, NULL); | |
if (hJob == NULL) | |
{ | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
JOBOBJECT_EXTENDED_LIMIT_INFORMATION eli{}; | |
eli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; | |
if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &eli, sizeof(eli))) | |
{ | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
if (!AssignProcessToJobObject(hJob, GetCurrentProcess())) | |
{ | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
#endif | |
COORD size = { 80, 25 }; | |
SetUpPseudoConsole(size); | |
} | |
HRESULT SetUpPseudoConsole(COORD size) | |
{ | |
// | |
// Copied from https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-pseudoconsole | |
// | |
HRESULT hr = S_OK; | |
// Create communication channels | |
// - Close these after CreateProcess of child application with pseudoconsole object. | |
HANDLE inputReadSide, outputWriteSide; | |
// - Hold onto these and use them for communication with the child through the pseudoconsole. | |
HANDLE outputReadSide, inputWriteSide; | |
if (!CreatePipe(&inputReadSide, &inputWriteSide, NULL, 0)) | |
{ | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
if (!CreatePipe(&outputReadSide, &outputWriteSide, NULL, 0)) | |
{ | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
HPCON hPC; | |
hr = CreatePseudoConsole(size, inputReadSide, outputWriteSide, 0, &hPC); | |
if (FAILED(hr)) | |
{ | |
return hr; | |
} | |
// | |
// Copied from https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#creating-the-hosted-process | |
// | |
PCWSTR childApplication = L"C:\\windows\\system32\\cmd.exe"; | |
// Create mutable text string for CreateProcessW command line string. | |
const size_t charsRequired = wcslen(childApplication) + 1; // +1 null terminator | |
PWSTR cmdLineMutable = (PWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t) * charsRequired); | |
if (!cmdLineMutable) | |
{ | |
return E_OUTOFMEMORY; | |
} | |
wcscpy_s(cmdLineMutable, charsRequired, childApplication); | |
PROCESS_INFORMATION pi; | |
ZeroMemory(&pi, sizeof(pi)); | |
STARTUPINFOEX siEx; | |
hr = PrepareStartupInformation(hPC, &siEx); | |
if (FAILED(hr)) | |
{ | |
return hr; | |
} | |
// Call CreateProcess | |
if (!CreateProcessW(NULL, | |
cmdLineMutable, | |
NULL, | |
NULL, | |
FALSE, | |
EXTENDED_STARTUPINFO_PRESENT, | |
NULL, | |
NULL, | |
&siEx.StartupInfo, | |
&pi)) | |
{ | |
HeapFree(GetProcessHeap(), 0, cmdLineMutable); | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
// ... | |
// | |
// Simulate Ctrl+Break, the [x] button of a console (suppose the host is a console app), | |
// TerminateProcess from another process. | |
TerminateProcess(GetCurrentProcess(), 1); | |
return S_OK; | |
} | |
// | |
// Copied from https://learn.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#preparing-for-creation-of-the-child-process | |
// | |
HRESULT PrepareStartupInformation(HPCON hpc, STARTUPINFOEX* psi) | |
{ | |
// Prepare Startup Information structure | |
STARTUPINFOEX si; | |
ZeroMemory(&si, sizeof(si)); | |
si.StartupInfo.cb = sizeof(STARTUPINFOEX); | |
// Discover the size required for the list | |
size_t bytesRequired; | |
InitializeProcThreadAttributeList(NULL, 1, 0, &bytesRequired); | |
// Allocate memory to represent the list | |
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, bytesRequired); | |
if (!si.lpAttributeList) | |
{ | |
return E_OUTOFMEMORY; | |
} | |
// Initialize the list memory location | |
if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &bytesRequired)) | |
{ | |
HeapFree(GetProcessHeap(), 0, si.lpAttributeList); | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
// Set the pseudoconsole information into the list | |
if (!UpdateProcThreadAttribute(si.lpAttributeList, | |
0, | |
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, | |
hpc, | |
sizeof(hpc), | |
NULL, | |
NULL)) | |
{ | |
HeapFree(GetProcessHeap(), 0, si.lpAttributeList); | |
return HRESULT_FROM_WIN32(GetLastError()); | |
} | |
*psi = si; | |
return S_OK; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment