How to start cmd.exe using CreateProcess() without exiting cmd.exe immediately? - winapi

I want to execute Windows' cmd.exe via CreateProcess() from my own command-line program. I redirect its input/output to another program I have written, which needs to have access to cmd.exe. But cmd.exe terminates immediately. I want to run cmd.exe until it receives "exit" from the other program communicating with it via the redirected stdin/stdout. I have tried no parameter, and the parameters /C and /K.
Here is my code:
ZeroMemory( &StartupInfo, sizeof(StartupInfo) );
StartupInfo.cb = sizeof(StartupInfo);
ZeroMemory( &ProcessInformation, sizeof(ProcessInformation) );
StartupInfo.hStdOutput = &cmd_stdout;
StartupInfo.hStdInput = &cmd_stdin;
StartupInfo.hStdError = &cmd_stderr;
//https://stackoverflow.com/questions/4380575/how-to-launch-console-application-using-createprocess-with-minimized-main-window
StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_SHOWNOACTIVATE;//SW_SHOWNORMAL also tried;
pprogstr = "C:\\WINDOWS\\system32\\cmd.exe"; //gets substituted by GetSystemDirectory...
pargstr = '\0';//"/K";
LastError = 0;
OK = CreateProcess (
pprogstr, // pointer to name of executable module
pargstr, // pointer to command line string: full progname + parameters
NULL, // pointer to process security attributes
NULL, // pointer to thread security attributes
TRUE, // handle inheritance flag
NORMAL_PRIORITY_CLASS,// | DETACHED_PROCESS, // creation flags
NULL, // pointer to new environment block
NULL, // pointer to current directory name
&StartupInfo, // pointer to STARTUPINFO
&ProcessInformation // pointer to PROCESS_INFORMATION
);
LastError = GetLastError();
pidProcess = ProcessInformation.hProcess;
What do I have to change so cmd.exe does not terminate immediately?
Now I have written an executable sample program:
/* a Win32 test program starting cmd.exe via CreateProcess */
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define logwrite dat = fopen ("cmdtest.log", "at"); fprintf
#define logend; fclose (dat);
int main(int argc, char* argv[])
{
STARTUPINFO StartupInfo; // pointer to STARTUPINFO
PROCESS_INFORMATION ProcessInformation; // pointer to PROCESS_INFORMATION
char *pargstr, *pargstr2, *pprogstr;
DWORD LastError, WaitResult, exit_code;
FILE *dat;
BOOL OK;
ZeroMemory( &StartupInfo, sizeof(StartupInfo) );
StartupInfo.cb = sizeof(StartupInfo);
ZeroMemory( &ProcessInformation, sizeof(ProcessInformation) );
//StartupInfo.hStdOutput = cmd_stdout;
//StartupInfo.hStdInput = cmd_stdin;
//StartupInfo.hStdError = cmd_stderr;
//https://stackoverflow.com/questions/4380575/how-to-launch-console-application-using-createprocess-with-minimized-main-window
StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow = SW_SHOWNOACTIVATE;//SW_SHOWNORMAL;
//please adapt the string
pprogstr = "C:\\WINDOWS\\system32\\cmd.exe";
pargstr = "/K"; //"\0";
logwrite (dat, "\n"); logend;
logwrite (dat, "Trying CreateProcess pprogstr: %s; pargstr: %s;\n", pprogstr, pargstr); logend;
LastError = 0;
OK = CreateProcess (
pprogstr, // pointer to name of executable module
pargstr, // pointer to command line string: full progname + parameters
NULL, // pointer to process security attributes
NULL, // pointer to thread security attributes
TRUE, // handle inheritance flag
NORMAL_PRIORITY_CLASS,//0,//DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, // creation flags
NULL, // pointer to new environment block
NULL, // pointer to current directory name
&StartupInfo, // pointer to STARTUPINFO
&ProcessInformation // pointer to PROCESS_INFORMATION
);
LastError = GetLastError();
logwrite (dat, "CreateProcess cmd.exe returns OK %i Processhandle %i LastError %i \n", OK, ProcessInformation.hProcess, LastError); logend;
GetExitCodeProcess(ProcessInformation.hProcess, &exit_code);
if (exit_code == STILL_ACTIVE) {
logwrite (dat, "After 0 seconds, cmd.exe process is still active\n"); logend;
}
else {
logwrite (dat, "After 0 seconds, cmd.exe process is no more active\n"); logend;
}
Sleep (2000);
if (exit_code == STILL_ACTIVE) {
logwrite (dat, "After 2 seconds, cmd.exe process is still active\n"); logend;
}
else {
logwrite (dat, "After 2 seconds, cmd.exe process is no more active\n"); logend;
}
Sleep (8000);
WaitResult = WaitForSingleObject (ProcessInformation.hProcess, INFINITE);
LastError = GetLastError();
if (WaitResult == WAIT_FAILED)
{
logwrite (dat, "WaitForSingleObject error: return code = %u Processhandle %i LastError %i\n", WaitResult, ProcessInformation.hProcess, LastError); logend;
}
else
{
logwrite (dat, "cmd.exe complete.\n"); logend;
}
}
The log file shows:
Trying CreateProcess pprogstr: C:\WINDOWS\system32\cmd.exe; pargstr: /K;
CreateProcess cmd.exe returns OK 1 Processhandle 252 LastError 183
After 0 seconds, cmd.exe process is still active
After 2 seconds, cmd.exe process is still active
cmd.exe complete.
This is a strange behaviour:
GetExitCodeProcess returns STILL_ACTIVE
the Task Manager detail view shows no cmd.exe while pausing the program with Sleep
WaitForSingleObject immediately exits cmd.exe.
The behaviour I want is that cmd.exe still runs and does not terminate (so my program should "hang").

