How to pass named pipe into CreateProcess (C++ Windows) - c++

The hierarchy of my project is that I have 3 processes.
Main exe
Middle exe
Worker exe
Main exe will spawn middle exe using createprocess, from there middle exe will spawn worker exe using createprocess as well. Once worker exe is running middle exe terminates.
I would like to be able to pass the output data from worker exe back to main exe.
The way I think is best is to use a named pipe as worker exe's stdout.
In main exe I have this code:
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
HANDLE hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\MPipe"),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
PIPE_UNLIMITED_INSTANCES,
1024 * 4,
1024 * 4,
0,
&sa);
//CreateProcess() code here for middle exe
if (hPipe == NULL || hPipe == INVALID_HANDLE_VALUE) {
MessageBoxA(NULL, "Failed to create pipe", NULL, MB_OK);
}
int count = 0;
while (true) {
if (!ConnectNamedPipe(hPipe, 0)) {
std::this_thread::sleep_for(std::chrono::seconds(20));
count++;
}
if (count > 5)
break;
}
if (count > 5) {
MessageBoxA(NULL, "Failed...", NULL, MB_OK);
}
Then in middle exe I have
memset(&si, 0x00, sizeof(si));
memset(&pi, 0x00, sizeof(pi));
si.cb = sizeof(si);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE hPipe;
BOOL keepGoing = TRUE;
while(keepGoing){
hPipe = CreateFile(TEXT("\\\\.\\pipe\\MPipe"),
GENERIC_WRITE | GENERIC_READ,
0,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hPipe == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PIPE_BUSY) {
MessageBoxA(NULL, "Pipe busy...", NULL, MB_OK);
WaitNamedPipe("\\\\.\\pipe\\MPipe", 2000);
}
else {
MessageBoxA(NULL, "Failed to open pipe for writing", NULL, MB_OK);
break;
}
}
else {
keepGoing = FALSE;
}
}
MessageBoxA(NULL, "Client pipe connected!", NULL, MB_OK);
SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, 0);
si.hStdError = hPipe;
si.hStdOutput = hPipe;
processFlags = CREATE_SUSPENDED;
CreateProcessW(processPath, commandLine, NULL, NULL, TRUE, processFlags, NULL, NULL, &si, &pi);
For some reason in Main exe it will output "Failed to establish connection..." But in my Middle exe it will output "Client connected!".
I can't figure out why the Middle exe appears to establish a connection but the Main exe doesn't recognize it when ConnectNamedPipe() is called.
Any ideas?

Related

How to execute commands in the same process

