Related
I have to run cmd.exe with CreateProcessWithLogonW() but in the context of my program without creating another console, but MSDN says the CREATE_NEW_CONSOLE flag has been set by default. How can I unset this flag so this API doesn't create a new window for my child process?
The following code shows how this API is used in my program. I don't want the new program to run in a new console, but I could not find a solution for that.
BOOL status = FALSE;
DWORD process_flags = 0 | arg_process_flags;
DWORD logon_flags = 0 | arg_logon_flags;
PTSTR duplicate_command_Line;
PPROCESS_INFORMATION ptr_process_info;
STARTUPINFO startup_info;
RtlZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
if (ptr_process_info = arg_process_infos ? arg_process_infos : (PPROCESS_INFORMATION)LocalAlloc(LPTR, sizeof(PROCESS_INFORMATION)))
{
if (duplicate_command_Line = _wcsdup(arg_command_Line))
{
switch (arg_type)
{
case KULL_M_PROCESS_CREATE_NORMAL:
status = CreateProcess(NULL, duplicate_command_Line, NULL, NULL, FALSE, process_flags, NULL, NULL, &startup_info, ptr_process_info);
break;
case KULL_M_PROCESS_CREATE_USER:
status = CreateProcessAsUser(arg_user_token, NULL, duplicate_command_Line, NULL, NULL, FALSE, process_flags, NULL, NULL, &startup_info, ptr_process_info);
break;
case KULL_M_PROCESS_CREATE_LOGON:
status = CreateProcessWithLogonW(arg_user, arg_domain, arg_password, logon_flags, NULL, duplicate_command_Line, process_flags, NULL, NULL, &startup_info, ptr_process_info);
break;
}
if (status && (arg_auto_close_handle || !arg_process_infos))
{
CloseHandle(ptr_process_info->hThread);
CloseHandle(ptr_process_info->hProcess);
}
if (!arg_process_infos)
LocalFree(ptr_process_info);
free(duplicate_command_Line);
}
}
You could redirected input and output of child process
Here's my test program(remove the error checking).
Parent:
#include <windows.h>
#include <iostream>
#define BUFSIZE 4096
void main()
{
printf("in Parent \n");
HANDLE R_In, R_Out, R_err, W_In, W_Out, W_err;
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
SECURITY_ATTRIBUTES saAttr;
BOOL bSuccess;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&R_In, &W_In, &saAttr, 0);
CreatePipe(&R_Out, &W_Out, &saAttr, 0);
CreatePipe(&R_err, &W_err, &saAttr, 0);
PROCESS_INFORMATION process_info;
STARTUPINFO startup_info;
RtlZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
startup_info.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startup_info.wShowWindow = SW_HIDE;
startup_info.hStdInput = R_In;
startup_info.hStdOutput = W_Out;
startup_info.hStdError = W_err;
BOOL ret = CreateProcessWithLogonW(L"username",L"domain",L"password", 0,L"ChildProcess.exe",NULL, CREATE_NO_WINDOW,NULL,NULL,&startup_info,&process_info);
CloseHandle(R_In);
CloseHandle(W_Out);
CloseHandle(W_err);
CHAR chBuf[BUFSIZE];
DWORD dwRead, dwWritten;
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);
bSuccess = WriteFile(W_In, chBuf, dwRead, &dwWritten, NULL);
while (1)
{
bSuccess = ReadFile(R_Out, chBuf, BUFSIZE, &dwRead, NULL);
if (bSuccess == 0 & GetLastError() == ERROR_BROKEN_PIPE) // child process exit.
break;
bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);
}
WaitForSingleObject(process_info.hProcess, INFINITE);
printf("Parent exit\n");
}
Child:
#include <windows.h>
#include <iostream>
#define BUFSIZE 4096
#pragma warning(disable : 4996)
void main()
{
CHAR chBuf[BUFSIZE];
scanf("%s", chBuf);
printf("in Child %s\n", chBuf);
printf("Child exit\n");
return;
}
Result:
Do you mean you don't want to create a new window?
try startup_info.dwFlags = STARTF_USESHOWWINDOW;startup_info.wShowWindow = SW_HIDE; then it won't create a window.
It's been awhile, but passing DETACHED_PROCESS should work.
If not, you can call CreateProcessWithLogonW passing it a win32 binary that you provide (possibly your own with different options) that in turn will call CreateProcess opening cmd.exe without passing CREATE_NEW_CONSOLE.
Unless you're already admin, creating in the same console is utterly impossible, and if you are admin, it's an arcane technique you're better off not using.
I want to capture PuTTY's STDIN and STDOUT pipes. So I open putty.exe via CreateProcess with passing created pipes but I can't read anything from pipes. My code works when I run a bat file.
How can I accomplish this?
Thanks
//This code is from MSDN and I edited it a little
#include <QCoreApplication>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#pragma comment(lib, "user32.lib")
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int main(int argc, char *argv[])
{
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit((PTSTR)TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit((PTSTR)TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit((PTSTR)TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit((PTSTR)TEXT("Stdin SetHandleInformation"));
// Create the child process: putty
CreateChildProcess();
// read from pipe and printf incoming data
ReadFromPipe();
}
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 )
break;
printf("received hex:\n");
for (int i = 0; i < dwRead; i++)
{
printf("%02X", chBuf[i]);
}
printf("\nactual string: %s\n",chBuf);
}
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(L"E:\\putty.exe",
NULL, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if ( ! bSuccess )
ErrorExit((PTSTR)TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
// Format a readable error message, display a message box,
// and exit from the application.
void ErrorExit(PTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
PuTTY is a GUI application. You cannot communicate with it using input/output redirection.
Use plink.exe from PuTTY package. It's a console application with the same set of command-line arguments and features as PuTTY.
Though even better, using some C++ SSH library instead.
I found out that PuTTY doesn't have pipe for this purpose and since I didn't want to use plink, I modified PuTTY's source code and embedded pipe in it. Now it works as I want.
In my Win32 program I implemented executing a console application and reading its std/err output. Basically it's the same code as given in MSDN: Creating a Child Process with Redirected Input and Output
So far, so good. It worked like a charm, reading both std and err streams with all my console applications. But obviously (due to the global HANDLE variables) the code is designed to run console applications one by one, never together. So I've changed it a bit:
The global HANDLE variables replaced with the local ones. They are passed into the helper functions.
A parameter named bWait added. If it's false, no reading from the console pipe after starting and no waiting for a process handle (a flavor of asynchrony).
Instead, the reading handles are returned to a caller (thru given pointers). They can be used for reading from the pipe later.
Why do I need this? I want to start tshark (a console version of Wireshark, a traffic sniffer) with bWait = false, then start my own utility with bWait = true and wait until my utility stops working. Then I want to check, whether my utility pings a server. (Since we have a lot of utilities, this would be important functionality of our auto-testing procedure). So, I'd like to read from the tshark console pipe after that and parse its log.
Here is my modifications:
// Create a child process that uses the previously created pipes
// for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(HANDLE hChildStd_OUT_Wr, HANDLE hChildStd_ERR_Wr,
const std::wstring& cmd, bool& bSuccess, DWORD& exitCode, DWORD& lastError, bool bWait = true)
{
// Set the text I want to run
//char szCmdline[]="test --log_level=all --report_level=detailed";
bSuccess = false;
wchar_t wrBuffer[BUFSIZE];
::wcscpy_s(wrBuffer, cmd.c_str());
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDERR and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStd_ERR_Wr;
siStartInfo.hStdOutput = hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
wrBuffer, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo) != 0; // receives PROCESS_INFORMATION
if (!bSuccess)
{
lastError = ::GetLastError();
}
else
{
lastError = 0;
}
if (bWait && bSuccess && ::WaitForSingleObject(piProcInfo.hProcess, INFINITE) == WAIT_FAILED)
{
bSuccess = false;
}
if (bWait && FALSE == ::GetExitCodeProcess(piProcInfo.hProcess, &exitCode))
{
bSuccess = false;
}
if (bWait)
{
::CloseHandle(hChildStd_ERR_Wr);
::CloseHandle(hChildStd_OUT_Wr);
}
return piProcInfo;
}
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(HANDLE hChildStd_OUT_Rd, HANDLE hChildStd_ERR_Rd, std::wstring& stdS, std::wstring& errS)
{
DWORD dwRead;
CHAR chBuf[BUFSIZE];
bool bSuccess = FALSE;
std::string out = "", err = "";
for (;;)
{
bSuccess = ReadFile(hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL) != 0;
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
out += s;
}
dwRead = 0;
for (;;)
{
bSuccess = ReadFile(hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL) != 0;
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
err += s;
}
wchar_t utf[10000] = { 0 };
::MultiByteToWideChar(866, 0, (LPCCH) out.c_str(), -1, utf, sizeof(utf));
stdS = utf;
StringReplace(stdS, std::wstring(L"\n"), std::wstring(L"\r\n"));
::MultiByteToWideChar(866, 0, (LPCCH) err.c_str(), -1, utf, sizeof(utf));
errS = utf;
StringReplace(errS, std::wstring(L"\n"), std::wstring(L"\r\n"));
}
bool ExecuteCmd(std::wstring cmd, std::wstring& std, std::wstring& err, std::wstring& code, DWORD& lastError,
bool bWait = true, HANDLE* phChildStd_OUT_Rd = nullptr, HANDLE* phChildStd_ERR_Rd = nullptr)
{
HANDLE hChildStd_OUT_Rd = NULL;
HANDLE hChildStd_OUT_Wr = NULL;
HANDLE hChildStd_ERR_Rd = NULL;
HANDLE hChildStd_ERR_Wr = NULL;
SECURITY_ATTRIBUTES sa;
// Set the bInheritHandle flag so pipe handles are inherited.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDERR.
if (!CreatePipe(&hChildStd_ERR_Rd, &hChildStd_ERR_Wr, &sa, 0))
{
return false;
}
// Ensure the read handle to the pipe for STDERR is not inherited.
if (!SetHandleInformation(hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Create a pipe for the child process's STDOUT.
if (!CreatePipe(&hChildStd_OUT_Rd, &hChildStd_OUT_Wr, &sa, 0))
{
return false;
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if (!SetHandleInformation(hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Create the child process.
bool bSuccess = false;
DWORD dwExitCode = 9999;
PROCESS_INFORMATION piProcInfo = CreateChildProcess(hChildStd_OUT_Wr, hChildStd_ERR_Wr, cmd, bSuccess, dwExitCode, lastError, bWait);
if (phChildStd_OUT_Rd)
*phChildStd_OUT_Rd = hChildStd_OUT_Rd;
if (phChildStd_ERR_Rd)
*phChildStd_ERR_Rd = hChildStd_ERR_Rd;
if (!bWait)
return true;
wchar_t buffer[10] = { 0 };
code = ::_itow((int) dwExitCode, buffer, 10);
if (!bSuccess)
{
return false;
}
// Read from pipe that is the standard output for child process.
ReadFromPipe(hChildStd_OUT_Rd, hChildStd_ERR_Rd, std, err);
::CloseHandle(hChildStd_OUT_Rd);
::CloseHandle(hChildStd_ERR_Rd);
return true;
}
Now, the problem. When I tried starting tshark in the no-waiting mode, reading from pipe hanged up. Namely, in ReadFile.
if (g_iConnection != -1 && g_Products[i].PingbackDomain.size() > 0)
{
wchar_t buf[5] = { 0 };
std::wstring list, err, code;
DWORD dwErr = 0;
std::wstring cmd = L"C:\\Program Files\\Wireshark\\tshark -a duration:5 -l -i ";
cmd += ::_itow(g_iConnection + 1, buf, 10);
cmd += L" -f \"host ";
cmd += g_Products[i].PingbackDomain;
cmd += L"\"";
ExecuteCmd(cmd, list, err, code, dwErr, false, &hChildStd_OUT_Rd, &hChildStd_ERR_Rd);
::Sleep(500);
}
...
// Starting my utility (if this section is commented out, ReadFile still hangs).
...
if (hChildStd_OUT_Rd && hChildStd_ERR_Rd)
{
std::wstring traffic, tsharkErr;
ReadFromPipe(hChildStd_OUT_Rd, hChildStd_ERR_Rd, traffic, tsharkErr);
::CloseHandle(hChildStd_OUT_Rd);
::CloseHandle(hChildStd_ERR_Rd);
if (tsharkErr.size() > 0)
{
std::wstring msg = L"There has been an issue, while logging with Wireshark:\r\n\r\n";
msg += tsharkErr;
::MessageBox(NULL, msg.c_str(), L"uhelper", MB_ICONERROR | MB_OK);
}
else if (traffic.length() > 0)
{
newOutput += L"\r\nTraffic to ";
newOutput += g_Products[i].PingbackDomain;
newOutput += L":\r\n";
newOutput += traffic;
if (newOutput[newOutput.length() - 1] != L'\n')
newOutput += L"\r\n";
}
}
Did I break the MSDN code with my modifications? Unfortunately, I can't find how (and where).
This solves the problem (before creating a process!):
if (!bWait)
{
DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT;
::SetNamedPipeHandleState(hChildStd_OUT_Rd, &mode, NULL, NULL);
::SetNamedPipeHandleState(hChildStd_ERR_Rd, &mode, NULL, NULL);
}
After applying PIPE_NOWAIT reading stopped hanging.
I followed all the instructions provided on the MSDN site for Creating a Child Process with Redirected Input and Output but it gives me error:
CreateProcess failed with error 2: The system cannot find the file
specified
the code that i used is :
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int _tmain(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.
if (argc == 1)
ErrorExit(TEXT("Please specify an input file.\n"));
g_hInputFile = CreateFile(
argv[1],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if ( g_hInputFile == INVALID_HANDLE_VALUE )
ErrorExit(TEXT("CreateFile"));
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
WriteToPipe();
printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
// Read from pipe that is the standard output for child process.
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
ReadFromPipe();
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.
return 0;
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[]=TEXT("D:\Visual Studio Projects\LEARN\queues\Debug\queues.exe");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if ( ! bSuccess )
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
}
}
void WriteToPipe(void)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
if ( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
if ( ! bSuccess ) break;
}
// Close the pipe handle so the child process stops reading.
if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (! bSuccess ) break;
}
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
lets just say i want to use my own child process which is :
#include <iostream>
#include <queue>
#include <conio.h>
using namespace std;
int main(){
queue<int> myQ;
for(int i=0; i<10; i++)
{
cout<<"Enqueing..."<< i << endl;
myQ.push(i);
}
cout << "My size is "<< myQ.size()<<endl;
cout<<"Last element of the queue is "<<myQ.back()<<endl;
while(!myQ.empty())
{
cout<<"Dequeing ... "<<myQ.front()<<endl;
}
_getch();
return 0;
}
and want to print the values of the queue in the stdout.. but it shows error that i mentioned earlier .. i already copied the queues.exe to the debug folder of the main program.... but still it shows error..
I found the below comment on Microsoft's documentation site:
Also, CreateProcessWithLogonW rejects STD handles (0/1/2) i.e. what GetStdHandle() returns by default, when using STARTF_USESTDHANDLES. It returns error 6, invalid handles, because 0/1/2 are not "real" handles. The only way we found to redirect the console input/output was to create custom handles (pipes) and use those instead (e.g. even just as dummy handles that you don't use). This is missing functionality from CreateProcessWithLogonW, because CreateProcess (and maybe CreateProcessAsUser, I didn't verify that) accepts STD handles.
This points to creating custom pipes.
I found this StackOverflow answer which points to using things such as "SafeFileHandle". This is apparently C++/CLI which I am not familiar with how to use. I am using Visual C++ and therefore "native" C++?
I am looking for a simple solution of how to fix this issue. I am a newbie in C++ and the CLI thing confused me because I simply don't know how to use it, or make Visual C++ compatible with it.
Given my normal C++ and my question, HOW EXACTLY would I create the custom pipe which will allow me to get the standard output?
Here is my code:
#include "stdafx.h"
void DisplayError(LPWSTR pszAPI)
{
LPVOID lpvMessageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&lpvMessageBuffer, 0, NULL);
//
//... now display this string
//
wprintf(L"ERROR: API = %s.\n", pszAPI);
wprintf(L" error code = %d.\n", GetLastError());
wprintf(L" message = %s.\n", (LPWSTR)lpvMessageBuffer);
//
// Free the buffer allocated by the system
//
LocalFree(lpvMessageBuffer);
ExitProcess(GetLastError());
}
void _tmain(int argc, WCHAR *argv[])
{
STARTUPINFO si;
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
HANDLE pipe1 = CreateNamedPipe(L"\\\\.\\pipe\\pipe1", PIPE_ACCESS_DUPLEX,
PIPE_WAIT, 1024, 1024, 1024, 60, NULL);
HANDLE pipe2 = CreateNamedPipe(L"\\\\.\\pipe\\pipe2", PIPE_ACCESS_DUPLEX,
PIPE_WAIT, 1024, 1024, 1024, 60, NULL);
si.dwFlags |= STARTF_USESHOWWINDOW;
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = pipe1;
si.hStdError = pipe2;
DWORD dwSize;
HANDLE hToken;
LPVOID lpvEnv;
PROCESS_INFORMATION pi = {0};
WCHAR szUserProfile[256] = L"";
si.cb = sizeof(STARTUPINFO);
if (argc != 4)
{
wprintf(L"Usage: %s [user#domain] [password] [cmd]", argv[0]);
wprintf(L"\n\n");
return;
}
//
// TO DO: change NULL to '.' to use local account database
//
if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken))
DisplayError(L"LogonUser");
if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
DisplayError(L"CreateEnvironmentBlock");
dwSize = sizeof(szUserProfile)/sizeof(WCHAR);
if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
DisplayError(L"GetUserProfileDirectory");
//
// TO DO: change NULL to '.' to use local account database
//
if (!CreateProcessWithLogonW(argv[1], NULL, argv[2],
LOGON_WITH_PROFILE, NULL, argv[3],
CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile,
&si, &pi))
DisplayError(L"CreateProcessWithLogonW");
if (!DestroyEnvironmentBlock(lpvEnv))
DisplayError(L"DestroyEnvironmentBlock");
CloseHandle(hToken);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
I tried this and I am getting no output on the console window. As I said, I checked the answer on StackOverflow and this whole C++/CLI thing confuses me. If someone knows a simple solution to redirecting the output using a custom pipe using Visual C++, let me know.
And if I did anything wrong above, let me know as well.
EDIT:
After following the suggestions given by Remy's answer. I came up with the following code, which yields an error code 3: The system cannot find the path specified.
#include "stdafx.h"
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
int BUFSIZE = 1064;
void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
void DisplayError(LPWSTR pszAPI)
{
LPVOID lpvMessageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&lpvMessageBuffer, 0, NULL);
//
//... now display this string
//
wprintf(L"ERROR: API = %s.\n", pszAPI);
wprintf(L" error code = %d.\n", GetLastError());
wprintf(L" message = %s.\n", (LPWSTR)lpvMessageBuffer);
//
// Free the buffer allocated by the system
//
LocalFree(lpvMessageBuffer);
ExitProcess(GetLastError());
}
void _tmain(int argc, WCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
/////////////////////////////Start CreateChildProcess////////
STARTUPINFO si;
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_USESHOWWINDOW;
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
DWORD dwSize;
HANDLE hToken;
LPVOID lpvEnv;
PROCESS_INFORMATION pi = {0};
WCHAR szUserProfile[256] = L"";
si.cb = sizeof(STARTUPINFO);
if (argc != 4)
{
wprintf(L"Usage: %s [user#domain] [password] [cmd]", argv[0]);
wprintf(L"\n\n");
return;
}
//
// TO DO: change NULL to '.' to use local account database
//
if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, &hToken))
DisplayError(L"LogonUser");
if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
DisplayError(L"CreateEnvironmentBlock");
dwSize = sizeof(szUserProfile)/sizeof(WCHAR);
if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
DisplayError(L"GetUserProfileDirectory");
//
// TO DO: change NULL to '.' to use local account database
//
if (!CreateProcessWithLogonW(argv[1], NULL, argv[2],
LOGON_WITH_PROFILE, NULL, argv[3],
CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile,
&si, &pi))
DisplayError(L"CreateProcessWithLogonW");
if (!DestroyEnvironmentBlock(lpvEnv))
DisplayError(L"DestroyEnvironmentBlock");
CloseHandle(hToken);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
/////////////////////////////End CreateChildProcess///////////
if (argc == 1)
ErrorExit(TEXT("Please specify an input file.\n"));
g_hInputFile = CreateFile(
argv[3],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);
if ( g_hInputFile == INVALID_HANDLE_VALUE )
ErrorExit(TEXT("CreateFile"));
WriteToPipe();
printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
ReadFromPipe();
}
void WriteToPipe(void)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[4096];
BOOL bSuccess = FALSE;
for (;;)
{
bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
if ( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
if ( ! bSuccess ) break;
}
// Close the pipe handle so the child process stops reading.
if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT("StdInWr CloseHandle"));
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[4096];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (! bSuccess ) break;
}
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
Use anonymous pipes via CreatePipe(), not named pipes via CreateNamedPipe(). See MSDN for an example:
Creating a Child Process with Redirected Input and Output