Related

NtReadFile not properly reading the file

I am attempting to read ntdl.dll from disk using the NtReadFile native call. A handle has already been acquired through the NtCreateFile native call. However, when I attempt to read it using NtReadFIle, it neither fails nor succeeds. I try to print out the NtStatus, but there's nothing, as it looks like the call never actually completes. It does seem to work with the ReadFile Win API call, so not quite sure why the native call isn't working.
In the debugger, it seems like an unhandled exception is raised when transitioning back to userland after making the syscall, so not sure what is going on. Below is a snippet of the code.
#define OBJ_CASE_INSENSITIVE 0x00000040
#define FILE_OPEN 0x00000001
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_RANDOM_ACCESS 0x00000800
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
SIZE_T bytesWritten = 0;
DWORD oldProtection = 0;
//HANDLE file = NULL;
HANDLE file = NULL;
ULONG fileSize = NULL;
LPDWORD bytesRead = NULL;
PVOID fileData = NULL;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING f;
IO_STATUS_BLOCK IoStatusBlock;
WCHAR ntdl[100] = L"\\?\\c:\\windows\\system32\\ntdll.dll";
WCHAR filepath[100] = L"\\??\\c:\\windows\\system32";
RtlInitUnicodeString(&f, ntdl);
InitializeObjectAttributes(&oa, &f, OBJ_CASE_INSENSITIVE, NULL, NULL);
NTSTATUS stat = NtCreateFile(&file, FILE_GENERIC_READ, &oa, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, 0, 0);
fileSize = GetFileSize(file, NULL);
fileData = HeapAlloc(GetProcessHeap(), 0, fileSize);
NTSTATUS stat1 = NtReadFile(file, NULL, NULL, NULL, &IoStatusBlock, fileData, sizeof(fileData), 0, NULL);
if (stat1 != 0)
{
printf("failed: %X\n", stat1);
}
Instead of sizeof(fileData) pass the fileSize and initialize + pass the ByteOffset value of 0.
LARGE_INTEGER liBytes = { 0 };
status = SysNtReadFile(file, NULL, NULL, NULL, &statusBlock, fileData, fileSize, &liBytes, NULL);
And, In code line
fileData = HeapAlloc(GetProcessHeap(), 0, fileSize);
the third parameter should be set HEAP_ZERO_MEMORY, not 0. See nf-heapapi-heapalloc

Using pipes in a WinDBG extension

