I am using CreateProcess() to run an external console application in Windows from my GUI application. I would like to somehow gather the output to know whether there were errors. Now I know I have to do something with hStdOutput, but I fail to understand what. I am new to c++ and an inexperienced programmer and I actually don't know what to do with a handle or how to light a pipe.
How do I get the output to some kind of variable (or file)?
This is what I have a the moment:
void email::run(string path,string cmd){
WCHAR * ppath=new(nothrow) WCHAR[path.length()*2];
memset(ppath,' ',path.length()*2);
WCHAR * pcmd= new(nothrow) WCHAR[cmd.length()*2];
memset(pcmd,' ',cmd.length()*2);
string tempstr;
ToWCHAR(path,ppath); //creates WCHAR from my std::string
ToWCHAR(cmd,pcmd);
STARTUPINFO info={sizeof(info)};
info.dwFlags = STARTF_USESHOWWINDOW; //hide process
PROCESS_INFORMATION processInfo;
if (CreateProcess(ppath,pcmd, NULL, NULL, FALSE, 0, NULL, NULL, &info, &processInfo))
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
delete[](ppath);
delete[](pcmd);
}
This code probably makes any decent programmer scream, but (I shouldn't even say it:) It works ;-)
The Question: How do I use hStdOutput to read the output to a file (for instance)?
Microsoft has an example in its knowledge base that demonstrates how to capture the output of a child console process. The basic principle is that the parent process creates pipes (one per standard handle to redirect) and passes the handles to CreateProcess.
The child process does not need to be modified for this to work, which is important if you do not have control over the child's source.
More information: How to spawn console processes with redirected standard handles
Related
I am trying to redirect the stdout of a child process in Windows. Both are console programs. I don't have the source code of the child process, so I can't force it to flush the buffer. As discussed here and here, for the implementation of printf and similar, the C runtime buffers everything except consoles and printers. So the solution is apparently to create a console screen buffer using, appropriately enough, CreateConsoleScreenBuffer. I'm using the approach from codeproject.
PROCESS_INFORMATION pi;
HANDLE hConsole;
const COORD origin = { 0, 0 };
// Default security descriptor. Set inheritable.
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE; // So the child can use it
// Create and initialize screen buffer
hConsole = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE, // Desired access
FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode to child processes
&sa, // SECURITY_ATTRIBUTES
CONSOLE_TEXTMODE_BUFFER, // Must be this.
NULL // Reserved. Must be NULL
);
DWORD dwDummy;
FillConsoleOutputCharacter(hConsole, '\0', MAXLONG, origin, &dwDummy)
Now I direct the child's stdout to the console and start the process
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_FORCEOFFFEEDBACK | STARTF_USESTDHANDLES; // first one prevents cursor from showing loading.
si.hStdOutput = hConsole;
//...
// Get the command line and environmental block
//...
if (! CreateProcess(
NULL, // module name.
(char*)command_line.c_str(), // command line
NULL, // process SECURITY_ATTRIBUTES
NULL, // thread SECURITY_ATTRIBUTES
TRUE, // inherit handles
NULL, // creation flags
enviros, // environmentBlock (enviros=NULL for testing)
cDir, // working directory
&si, // STARTUP_INFO object
&pi // PROCESSINFO
) ){
auto test = GetLastError();
CloseHandle(hConsole);
return false;
}
CloseHandle(pi.hThread);
Then, in a loop, I can use ReadConsoleOutputCharacter to grab output, as shown at the codeproject link. It looks like
//... some initialization
GetConsoleScreenBufferInfo(hConsole, &csbi);
DWORD count = (csbi.dwCursorPosition.Y - lastpos.Y)*lineWidth + csbi.dwCursorPosition.X - lastpos.X;
LPTSTR buffer = (LPTSTR)LocalAlloc(0, count * sizeof(TCHAR));
ReadConsoleOutputCharacter(hConsole, buffer, count, lastpos, &count);
DWORD dwDummy;
FillConsoleOutputCharacter(hConsole, '\0', count, lastpos, &dwDummy);
//... Now move the cursor and grab the data from `buffer`
On Windows 7/8.1 this works fine for all programs. On Windows 10, some programs seem to be bypassing the supplied handles and printing directly to the parent console, preventing me from grabbing the output as I need.
I do have an additional clue. If I force the process to create a new console window, i.e.
CreateProcess(NULL, (char*)command_line.c_str(), NULL, NULL, TRUE, CREATE_NEW_CONSOLE, enviros, cDir, &si, &pi)
but still redirect the handles in the STARTUPINFO object, the new console will display a single line that says The system cannot write to the specified device, which just happens to be the exact wording of Windows error code ERROR_WRITE_FAULT = 29 in the MSDN docs. This happens only for those programs which aren't working as expected. Other programs, the new console is blank and they still function as expected.
My first thought is a permissions problem, but I have wide open permissions on the directories of the relevant executables.
Things I've tried
A different computer with Windows 10
Different versions of MS Visual C++ runtime
Setting the working directory explicitly in CreateProcess
Adding a super-permissive DACL to a SECURITY_ATTRIBUTES object passed to CreateProcess
Adding a super-permissive DACL to a SECURITY_ATTRIBUTES object passed to CreateConsoleScreenBuffer
Moving everything to a newly created directory under my Windows user directory
Running as administrator
Deeper
Thank you #PaulSanders for your suggestion.
To aid with anyone who might want to assist, I've made available a modified version of the RTConsole code from the codeproject page. It should compile in Visual Studio with just a retarget, I think. Around line 135, I've prepended a little string to the front of the output which takes the expected path. I've provided a pre-compiled version in there for convenience, as well.
One example of software that doesn't work is EWBF miner. For a quick test using the code I provided above, you could run
RTConsole2.exe "path\to\ewbf.exe" --help
You'll see that the prepended flag is not present in the output.
On the other hand, with ccminer, you'll get the expected behavior when running
RTConsole2.exe "path\to\ccminer.exe" --help
The new console implementation in Windows 10 has a bug in which high-level WriteConsole and WriteFile to a non-active screen buffer instead always writes to the active screen buffer. Low-level WriteConsoleOutput[Character] works correctly. Using the legacy console also works. You can enable the legacy console in the properties dialog.
Note that a process cannot use an inherited handle for a screen buffer in the parent's console if it allocates a new console due to the CREATE_NEW_CONSOLE flag. Trying to write to the screen-buffer file will fail because it's not bound to the caller's attached console (i.e. instance of conhost.exe).
Console files that are bound include "CON", "CONIN$", "CONOUT$", and a new screen buffer from CreateConsoleScreenBuffer. There are also unbound input and output console files, which are set as the standard handles when allocating a new console (e.g. via AllocConsole()). These handles access the input buffer and active screen buffer of any attached console [*]. Note that a process can have handles for console files that are bound to multiple consoles and can switch between consoles using AttachConsole.
Note also that some programs open "CONOUT$" instead of writing to the StandardOutput and StandardError handles, especially if they require a console instead of whatever the standard handles might be (e.g. a pipe or disk file). In this case it is not enough to set hStdOutput in STARTUPINFO. You have to temporarily make the new screen buffer active via SetConsoleActiveScreenBuffer. This does not affect the caller's standard handles. It sets the active screen buffer in the attached console, which is what "CONOUT$" opens. The previous screen buffer can be restored after the child process exits, or after you know the child has already opened and written to the new screen buffer.
[*] In Windows 8+, these console files are implemented by the condrv.sys device driver and opened on "\Device\ConDrv". They are respectively named "Console", "CurrentIn", "CurrentOut", "ScreenBuffer", "Input", and "Output". The filename for the console connection itself is "Connect". Internally the latter is opened as the process ConsoleHandle, which is used implicitly by the console API in some cases (e.g. GetConsoleWindow, GetConsoleCP, and GetConsoleTitle).
So, on Windows, it seems execl() is broken in different ways across different versions of windows. I've spent a lot of time narrowing it down and debugging, and it doesn't really make sense, and I can only think there's something wrong with Microsoft's implementation of execl() (actually execlp() for me).
The only Windows version execlp() seems to work correctly on is 7.
On Windows 10, it works fine, until I compile with -mwindows in MinGW.
Then it just makes my program terminate with a zero exit code.
On Windows XP, it interprets spaces in argument parameters as separate arguments, despite the actual number of arguments being clearly specified by the nature of the function's prototype...
So, looks like I'll have to use some Windows native function and wrap it in "#ifdef WIN32"s.
What I really need is execl() (execlp isn't necessary) like behavior on Windows, in that it replaces the current process image with a new one, and keeps network descriptors open like execl() will.
I'm really at a loss as to what good options are, and while CreateProcess seems somewhat able to do some of it, I can't find enough info on what I'm trying to do.
Any help is appreciated. Thanks!
Unfortunately, you will need a completely different code path on windows, because in the win32 subsystem, creating a process is coupled together with loading and running a new image in a single call: CreateProcess().
In a typical posix scenario, you would fork() your new process, set up things like e.g. file descriptors and then exec*() the new binary. To achieve something similar in Windows, you must rely on the possibilities you get from CreateProcess(). For open files (or sockets), win32 uses "handles" and these can be marked inheritable, for example I do the following for a pipe:
HANDLE pin, pout;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = 1;
if (!CreatePipe(&pin, &pout, &sa, 0))
{
fprintf(stderr, "Error creating pipe: %lu\n", GetLastError());
return;
}
This way, the pipe's handles are inheritable. Then, when calling CreateProcess(), by passing 1 (or TRUE) for bInheritHandles, the new process inherits all handles marked this way. In this example, I do the following:
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.hStdInput = nul;
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
// simple example, my production code looks different, e.g. quoting the command:
char cmdline[1024];
strcpy(cmdline, exename);
strcat(cmdline, " ");
snprintf(cmdline + strlen(cmdline), 1024 - strlen(cmdline), "%" PRIxPTR, (uintptr_t)pout);
// [...]
PROCESS_INFORMATION pi;
CreateProcess(0, cmdline, 0, 0, 1, 0, 0, 0, &si, &pi);
In the child, the pipe could be used like that:
uintptr_t testPipeHandleValue;
if (sscanf(argv[1], "%" SCNxPTR, &testPipeHandleValue) != 1)
{
exit(EXIT_FAILURE);
}
int testPipeFd = _open_osfhandle(
(intptr_t)testPipeHandleValue, _O_APPEND | _O_WRONLY);
FILE *testPipe = _fdopen(testPipeFd, "a");
setvbuf(testPipe, 0, _IONBF, 0);
Of course this will look different for a network socket, but I hope the general idea helps.
So, curious problem, I'm trying to create a process, and then resume it, mostly exploring the Windows API. I've noticed that if I do this:
system("C:\\Windows\\System32\\calc.exe");
It will open a calculator exe, however if I try to do the same thing using CreateProcessA, I get this:
STARTUPINFO starting_info;
PROCESS_INFORMATION process_info;
// let's try and make a process
if (!CreateProcessA(NULL, "C:\\Windows\\System32\\calc.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &starting_info, &process_info)) {
return;
}
// resume thread
NtResumeThread(process_info.hThread, NULL);
This for some reason throws an error of 0xc0000142 most of the times when it "creates" the process, else it just fails.
What's going on?
See the following MSDN sample code for creating a process:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx
You need to zero out the si and pi structs, also set
si.cb = sizeof(si);
In the end, close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
When I call CreateProcess in Windows, the new process doesn't seem to inherit the console of the calling process. I made a test program that runs "ruby xtest", xtest being a script that writes "hello" to standard output. I ran this test program from Emacs, and get no output. I also tried the following code calling GetStdHandle, but again, no output. Then I tried passing CREATE_NEW_CONSOLE in dwCreationFlags to CreateProcess, which made a whole new window with the Ruby output. Finally, I made a simple fork/exec
test program and compiled it using Cygwin's GCC. This program worked: the Ruby output showed up in Emacs as expected. I tried to decipher the Cygwin source code in http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/spawn.cc?rev=1.268&content-type=text/x-cvsweb-markup&cvsroot=src but failed. So, how do you make the new process inherit the console of the parent process such that the output from the child shows up as expected?
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess(0, "ruby xtest", 0, 0, 1, 0, 0, 0, &si, &pi)) die("CreateProcess");
I know, this thread is rather old, however, I just ran into the same problem.
Just as for the TS, the console handle was inherited and working fine under Cygwin, but not on a Windows console. Instead, the output on stdout was neither shown, nor any error was reported. Inherited Pipe handles worked still fine.
I took me some time to identify the (now obvious) problem: CreateProcess() was called with CREATE_NO_WINDOW. Dropping this flag, console output is fine. (Though, according to the code of the TS, they never set this flag in the first place.)
Hope this might be helpful for people who also stumble across this thread, like myself.
According to Microsoft documentation, lpCommandLine (2. parameter):
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.
When I stopped using a constant here it worked for me. I didn't need the STARTF_USESTDHANDLES and GetStdHandle thing.
This code from a console prg runs and outputs another console exe in the same console:
FillChar(SI, SizeOf(SI), 0);
SI.cb:=SizeOf(SI);
FillChar(PI, SizeOf(PI), 0);
if CreateProcess(nil, CmdLineVar, nil, nil, False, 0, nil, nil, SI, PI) then ...
I've done this by passing in pipes for hStdInput, hStdOutput, and hStdError and manually routing data from the hStdOutput and hStdError pipes to the console.
Not sure if debeige ever solved this, but I needed the same thing, but starting up another thread to listen to stdout output, just to put it on stdout seemed nuts to me.
The following works for me, and is slightly different than what he originally posted. I thought at first it wouldn't work if you don't set si.cb, but when I commented that in mine, it still worked, so... YMMV.
STARTUPINFO siStartInfo;
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdInput = g_hChildStd_IN_Rd; // my outgoing pipe
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(
NULL,
szCmdline,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&siStartInfo,
&piProcInfo);
I've coded two processes using C++. One is the GUI process that is called by my console app using CreateProcess API. I need to pass text from the GUI app (child) to the console app (parent.) The amount of text could be arbitrary -- from a few lines to KBs of text.
What is the easiest way to do it?
PS. I have access to the source code of both processes.
Console application can create a WinAPI window (non-visible), such that it can receive messages (idea taken from AllocateHWND function in Delphi).
Another solution is to use named pipes.
Another solution is to send data locally via TCP/IP.
If these strings are only a debug ones, consider using OutputDebugString function from WinAPI and capturing them with a program like SysInternals' DbgView.
If the GUI application is truly graphical only, you don't really use the standard output stream (i.e. std::cout). This can the be reused for output to your console application.
First you need to create an anonymous pipe using CreatePipe:
HANDLE hPipeRead;
HANDLE hPipeWrite;
CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0);
Now you have to handles that can be used as a normal file handle; One to read from and the other to write to. The write-handle should be set as the standard output for the new process you create:
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdOutput = hPipeWrite; // Standard output of the new process
// is set to the write end of the pipe
CreateProcess(
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&startupInfo, // Use our startup information
&processInfo);
Now whenever the child process needs to write to the parent, it only have to use standard output:
std::cout << "In child process, are you getting this parent?";
The parent uses ReadFile to read from the read-end of the pipe:
char buffer[256];
DWORD bytesRead = 0;
ReadFile(hPipeRead, buffer, sizeof(buffer), &bytesRead, NULL);
Note: I haven't done WIN32 programming in some time, so might be wrong on some details. But should hopefully be enough to get you started.
There are of course many other ways if Inter Process Communications (IPC), including (but not limited to) sockets, files, shared memory, etc.
The easy way is probably to make the child actually a console application, even though it also creates windows.
In that case, you can have your parent spawn the child using _popen, and the child can just write the output to its normal stdout/std::cout. _popen returns a FILE *, so the parent can read the child's output about like it'd normally read a file (well, normally for C anyway).
Various methods can be used, some of them were given above. Which one is simplest depends on you task.
I can also suggest you filemapping technics which is widely used in IPC, and for ex. dll are implemented using filemapping.
It allows mutliply processes to share the same resources simultaniously, access is random not consequntial.
Here are main steps of implementation:
1. Process A creates a file;
2. Process A creates a named system object mappedfile to the file (mappedfile allocates memory);
3. Process A creates a system object a viewOfMapped file(this allows to map some area of the process A to the pages in the main memory which were allocated by mappedFile);
4. Process B creates the named system object mappedfile(name should be similar to the one of process A used), viewOfMapped file;
5. By pointers returned by viewOfMapped processes can share the same memory.
Example:
Process A:
/* 1. file creation */
hFile = CreateFile(0, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
/* 2. Create file mapping */
wchar_t lpName[] = L"fileMappingObject0";
HANDLE hfileMappingObject;
hfileMappingObject = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024, lpName);
/* 3. Create MappedFileViewOfFile */
void* p = (MapViewOfFile(hfileMappingObject, FILE_MAP_ALL_ACCESS, 0, 0, 0));
Process B:
/* 2. Create file mapping */
wchar_t lpName[] = L"fileMappingObject0";
HANDLE hfileMappingObject;
hfileMappingObject = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024, lpName);
/* 3. Create MappedFileViewOfFile */
void* p = (MapViewOfFile(hfileMappingObject, FILE_MAP_ALL_ACCESS, 0, 0, 0));
This method is rather simple and also powerfull.