void
ReadAndHandleOutput(
_In_ HANDLE hPipeRead)
{
CHAR lpBuffer[256];
DWORD nBytesRead;
BOOL result = TRUE;
while (result)
{
ZeroMemory(lpBuffer, sizeof(lpBuffer));
result = ReadFile(hPipeRead, lpBuffer, (sizeof(lpBuffer) - 1), &nBytesRead, NULL);
if (!result || !nBytesRead)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
{
break;
}
else
{
break;
}
}
// Instead of outputting this via OutputDebugStringA, it could instead be written to a log file.
try
{
OutputDebugStringA(lpBuffer);
}
catch (...)
{
return;
}
}
}
HRESULT
WINAPI
CreateAndRunRedirectedStdoutProcess(
_In_ LPCWSTR mainExe,
_In_ LPWSTR lpCommandLine)
{
HRESULT hr = S_OK;
HANDLE hOutputReadTmp = INVALID_HANDLE_VALUE;
HANDLE hOutputRead = INVALID_HANDLE_VALUE;
HANDLE hOutputWrite = INVALID_HANDLE_VALUE;
HANDLE hErrorWrite = INVALID_HANDLE_VALUE;
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
SECURITY_ATTRIBUTES sa = { 0 };
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0))
{
OutputDebugString(L"Failed to create pipe");
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if (!DuplicateHandle(GetCurrentProcess(), hOutputWrite, GetCurrentProcess(), &hErrorWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
OutputDebugString(L"Failed to dupicate handle");
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
if (!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp, GetCurrentProcess(), &hOutputRead, 0, FALSE /*no inheritable*/, DUPLICATE_SAME_ACCESS))
{
OutputDebugString(L"Failed to duplicate handle");
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// All redirection handles are created, set them and create the process
si.dwFlags = 0x00000100; // STARTF_USESTDHANDLES;
si.hStdOutput = hOutputWrite;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdError = hErrorWrite;
if (!CreateProcess(
mainExe, lpCommandLine,
nullptr, nullptr,
true, CREATE_NO_WINDOW,
nullptr, nullptr,
&si, &pi))
{
OutputDebugString(L"Failed to create process");
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// pThread is unneeded, so close it now.
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
{
CloseHandle(pi.hThread);
}
Exit:
// Close output and error handles
if (hOutputReadTmp != INVALID_HANDLE_VALUE)
{
CloseHandle(hOutputReadTmp);
hOutputReadTmp = INVALID_HANDLE_VALUE;
}
if (hOutputWrite != INVALID_HANDLE_VALUE)
{
CloseHandle(hOutputWrite);
hOutputWrite = INVALID_HANDLE_VALUE;
}
if (hErrorWrite != INVALID_HANDLE_VALUE)
{
CloseHandle(hErrorWrite);
hErrorWrite = INVALID_HANDLE_VALUE;
}
// Read output if present. This method will block until the process
// has exited. Then close the handle.
if (hOutputRead != INVALID_HANDLE_VALUE)
{
ReadAndHandleOutput(hOutputRead);
CloseHandle(hOutputRead);
hErrorWrite = INVALID_HANDLE_VALUE;
}
// Check the return code from the process
if (pi.hProcess && pi.hProcess != INVALID_HANDLE_VALUE)
{
DWORD exitCode = WaitForSingleObject(pi.hProcess, INFINITE);
if (exitCode != WAIT_OBJECT_0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
else if (!GetExitCodeProcess(pi.hProcess, &exitCode))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
else
{
if (exitCode)
{
hr = E_FAIL;
}
}
CloseHandle(pi.hProcess);
pi.hProcess = INVALID_HANDLE_VALUE;
}
return hr;
}