I am writing a WinDBG extension to debug a device driver, and need to call an external binary to debug the device's firmware. I would like to show the output of this binary in the WinDBG console.
My initial idea was to simply pipe the output of the binary to a buffer and print that buffer with ControlledOutput. However, I get a 'broken pipe' error when I try to read from the pipe in my extension.
Here is how I create the external process in my extension:
SECURITY_ATTRIBUTES sAttr;
HANDLE childOutRead = NULL;
HANDLE childOutWrite = NULL;
PROCESS_INFORMATION childProcInfo;
STARTUPINFO childStartInfo;
char buf[4096];
sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
sAttr.bInheritHandle = TRUE;
sAttr.lpSecurityDescriptor = NULL;
CreatePipe(&childOutRead, &childOutWrite, &sAttr, 0);
// don't inherit read end
SetHandleInformation(childOutRead, HANDLE_FLAG_INHERIT, 0);
ZeroMemory(&childProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&childStartInfo, sizeof(STARTUPINFO));
childStartInfo.cb = sizeof(STARTUPINFO);
childStartInfo.hStdError = childOutWrite;
childStartInfo.hStdOut = childOutWrite;
childStartInfo.hStdIn = GetStdHandle(STD_INPUT_HANDLE);
childStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcessA(NULL, "myBinary.exe someArgs",
NULL, NULL, TRUE, 0, NULL, NULL,
&childStartInfo, &childProcInfo);
// close the handle not used in parent
CloseHandle(childOutWrite);
// read output
while (1) {
DWORD read;
BOOL r;
DWORD error;
r = ReadFile(childOutRead, buf, sizeof(buf), &read, NULL);
if (!r) {
error = GetLastError();
windbgPrintf("got error 0x%x\n", error);
break;
}
if (read == 0) break;
windbgPrint(buf, read);
}
ReadFile fails with error 0x6D, BROKEN_PIPE. This makes me suspect that the pipe is somehow not being inherited.
I have nearly identical code working in a test outside of WinDBG, so it must be doing something differently. How do I get pipes working in this way inside WinDBG?

ReadFile does not return while reading stdout from a child process after it ends