I made my own function to execute a command and save the result. The problem is the command is executed once and I can't execute several commands with the same "context". For example. If I execute cd .., the next command my cd .. will be ignored. This is my code :
std::string executeCommand(const std::string& cmd)
{
system((cmd + " > temp.txt").c_str());
std::ifstream ifs("temp.txt");
std::string ret{ std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>() };
ifs.close(); // must close the inout stream so the file can be cleaned up
return ret;
}
Can I have a sample with this feature ?
Do you use Windows system?
In Windows, you can interact with cmd through a Pipe.
like this:
#define BUFFER_SIZE 131072 //128*1024‬
void work() {
HANDLE hinRead, hinWrite, houtRead, houtWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if (!CreatePipe(&hinRead, &hinWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
if (!CreatePipe(&houtRead, &houtWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
STARTUPINFOA si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = hinRead;//redirect Input
si.hStdOutput = si.hStdError = houtWrite;//redirect Output
PROCESS_INFORMATION ProcessInformation;
char cmdLine[256] = { 0 };
GetSystemDirectoryA(cmdLine, sizeof(cmdLine));
strcat(cmdLine, ("\\cmd.exe"));
if (CreateProcessA(cmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &ProcessInformation) == 0) {
MessageBoxA(NULL, "Error CreateProcessA", "Error", MB_ICONERROR);
}
char* pbuff = new char[BUFFER_SIZE];
DWORD dw;
Sleep(100);//wait cmd start
ReadFile(houtRead, pbuff, BUFFER_SIZE-1, &dw, NULL);
pbuff[dw] = 0;
printf(pbuff);//version info
while (1) {
char* p = pbuff;
while ((*p = getchar()) != '\n') {
++p;
}
WriteFile(hinWrite, pbuff, p - pbuff + 1, &dw, 0);
while (1) {
ReadFile(houtRead, pbuff, BUFFER_SIZE - 1, &dw, NULL);
pbuff[dw] = 0;
printf("%s", pbuff);
if (pbuff[dw - 1] == '>')break;
}
}
}
Pipe can read/write like a file.
In other systems, there is also something like pipe,but I'm not familiar with them.
edit: another more clear example
int main() {
HANDLE hinRead, hinWrite, houtRead, houtWrite;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = true;
if (!CreatePipe(&hinRead, &hinWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
if (!CreatePipe(&houtRead, &houtWrite, &sa, 0)) {
MessageBoxA(NULL, "Error CreatePipe", "Error", MB_ICONERROR);
}
STARTUPINFOA si;
ZeroMemory(&si, sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;//no window
si.hStdInput = hinRead;//redirect Input
si.hStdOutput = si.hStdError = houtWrite;//redirect Output
PROCESS_INFORMATION pi;
char cmdLine[256] = { 0 };
GetSystemDirectoryA(cmdLine, sizeof(cmdLine));
strcat(cmdLine, ("\\cmd.exe"));
if (CreateProcessA(cmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == 0) {
MessageBoxA(NULL, "Error CreateProcessA", "Error", MB_ICONERROR);
}
Sleep(100);//wait cmd start
char* pbuff = new char[1024 * 128];
DWORD dw;
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);//cmd version info
WriteFile(hinWrite, "cd ..\n", 6, &dw, NULL);//send command to cmd.exe
Sleep(100);//wait command execute and cmd outputs
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);
WriteFile(hinWrite, "dir\n", 4, &dw, NULL);//another command
Sleep(100);
ReadFile(houtRead, pbuff, 1024 * 128, &dw, NULL), pbuff[dw] = 0, printf("%s", pbuff);
delete[] pbuff;
TerminateProcess(pi.hProcess, 0);//Terminate cmd.exe
return 0;
}

Execute CMD.EXE with CreateProcessWithLogonW() without a new console

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.

How to execute commands from an attached console

I'm coding a WinAPI GUI program that needs calling ftp and possibly other console programs while getting their console output to act accordingly ie. waiting for ftp to complete execution before reading all its output wouldn't do.
My current approach is calling CreateProcess() to create a cmd.exe process potentially hiding the ugly console window, AttachConsole() to make it my own, GetStdHandle() to get input and output handles, SetConsoleCursorPosition() to the end of the console buffer, and WriteConsole() with commands such as ftp\n or dir\n. Yet this commands are written but not executed. However, I can manually use the same console ( using CreateProcess() with CREATE_NEW_CONSOLE flag ) to type ftp press enter and get it executed.
Previous approaches involved:
Calling ftp directly with CreateProcess() and redirected inputs/outputs.
Couldn't get ftp output until the CreateProcess() process had already ended.
Using system().
Was advised against its usage before getting any output.
My current stripped down code:
// Next two structures might be a bit misleading, they were used for the 1. previous
// approach
PROCESS_INFORMATION piProcInfo;
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION));
STARTUPINFO siStartInfo;
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;
SECURITY_ATTRIBUTES security;
security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.lpSecurityDescriptor = NULL;
security.bInheritHandle = FALSE;
CreateProcess( NULL, "cmd", &security, &security, FALSE, NORMAL_PRIORITY_CLASS |
CREATE_NEW_CONSOLE, NULL, NULL, &siStartInfo, &piProcInfo);
uint32_t pidConsole = piProcInfo.dwProcessId;
while ( ! AttachConsole(pidConsole) ){};
HANDLE myConsoleIn, myConsoleOut;
myConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
myConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE);
Sleep(100);
CONSOLE_SCREEN_BUFFER_INFO myConsoleCursorInformation = {};
GetConsoleScreenBufferInfo(myConsoleOut,&myConsoleCursorInformation);
SetConsoleCursorPosition(myConsoleOut,myConsoleCursorInformation.dwSize);
CHAR myConsoleBuffer[200]="dir\n";
DWORD myConsoleProcessed;
WriteConsole( myConsoleOut, myConsoleBuffer, 4, &myConsoleProcessed, NULL);
How can I get a command written in the console to execute? Is there an alternative to my attempt of ending commands with a trailing \n ie. using WriteConsole() with a dir\n or ftp\n argument.
I thought about sending a keypress to the process in question after typing the desired command. Yet the created console needs not only to manually press the enter key but also having dir, ftp or whatever command to be manually typed.
Please feel free to point out any missing information !
How can I get a command written in the console to execute? Is there an
alternative to my attempt of ending commands with a trailing \n ie.
using WriteConsole() with a dir\n or ftp\n argument.
Try the following code to see if it works:
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
const wchar_t *cmdPath = L"C:\\Windows\\System32\\cmd.exe";
wchar_t *cmdArgs = (wchar_t *)L"C:\\Windows\\System32\\cmd.exe /k dir";
BOOL result = CreateProcess(cmdPath, cmdArgs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
/K Run Command and then return to the CMD prompt.
This is useful for testing, to examine variables
Use /C if you want "Run Command and then terminate".
Update: Complete code for communicating with a child process(cmd.exe) using pipes.
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
#define BUFSIZE 1024
void ErrorExit(LPCTSTR 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);
}
void ReadFromPipe(void)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
DWORD bytesAvail = 0;
if (!PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, 0, NULL, &bytesAvail, NULL)) {
std::cout << "Failed to call PeekNamedPipe" << std::endl;
}
if (bytesAvail) {
DWORD n;
BOOL success = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &n, NULL);
if (!success || n == 0) {
}
bSuccess = WriteFile(hParentStdOut, chBuf,n, &dwWritten, NULL);
}
else
{
break;
}
}
}
void WriteToPipe(void)
{
DWORD dwWritten;
BOOL bSuccess = FALSE;
CHAR buf[] = "dir\n";
bSuccess = WriteFile(g_hChildStd_IN_Wr, buf, sizeof(buf)-1, &dwWritten, NULL);
}
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
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"));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(STARTUPINFO);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
TCHAR cmdPath[] = TEXT("C:\\Windows\\System32\\cmd.exe");
BOOL result = CreateProcess(cmdPath, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
DWORD errCode = GetLastError();
if (!result)
{
std::cout << "Create Process failed: " << GetLastError() << std::endl;
}
for (;;)
{
ReadFromPipe();
WriteToPipe();
}
}