I am working on my library which needs to capture and process the standard output (and err) of a child process as it runs. The problem arises when ReadFile is used to read the output, it does not return once the process ends (gets killed or exits).
It looks like ReadFile is not able to detect that the other end of the pipe (the write handle) is closed. According to the documentation it should return FALSE and set the last error to ERROR_BROKEN_PIPE:
If an anonymous pipe is being used and the write handle has been closed, when ReadFile attempts to read using the pipe's corresponding read handle, the function returns FALSE and GetLastError returns ERROR_BROKEN_PIPE.
Here is my code, I have stripped out the irrelevant bits: (NOTE: I have updated the allium_start to follow the suggested changes, I am keeping the original for reference, please use the newer function code to find flaws)
bool allium_start(struct TorInstance *instance, char *config, allium_pipe *output_pipes) {
// Prepare startup info with appropriate information
SecureZeroMemory(&instance->startup_info, sizeof instance->startup_info);
instance->startup_info.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES pipe_secu_attribs = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
HANDLE pipes[2];
if (output_pipes == NULL) {
CreatePipe(&pipes[0], &pipes[1], &pipe_secu_attribs, 0);
output_pipes = pipes;
}
instance->startup_info.hStdOutput = output_pipes[1];
instance->startup_info.hStdError = output_pipes[1];
instance->stdout_pipe = output_pipes[0]; // Stored for internal reference
// Create the process
bool success = CreateProcessA(
NULL,
cmd,
NULL,
NULL,
config ? true : false,
0,
NULL,
NULL,
&instance->startup_info,
SecureZeroMemory(&instance->process, sizeof instance->process)
);
// Return on failure
if (!success) return false;
}
char *allium_read_stdout_line(struct TorInstance *instance) {
char *buffer = instance->buffer.data;
// Process the input
unsigned int read_len = 0;
while (true) {
// Read data
unsigned long bytes_read;
if (ReadFile(instance->stdout_pipe, buffer, 1, &bytes_read, NULL) == false || bytes_read == 0) return NULL;
// Check if we have reached end of line
if (buffer[0] == '\n') break;
// Proceed to the next character
++buffer; ++read_len;
}
// Terminate the new line with null character and return
// Special handling for Windows, terminate at CR if present
buffer[read_len >= 2 && buffer[-1] == '\r' ? -1 : 0] = '\0';
return instance->buffer.data;
}
The allium_start creates the pipe for output redirection (it uses the same pipe for both stdout and stderr to get merged streams) and then creates the child process. The other allium_read_stdout_line function is responsible for reading the output from the pipe and returning it when it encounters a new line.
The issue occurs at the ReadFile function call, it never returns if there is nothing to read after the process exits, from my understanding all the handles of a process are closed by Windows when it ends, so it looks like ReadFile is not able to detect the fact that the pipe (write handle) at the other end has been closed.
How do I fix this? I have been searching for a solution but I have found none so far, one potential option is to use multi-threading and put ReadFile in a separate thread so that it doesn't block the whole program, by using that method I can check if the process still exists periodically while I wait for the reading to finish... or kill/stop the thread if the process is gone.
I do prefer fixing the issue instead of opting for a workaround, but I am open to any other solutions to make it work. Thanks in advance!
Edit: After reading #RemyLebeau's answer and #RbMm's comments in that answer, it is pretty clear that my understand of how handle inheritance works is fundamentally flawed. So I incorporated their suggestions (SetHandleInformation to disable inheritance of read handle and closing it after creating the child process) into my allium_start function:
bool allium_start(struct TorInstance *instance, char *config, allium_pipe *output_pipes) {
// Prepare startup info with appropriate information
SecureZeroMemory(&instance->startup_info, sizeof instance->startup_info);
instance->startup_info.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES pipe_secu_attribs = {sizeof(SECURITY_ATTRIBUTES), NULL, true};
HANDLE pipes[2];
if (output_pipes == NULL) {
CreatePipe(&pipes[0], &pipes[1], &pipe_secu_attribs, 0);
output_pipes = pipes;
}
SetHandleInformation(output_pipes[0], HANDLE_FLAG_INHERIT, 0);
instance->startup_info.hStdOutput = output_pipes[1];
instance->startup_info.hStdError = output_pipes[1];
instance->stdout_pipe = output_pipes[0]; // Stored for internal reference
// Create the process
bool success = CreateProcessA(
NULL,
cmd,
NULL,
NULL,
config ? true : false,
0,
NULL,
NULL,
&instance->startup_info,
SecureZeroMemory(&instance->process, sizeof instance->process)
);
// Close the write end of our stdout handle
CloseHandle(output_pipes[1]);
// Return on failure
if (!success) return false;
}
(The below text was originally here before edit 2)
But sadly it still doesn't work :(
Edit 2 (after accepting answer): It does work! See my last comment on the accepted answer.
You are not managing your pipes correctly, or more specifically, you are not controlling the inheritance of your pipe handles. DO NOT let the child process inherit the reading handle of your pipe (output_pipes[0]), otherwise the pipe will not break correctly when the child process ends.
Read MSDN for more details:
Creating a Child Process with Redirected Input and Output
The case of the redirected standard handles that won’t close even though the child process has exited
Use SetHandleInformation() or PROC_THREAD_ATTRIBUTE_LIST to prevent CreateProcess() from passing output_pipes[0] to the child process as an inheritable handle. The child process does not need access to that handle, so there is no need to pass it over the process boundary anyway. It only needs access to the writing handle of your pipe (output_pipes[1]).
For anonymous pipelines, the read process and the write process will have the handler of hRead and hWrite, each of process has its own handler(copy after inheritance). So after your child process exit and close the handler in it, anther hWrite still in parent process. We must pay attention to close hRead in the write process, close hWrite in the read process.
I can reproduce this ReadFile issue, and if closing write handler after setting child's hStdOutput and hStdError, the ReadFile will return 0 after the child process exit.
Here is my code sample,
Parent.cpp:
#include <windows.h>
#include <iostream>
#include <stdio.h>
HANDLE childInRead = NULL;
HANDLE W1 = NULL;
HANDLE W2 = NULL;
HANDLE R2 = NULL;
HANDLE R1 = NULL;
#define BUFSIZE 4096
void CreateChildProcess() {
TCHAR applicationName[] = TEXT("kids.exe");
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL success = FALSE;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = W1;
si.hStdOutput = W1;
si.hStdInput = R2;
si.dwFlags |= STARTF_USESTDHANDLES;
success = CreateProcess(NULL, applicationName, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (!success) {
printf("Error creating child process \n");
}
else {
printf("Child process successfuly created \n");
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
int main()
{
printf("Parent process running.... \n");
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE] = { 0 };
BOOL bSuccess = FALSE;
SECURITY_ATTRIBUTES secAttr;
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.bInheritHandle = TRUE;
secAttr.lpSecurityDescriptor = NULL;
printf("Creating first pipe \n");
if (!CreatePipe(&R1, &W1, &secAttr, 0)) {
printf("\n error creating first pipe \n");
}
printf("Creating second pipe \n");
if (!CreatePipe(&R2, &W2, &secAttr, 0)) {
printf("\n error creating second pipe \n");
}
if (!SetHandleInformation(R1, HANDLE_FLAG_INHERIT, 0)) {
printf("\n R1 SetHandleInformation \n");
}
if (!SetHandleInformation(W2, HANDLE_FLAG_INHERIT, 0)) {
printf("\n W1 SetHandleInformation \n");
}
printf("\n Creating child process..... \n");
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
CreateChildProcess();
CloseHandle(W1);
CloseHandle(R2);
for (;;) {
printf("Inside for loop \n");
//1. read from stdin
printf("read from stdin:\n");
bSuccess = ReadFile(hStdIn, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//2. write to Pipe2
printf("write to Pipe2...\n");
bSuccess = WriteFile(W2, chBuf, 100, &dWritten, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
//3. read from Pipe1
printf("read from Pipe1...\n");
bSuccess = ReadFile(R1, chBuf, BUFSIZE, &dRead, NULL);
if (!bSuccess)
{
printf("error reading :%d \n", GetLastError());
break;
}
//4. write to stdout
printf("write to stdout:\n");
bSuccess = WriteFile(hStdOut, chBuf, 100, &dWritten, NULL);
if (!bSuccess) {
printf("error reading \n");
break;
}
}
getchar();
return 0;
}
Kids.cpp:
#include <windows.h>
#include <stdio.h>
#define BUFSIZE 4096
int main()
{
DWORD dRead, dWritten;
CHAR chBuf[BUFSIZE];
BOOL success = FALSE;
HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
printf("Child process running....");
if (stdIn == INVALID_HANDLE_VALUE || stdOut == INVALID_HANDLE_VALUE) {
ExitProcess(1);
}
//for (;;) {
success = ReadFile(stdIn, chBuf, BUFSIZE, &dRead, NULL);
//if (!success || dRead == 0) break;
success = WriteFile(stdOut, chBuf, dRead, &dWritten, NULL);
//if (!success) break;
//}
return 0;
}

Windows: How to stop buffering of redirected Stdout using CreateProcess

I am using pipes to get redirected stdout output from a command line executable. Unfortunately I don't get any output until the process has completed. The executable outputs progress status as it runs and this is what I would like to parse.
BOOL RunCmd( char *pCmd,
char *pWorkingDir,
int nWaitSecs,
BOOL fRegImport,
DWORD *pdwExitCode )
{
BOOL fSuccess = TRUE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sFileSecurity;
ZeroMemory( &sFileSecurity, sizeof( sFileSecurity ) );
sFileSecurity.nLength = sizeof( sFileSecurity );
sFileSecurity.bInheritHandle = TRUE;
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
fSuccess = CreatePipe( &hReadPipe, &hWritePipe, &sFileSecurity, 0 );
SetHandleInformation( hReadPipe, HANDLE_FLAG_INHERIT, 0 );
ZeroMemory( &si, sizeof(si) );
ZeroMemory( &pi, sizeof(pi) );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hWritePipe;
si.hStdError = hWritePipe;
si.wShowWindow = SW_HIDE;
int rc;
// Start the child process.
rc = CreateProcess( NULL, // No module name (use command line).
pCmd, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
TRUE,
CREATE_NO_WINDOW,
NULL, // Use parent's environment block.
pWorkingDir, // Working folder
&si, // Pointer to STARTUPINFO structure.
&pi ); // Pointer to PROCESS_INFORMATION structure.
if( ! rc )
return FALSE;
// Wait until child process exits.
DWORD dwWaitResult;
DWORD dwTimeStart = ::GetTickCount();
DWORD dwTimeNow;
#define BUFSIZE 4096
DWORD dwRead = 0;
DWORD dwAvail;
CHAR chBuf[ BUFSIZE ];
BOOL bSuccess = TRUE;
for( ;; )
{
dwTimeNow = ::GetTickCount();
dwWaitResult = ::WaitForSingleObject( pi.hProcess, ONE_SECOND );
dwRead = 0;
for( dwAvail = 0; PeekNamedPipe( hReadPipe, 0, 0, 0, &dwAvail, 0 ) && dwAvail; dwAvail = 0 )
{
dwRead = 0;
ReadFile( hReadPipe, chBuf, min( BUFSIZE, dwAvail ), &dwRead, NULL );
if( dwRead > 0 )
{
FILE *op = fopen( "c:\\REDIR.OUT", "a" );
if( op )
{
fwrite( chBuf, 1, dwRead, op );
fclose( op );
}
}
}
if( dwWaitResult == WAIT_OBJECT_0 )
{
DWORD dwExitCode;
GetExitCodeProcess( pi.hProcess, &dwExitCode );
if( pdwExitCode )
(*pdwExitCode) = dwExitCode;
break;
}
if( dwWaitResult == WAIT_TIMEOUT )
{
if( dwTimeNow - dwTimeStart < (DWORD)( ONE_SECOND * nWaitSecs ) )
continue;
else
{
fSuccess = FALSE;
break;
}
}
fSuccess = FALSE;
break;
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
CloseHandle( hReadPipe );
CloseHandle( hWritePipe );
return fSuccess;
}
The PeekNamedPipe() call is called every second and dwAvail is zero every time until the process completes.
How do I get output from the process sooner? When running the process from the console, I see progress output as it goes. The process will be using "\r" in it's output to display a percentage at the start of the same line.
Note: My answer only deals with executables compiled using MSVC.
The buffering policy is coded inside Microsoft C Runtime (CRT) Library. You can learn the details here. This article suggests using console handles and manipulate console buffers to receive unbuffered output.
However, there's an undocumented feature inside Microsoft C Runtime to inherit file handles with some internal flags directly from its parent process using lpReserved2 and cbReserved2 fields of STARTUPINFO structure. You can find the details in the crt source code provided by Microsoft Visual Studio. Or search for something like posfhnd on GitHub.
We can exploit this undocumented feature to provide a pipe handle and specify FOPEN | FDEV flags to the child process, to fool the child process treat that pipe handle the same way as a FILE_TYPE_CHAR handle.
I have a working Python3 script to demonstrate this method.
A simplified C/C++ version of the answer by #youfu.
STARTUPINFO si;
int nh = 2;
si.cbReserved2 = (WORD)(sizeof(int) + (nh *
(sizeof(char) + sizeof(HANDLE))));
si.lpReserved2 = (LPBYTE) calloc(si.cbReserved2, 1);
*((UNALIGNED int *)(si.lpReserved2)) = nh;
unsigned char* posfile = (unsigned char *)(si.lpReserved2 + sizeof(int));
UNALIGNED HANDLE* posfhnd = (UNALIGNED HANDLE *)(si.lpReserved2 + sizeof(int) +
(nh * sizeof(unsigned char)));
unsigned char FOPEN = 0x01;
unsigned char FDEV = 0x40;
*posfile = FOPEN | FDEV;
posfile++;
*posfile = FOPEN | FDEV;
*posfhnd = child_stdin_rd;
posfhnd++;
*posfhnd = child_stdout_wr;
-Suraj

start a process in a .bat file but hide it?

I would like to launch one of my apps inside a .bat file but it is visible and taking up space in my taskbar. How do i launch the app and not have it visible?
Here's a utility I wrote years ago to do this:
#include <windows.h>
#pragma comment(lib, "user32.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
const char *p = GetCommandLine();
if (*p == '"') {
p++;
while (*p && *p != '"') {
p++;
}
p++;
} else {
while (*p && *p != ' ') {
p++;
}
}
while (*p == ' ') {
p++;
}
if (*p == 0) {
MessageBox(NULL, "Usage: nocli <command>\nExecute <command> without a command prompt window.", "nocli Usage", MB_OK);
return 1;
}
//if (MessageBox(NULL, p, "nocli debug", MB_OKCANCEL) != IDOK) return 1;
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
if (CreateProcess(NULL, const_cast<char *>(p), NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitcode;
GetExitCodeProcess(pi.hProcess, &exitcode);
CloseHandle(pi.hProcess);
return exitcode;
} else {
MessageBox(NULL, "Error executing command line", "nocli", MB_OK);
return 1;
}
return 0;
}
No guarantees, but it worked for me in one situation at one time. :)
Assuming you want to open an application and have the DOS prompt go away immediately, use start <command> in your .bat file instead of just <command>
If your not afraid to use Perl then this will do the trick
use Win32::GUI;
Win32::GUI::Hide(scalar(Win32::GUI::GetPerlWindow()));
Here I've compiled all ways that I know to start a hidden process with batch without external tools.With a ready to use scripts (some of them rich on options) , and all of them form command line.Where is possible also the PID is returned .Used tools are IEXPRESS,SCHTASKS,WScript.Shell,Win32_Process and JScript.Net - but all of them wrapped in a .bat files.

Resources