How can i make createprocess reindirect input success, I have successully get the reindirect out put result

I searched a lot on internet, but most of them are talking about reindirect output. No one gives any successful reindirect input example. For my codes below, it gave right output when I ran command "ipconfig" or "192.168.0.10" because child process ends after running these commands, no input needed. But when I ran command "ftp" instead of "ipconfig" child process which is console is waiting for the next input command. And I tried to write 11111 as input to console in this case as you can see. However console did not receive my input command and waiting for the input command forever. How can I successfully response to "ftp" command in this program and keep console running
#include <windows.h>
#include <fstream>
using namespace std;
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpComLine,
int nCmdShow)
{
SECURITY_ATTRIBUTES secAttr;
HANDLE hRead,hWrite;
char command[256];
char testBuf[256] = {0};
strcpy(command, "ipconfig");
// strcpy(command, "ping 192.168.0.10")
// strcpy(command, "ftp");
secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
secAttr.lpSecurityDescriptor = NULL;
secAttr.bInheritHandle = TRUE;
HANDLE hTxtFile = CreateFile("tmp.txt", GENERIC_ALL, 0, &secAttr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hTxtFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error createfile", NULL, MB_OK);
return 0;
}
HANDLE hWriteFile = CreateFile("Write.txt", GENERIC_WRITE, 0, &secAttr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hWriteFile == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "Error createWritefile", NULL, MB_OK);
return 0;
}
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
startupInfo.cb = sizeof(STARTUPINFO);
GetStartupInfo(&startupInfo);
startupInfo.hStdError = hTxtFile;
startupInfo.hStdOutput = hTxtFile;
startupInfo.hStdInput = hWriteFile;
startupInfo.wShowWindow = SW_SHOW;
startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
char output[10240] = {0};
DWORD bytesRead;
if (!CreateProcess(NULL, command,NULL,NULL,TRUE,NULL,NULL,NULL,&startupInfo,&processInfo))
{
MessageBox(NULL, "Error createprocess", NULL, MB_OK);
CloseHandle(hWrite);
CloseHandle(hRead);
return FALSE;
}
DWORD processExitCode = 0;
strcpy(testBuf, "11111\r\n");
while (GetExitCodeProcess(processInfo.hProcess, &processExitCode))
{
WriteFile(hWriteFile, testBuf, 7, &bytesRead, NULL);
if (processExitCode != STILL_ACTIVE)
{
// MessageBox(NULL, "End process", NULL, MB_OK);
break;
}
Sleep(1000);
}
SetFilePointer(hTxtFile, NULL, NULL, FILE_BEGIN);
ReadFile(hTxtFile, output, 10240, &bytesRead, NULL);
CloseHandle(hTxtFile);
MessageBox(NULL, output, NULL, MB_OK);
return 0;
}
Redirecting (not "reindirecting") input works just the same as redirecting output. Of course, the flow of data is in the opposite direction. This means that the process reads from the file. This in turn means when you open a handle for writing, as you do in the example code:
HANDLE hWriteFile = CreateFile("Write.txt", GENERIC_WRITE, ...);
the process will not be able to read from it. You must open the file for reading:
HANDLE hWriteFile = CreateFile("Write.txt", GENERIC_READ, ...);
But then, this also means that you must prepare the input that you want to send down to the process in advance. It does not help to write to the file after you have created the process.
If you do not know the data that you have to send to the process in advance, you cannot use a file for standard input, but you must use something else, such as a (named or anonymous) pipe.
you must redirect the console output and then write the buffer into file
1)
/* Create a pipe for the child process's STDOUT */
if(!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
BAIL_OUT(-1);
2)
/* Duplicate the pipe HANDLE */
if (!DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
BAIL_OUT(-1);
3)
CHAR chBuf[BUFSIZE];
DWORD dwRead;
DWORD dwAvail = 0;
if (!PeekNamedPipe(hChildStdoutRdDup, NULL, 0, NULL, &dwAvail, NULL) || !dwAvail)
return;
if (!ReadFile(hChildStdoutRdDup, chBuf, min(BUFSIZE - 1, dwAvail), &dwRead, NULL) || !dwRead)
return;
chBuf[dwRead] = 0;
Please find more details here:
https://www.codeproject.com/Articles/5531/Redirecting-an-arbitrary-Console-s-Input-Output

CreateProcessWithLogonW get standard output

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