I need to start a process that starts the commandline (it should not pop up, so something like a background process).
Then I need to write stuff to it and periodically read the last line of the cmd.
Since my C++ skills aren't that great I don't know how to achieve this.
In a pseudocode way I thought about something like this:
startProcess();
writeToCmd();
readFromCmd() { // Call that every given interval (e.g. 1000 ms)
if(returnValue >= 100) {
stopProcess();
}
}
I'm pretty sure it'll not be that easy. It would be really great if someone can help me out here.
The programm is for windows.
Edit after the suggestions:
This is what I've done so far (I did it a little bit different.)
int errorCode;
// Variables for CreateProcess()
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
PHANDLE hInRead, hInWrite;
LPDWORD bytesWritten, bytesRead;
// The CommandLine input
TCHAR tcsCommandLine[] = _T("cmd /c format H: /fs:FAT32 /V:device");
//TCHAR tcsCommandLine[] = _T("cmd /c start D:\\foo.txt");
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW; // SW_HIDE FOR PRODUCTION
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdInput = hInRead;
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
ZeroMemory(&pi, sizeof(pi));
errorCode = CreatePipe(hInRead, hInWrite, &sa, 0);
info = GetDriveInfo(m_wsNANDMountPoint);
if(info.m_uSizeInMB > 2048) {
log("Wrong Mounting Point. Device has more than 2GB space.");
return false;
}
else {
// Start Formatting
if(!CreateProcess(NULL, tcsCommandLine,
NULL, NULL,
TRUE, CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS, NULL, NULL,
&si,
&pi)) {
log("CreateProcess failed. Could not format Drive");
return false;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
WriteFile(hInWrite, "#13#10'J'#13#10", 5, bytesWritten, NULL);
CloseHandle(hInWrite);
// Wait until child process exits
WaitForSingleObject(pi.hProcess, 1100);
}
return true;
After a little bit of debugging, I recognized that the code doesn't break at ZeroMemory(), but on
errorCode = CreatePipe(hInRead, hInWrite, &sa, 0);
with an Access violation writing location error. I have no idea what I'm doing wrong.
It would be really great if you guys could help me out.
What you need to do in this instance is to create a console with a hidden window. If you use CreateProcess to launch the console, you should be able to set the visibility of the window through the STARTUPINFO structure.
The next step is to redirect the input and output of your console. You can do this by attaching the 3 console handles (input, output, error) to pipes and reading that from your parent process. This MSDN article describes how to do exactly this.
The command line consists of two parts you would be interested in.
A place to execute programs
A buffer for whatever is written on the screen
Since you don't need the screen to be visible, then you need these two things in your program. So from here on, forget about cmd.
To execute programs in your program, you have many ways. If you want the execution lines to look exactly the way you write it in cmd, you can use system (although Windows versions of fork/exec make more sense). For example:
system("my_prog.exe --option file.txt");
Which executes my_prog.exe with --option and file.txt as arguments.
Now the second one, you said you wanted to read the last line from cmd. The idea to realize this is to have the output of everything in a file, instead of in cmd.
If you are only interested in the last line at every instance, you can redirect the output of your programs to a file like this:
system("my_prog.exe --option file.txt > output.txt");
or if you want to keep the whole history, you can append instead of overwrite:
system("my_prog.exe --option file.txt >> output.txt");
If you also want to redirect stderr, you can write:
system("my_prog.exe --option file.txt &> output.txt");
The notation may be linuxy, try it in your windows see if it works (manually in a normal cmd). You could also search google for "shell redirect" and you get a lot of results.
Anyway, if you want the last line of cmd to also include the command itself, you can overwrite/append the command line to that particular yourself in the program.
Let me add this to these excellent answers:
This great link demonstrates console read and write : http://www.adrianxw.dk/SoftwareSite/Consoles/Consoles2.html
To do stuff periodically, use SetTimer().
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906(v=vs.85).aspx
The SetTimer function executes a function every x milliseconds.
Example:
The following console program works like this: It sets a timer using SetTimer
then loops in a message loop. The message loop receives and processes WM_TIMER messages
and the timer callback also is called for each time interval.
Simply put the stuff you want done in the TimerProc() function.
#define STRICT 1
#include <windows.h>
#include <iostream.h>
VOID CALLBACK TimerProc(HWND hWnd, UINT nMsg, UINT nIDEvent, DWORD dwTime)
{
//put the stuff you want done in here
cout << "Doing stuff Time: " << dwTime << '\n';
cout << "--------------------------------------------------\n" ;
cout.flush();
}
int main(int argc, char *argv[], char *envp[])
{
int Counter=0;
int usage_Time_millisec=1000;
MSG Msg;
UINT TimerId = SetTimer(NULL, 0, usage_Time_millisec, &TimerProc); //bind TimerProc() to SetTimer()
cout << "TimerId: " << TimerId << '\n';
if (!TimerId) return 16;
while (GetMessage(&Msg, NULL, 0, 0))
{
++Counter;
if (Msg.message == WM_TIMER)
cout << "Doing stuff Counter: " << Counter << "; timer message\n";
else
cout << "Doing stuff Counter: " << Counter << "; message: " << Msg.message << '\n';
DispatchMessage(&Msg);
}
KillTimer(NULL, TimerId);
return 0;
}
Related
I am trying to create a program in which you can execute commands. The output of these commands should be displayed in a GUI. For this I use QT (because I want to get familiar with WinAPI I don't use QProcess). In the current program it is already possible to redirect the output of a command with a handle. Now my question, how is it possible to interrupt the ReadFile if the command expects a user input.
As an example, I want to run the command yarn run from C++.
This returns as output that this command does not exist and asks which command I want to execute instead. At the moment the command aborts there (comparable with CTRL+C) and returns error No command specified. At this point, however, a user input should be possible.
Expected outcome of the program:
The output I get instead:
As you can see in picture 1 yarn asks the user for input. In image 2 there is no question at all. This behaviour is for example possible if you press CTRL+C if the question input shows up.
So how is it possible to make a user input in the gui (for now it would be enough to redirect the value of a variable into the input) and redirect it back to the process. The process should wait until it gets the input.
Command.h
#ifndef COMMAND_H
#define COMMAND_H
#include <string>
#include <cstdlib>
#include <cstdio>
#include <io.h>
#include <fcntl.h>
#include <stdexcept>
#include <windows.h>
#include <iostream>
#define BUFSIZE 256
class Project;
class Command
{
private:
int exitStatus;
const Project * project;
std::string cmd;
HANDLE g_hChildStd_IN_Rd = nullptr;
HANDLE g_hChildStd_IN_Wr = nullptr;
HANDLE g_hChildStd_OUT_Rd = nullptr;
HANDLE g_hChildStd_OUT_Wr = nullptr;
HANDLE g_hInputFile = nullptr;
void setupWindowsPipes();
void createWindowsError(const std::string &errorText);
void readFromPipe();
public:
Command() = delete;
explicit Command(std::string cmd, const Project *project);
void exec();
};
#endif // COMMAND_H
Command.cpp (the entry point which is called by the gui is exec())
#include "command.h"
#include "project.h"
Command::Command(std::string cmd, const Project *project) : exitStatus(0), project(project), cmd(std::move(cmd)) {}
void Command::createWindowsError(const std::string &errorText) {
DWORD code = GetLastError();
LPSTR lpMsgBuf;
if(code == 0) return;
auto size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &lpMsgBuf,
0, NULL );
std::string msg(lpMsgBuf, size);
LocalFree(lpMsgBuf);
throw std::runtime_error(errorText + "()" + std::to_string(code) + ": " + msg);
}
void Command::setupWindowsPipes(){
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = true;
saAttr.lpSecurityDescriptor = nullptr;
if(!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
createWindowsError("StdOutRd CreatePipe");
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
createWindowsError("StdOut SetHandleInformation");
if(!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
createWindowsError("StdInRd CreatePipe");
if(!SetHandleInformation(g_hChildStd_IN_Rd, HANDLE_FLAG_INHERIT, 0))
createWindowsError("StdIn SetHandleInformation");
}
void Command::readFromPipe() {
DWORD dwRead;
char chBuf[BUFSIZE];
bool bSuccess = false;
for (;;)
{
dwRead = 0;
for(int i = 0;i<BUFSIZE;++i) {
chBuf[i] = '\0';
}
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead <= 0 ) break;
std::cout << chBuf;
}
std::cout << std::endl;
}
void Command::exec() {
std::cout << "CMD to run: " << this->cmd << std::endl;
this->setupWindowsPipes();
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = g_hChildStd_OUT_Wr;
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdInput = g_hChildStd_IN_Rd;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
char* dir = nullptr;
if(this->project != nullptr) {
auto n = this->project->getLocalUrl().size() + 1;
auto nString = this->project->getLocalUrl().replace("/", "\\");
dir = new char[n];
std::strncpy(dir, nString.toStdString().c_str(), n);
}
std::string cmdString = "cmd /c ";
cmdString.append(this->cmd);
char cmdCopy[cmdString.size() + 1];
cmdString.copy(cmdCopy, cmdString.size());
cmdCopy[cmdString.size() + 1] = '\0';
bool rc = CreateProcessA( nullptr,
cmdCopy,
nullptr,
nullptr,
true,
CREATE_NO_WINDOW,
nullptr,
dir,
&si,
&pi);
delete []dir;
if(!rc)
createWindowsError("Failed to create process");
std::cout << "PID: " << pi.dwProcessId << std::endl;
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
readFromPipe();
std::cout << "fin reading pipe" << std::endl;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
It sounds like you have an XY problem, luckily you described X so we can address it.
The issue is not your failure to call WriteFile to store the response into the redirected input pipe. If the program were trying to read input, it would wait.
The issue is that the program is not requesting input at all. It has detected that interactive input is not possible, because it detects a pipe and assumes that a pipe is not interactive. So it doesn't perform the prompt or try to read from standard input at all. You can't provide an answer to a question that the program didn't ask!
(To confirm this is the behavior of the yarn program you are spawning, you can launch it from cmd.exe using a pipe to provide the input. cmd.exe has well-tested buffering logic for redirected input and output handles and you can be sure that any suspected deadlock in your code doesn't affect cmd.exe)
On Unix-like systems, this is solved by redirecting to a pseudo-tty (ptty) special file instead of a pipe special file, which causes the isatty() function to return true.
On Windows, this used to be effectively impossible, as the console API, implemented at kernel level, was permanently associated to the console subsystem csrss.exe which only exchanged data with the official Console Host process (owner of console windows).
Now however, Windows API supports pseudo-consoles. You can find a complete introduction on the Microsoft Dev Blog
Windows Command-Line: Introducing the Windows Pseudo Console (ConPTY)
The important function you need (in case that link breaks) is CreatePseudoConsole supported starting with Windows 10 version 1809 (October 2018 update).
When you use CreatePseudoConsole to promote the pipes and then supply this console to CreateProcess (instead of attaching pipes to your subprocess standard I/O streams), the subprocess will detect an interactive console, can use console API functions such as AttachConsole, can open the special filenames CONIN$ etc. And the data comes to you (and from you) instead of being linked to a console window.
There's also a complete sample on GitHub.
That same blog post also discusses the workaround used by "Terminal" and "remote shell" type software prior to the addition of CreatePseudoConsole in Windows 10, namely setting up the subprocess with a detached console, hiding the associated console window, and screen-scraping the console screen buffer.
The saga continues...
I've searched the web, i've searched on StackOverflow, i found many hope giving answers/solutions, but somehow they have all failed (up)on me (including the ones related to ShellExecute(Ex) ).
How to hide a (flashing) CMD window (incl. arguments) using CreateProcess??
I basically want to call/execute a set of conditional/native cmd.exe commands (i.e. FOR /F, and ||), but also an external command FIND(STR).exe. And this, without showing a (flashing) CMD window.
But even hiding something as simple as "cmd.exe /C ECHO ...flashing window is bad..." seems impossible to do.
The code i've tried (including many variations related to the dwFlags and wShowWindow flags
#include <windows.h>
int main()
{
char cmdline[] = "cmd.exe /c ECHO ...flashing window is bad...";
PROCESS_INFORMATION pi;
STARTUPINFO si;
// memset(&si,0,sizeof(STARTUPINFO));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
// si.dwFlags = STARTF_USESTDHANDLES;
// si.dwFlags = CREATE_NO_WINDOW;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
// si.wShowWindow = CREATE_NO_WINDOW;
CreateProcess(NULL, (LPSTR) cmdline, NULL, NULL, 0, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// ExitProcess;
return 0;
}
I don't want to rely on external programs i.e. .vbs (Windows Scripting Host) or shortcut tricks, but simply a standalone compiled .exe.
Is this (really) too much to ask, or am i doing it (completely) wrong?
Thanks...
Update: You also seem to confuse CreateProcess flags (its dwCreationFlags argument) with the member of STARTUPINFO structure. These are different flags, CREATE_NO_WINDOW should not be in STARTUPINFO.
You have to pass the CREATE_NO_WINDOW flag, then the console window won't show. Originally I've answered that you have to redirect the standard handles which is not correct (but still highly recommanded).
Set STARTF_USESTDHANDLES and fill in appropriate handles. If you are interested in the output of the process, create pipes, otherwise you can just open nul an pass that.
Try Using ProcessBuilder. Here is an example of some code that I have that seems to work just fine. In my code below, the shellScript is a StringBuilder that I am dynamically creating that contains the command and it's parameters that I want to execute.
String[] scriptArray = shellScript.toString().split(" ");
ProcessBuilder builder = new ProcessBuilder(scriptArray);
File outputFile = new File("/logs/AgentOutputLog.txt");
File errorFile = new File("/logs/AgentErrorLog.txt");
builder.redirectOutput(outputFile);
builder.redirectError(errorFile);
Process process = builder.start();
int errCode = process.waitFor();
//errCode = 0 means online
if(errCode == 0){
success = true;
break;
//errCode = 1 means offline
} else if (errCode == 1){
success = false;
break;
}
I try to redirect in windows the cmd.exe stdout&stdin (with CreateProcess()). It works fine as long as I run simple commands or open GUI apps, but if I try running a software like python, it does not give me it's output anymore (nor getting the input through stdin).
Code example:
#include <windows.h>
#include <iostream>
#include <string>
#include <thread>
using namespace std;
HANDLE child_input_read;
HANDLE child_input_write;
HANDLE child_output_read;
HANDLE child_output_write;
void writeToPipe()
{
while (true)
{
DWORD bytes_written;
string msg;
getline(cin, msg);
msg += '\n';
BOOL success = WriteFile(child_input_write, msg.c_str(), msg.size(), &bytes_written, NULL);
if (!success)
{
break;
}
}
}
void readFromPipe()
{
while (true)
{
DWORD bytes_read;
char buffer[512];
BOOL success = ReadFile(child_output_read, buffer, sizeof(buffer)-1, &bytes_read, NULL);
buffer[bytes_read] = 0;
if (!success)
{
break;
}
cout << buffer;
}
}
void createCmdProcess()
{
PROCESS_INFORMATION process_info;
STARTUPINFO startup_info;
SECURITY_ATTRIBUTES security_attributes;
// Set the security attributes for the pipe handles created
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attributes.bInheritHandle = TRUE;
security_attributes.lpSecurityDescriptor = NULL;
CreatePipe(&child_output_read, &child_output_write, &security_attributes, 0);
CreatePipe(&child_input_read, &child_input_write, &security_attributes, 0);
// Create the child process
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
startup_info.hStdInput = child_input_read;
startup_info.hStdOutput = child_output_write;
startup_info.hStdError = child_output_write;
startup_info.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info);
}
int main()
{
createCmdProcess();
thread t(writeToPipe);
thread t2(readFromPipe);
t.join();
t2.join();
system("pause");
}
It's not a bug, it's a feature. :-)
From Python Setup & Usage, section 1.1.1, Interface options (emphasis added):
The interpreter interface resembles that of the UNIX shell, but provides some additional methods of invocation:
When called with standard input connected to a tty device, it prompts for commands and executes them until an EOF (an end-of-file character, you can produce that with Ctrl-D on UNIX or Ctrl-Z, Enter on Windows) is read.
When called with a file name argument or with a file as standard input, it reads and executes a script from that file.
A pipe is neither a file nor a tty device, but it looks like a file as far as the C standard library (and hence Python) is concerned. So the second behaviour kicks in, and Python attempts to read to the end of file. Since we never close our end of the pipe, that never happens.
This behaviour isn't particularly sensible in my opinion (or at least not in Windows) and you could file a bug if you wanted. I would guess that such a proposal would be rejected, since any change would break backwards compatibility, but I could be wrong.
You can work around the problem by providing the -i option on the command line:
python -i
That makes Python enter interactive mode, despite the fact that stdin isn't a terminal.
Unfortunately, there is no known way to make a pipe that looks like a terminal on Windows.
I will use some CMD commands in my program and these commands might throw some exceptions. And as you know, when an exception accours, CMD writes its own error message the screen. But, I want to write my own error message.
My question is this: Is there a way to block CMD messages and write only my own error messages?
P.S. This is not a complex program. It executes CMD commands using System().
Example:
Let's say, the user can rename and copy any files in the program. As you know, if the user does not enter file's path properly, an error message is showed on the screen. And I want that this error message never appears on the screen. Only my own error message is showed.
Thank you!
It depends on your platform and the commands you are going to use. The usage of system() for calling console commands is by the way strongly discouraged by most people (it's way to heavy for most purposes).
I would suggest to you using CreateProcess() with the CREATE_NO_WINDOW flag and waiting for the process to exit with a call to WaitForSingleObject() and GetExitCodeProcess().
This approach utilizes the fact, that most CMD command are executables, located somewhere in C:/Windows/....
/*
* Executes a program and returns it's exit code.
*
* TODO: Error checking should be added for
* CreateProcess()
* WaitForSingleObject()
* GetExitCodeProcess()
*/
DWORD ExecCmdLine(TCHAR const* cmdline)
{
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
::CreateProcess(NULL, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
::CloseHandle(pi.Thread);
::WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitcode;
::GetExitCodeProcess(pi.hProcess, &exitcode);
::CloseHandle(pi.hProcess);
return exitcode;
}
If you want to retrieve the output of the command you could also provide hStdOutput, hStdError in the STARTUPINFO structure and set STARTF_USESTDHANDLES in STARTUPINFO.dwFlags.
You can even do other things in your own program while the command is executing (especially as you mentioned file copy). This one is done the C++ way:
/*
* TODO: Error checking should be added for
* CreateProcess()
* WaitForSingleObject()
* GetExitCodeProcess()
*/
class AsyncCmd
{
public:
AsyncCmd(std::string const& cmdline)
: cmdline(cmdline),
processHandle(NULL)
{
}
~AsyncCmd()
{
if (this->processHandle != NULL)
::CloseHandle(this->processHandle);
}
// Starts the execution of the commandline.
void Start(HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE), HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE))
{
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = hOut;
si.hStdError = hErr;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));
::CreateProcess(NULL, this->cmdline.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
::CloseHandle(pi.hThread);
this->processHandle = pi.hProcess;
}
// Blocks until execution is finished and returns the exit code.
DWORD ExitCode()
{
::WaitForSingleObject(this->processHandle, INFINITE);
DWORD exitcode;
::GetExitCodeProcess(this->processHandle, &exitcode);
return exitcode;
}
private:
AsyncCmd(AsyncCmd const&);
AsyncCmd& operator=(AsyncCmd const&);
std::string cmdline;
HANDLE processHandle;
}
To rephrase what's already been said:
Q: Can you somehow intercept an error thrown by a command you've invoked via "system()"?
A: No. For many reasons.
But you can redirect the textual error message that's written by the command line program:
Redirecting "stderr" is relatively easy. "GetStdHandle(STD_ERROR_HANDLE)" is one way. Redirecting to "> :err" is another.
Unfortunately, not all programs are nice enough to write error messages to "stderr".
Many write everything to "stdout".
In the latter case, you'd have to figure out 1) that an error actually occurred, and 2) figure out how to separate the parts of the text input that are "standard output", vs those parts that are "error text".
PS:
An alternative API to "system()" is "popen()".
One brute force way to do it would be to pipe the output of the CMD i.e. yourCommand > file.txt to a file and then read the file contents to determine if there was an exception.
Adding to what yourmt wrote, I'd like to point out that exceptions won't bleed through across process boundaries. So what you are tackling here is the output (both stderr and stdout) of the executed program (and shell) plus its exit code (in case this provides any useful information). This is mostly so you understand that this is literally not about exception handling as your title implies.
That means you can set hStdOutput and hStdError when using CreateProcess/CreateProcessEx to use pipes that you control and where you "catch" the output and then replace it with your own before outputting it to the console. In case you want to only replace stderr you can also do that.
You can use
#include "Exception.h"
class MyClass
{
public:
class Error : public Exception { };
MyClass();
~MyClass();
void myFunction() throw(Error);
}
...
catch (MyClass::Error & error)
{
cout << error.full_message << endl;
return;
}
and
class Exception
{
public:
std::string message;
std::string full_message;
virtual char * info() { return ""; };
void setMessage(std::string msg)
{
message = msg;
if (*info() == 0) { full_message = msg; }
else { full_message = MakeString() << info() << " " << msg; }
}
};
// template function ///////////////////////////////////////////
template <class errType>
void Throw(const std::string msg=std::string(""))
{
errType err;
err.setMessage(msg);
throw(err);
}
I've been searching for an answer regarding this issue for a few days now, I hope you guys will be able to assist me. (I've searched and found some solutions, but each has its own issue...).
Here is the thing:
I'm writing an automation at work, which is responsible for launching an external ".exe" file of a code written by my colleagues. As those programs they write go to customers, I'm not allowed to make any modification to their code. Those programs, once launched, are waiting for specific key strokes, and prints a message when a legal key stroke has been received.
My goal is this:
To write a program which will execute the external program, send it key strokes, and receive the output from their stdout.
So far, I have been able to run the program from my program (using ShellExecute), and simulate some sort of keyboard listener (using SendMessage) to the other program. I can see that it works - I can see the output in the tested program's console.
I'm trying to fetch the messages printed on the tested program's shell in real-time (and just get a bulk of data when the program terminates) so that I could analyse it when it occurs.
Those I've tried:
Writing an external batch file with inline output redirection to a text file.
Using freopen.
Redirecting the output while exectuing "ShellExecute".
You use handles for stdin, stdout, and stderr. Create process with CreateProcess function to get that handles.
Sample code - incomplete for your case, but good example of how to do it:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
/*for test.exe
#include <iostream>
#include <string> */
void _tmain( int argc, TCHAR *argv[] )
{
/*for test.exe
std::cout << "test output" << std::endl;
for (;;)
{
std::string line;
std::getline(std::cin, line);
std::cout << "line: " << line << std::endl;
}
return;*/
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
if( !CreateProcess( NULL, // No module name (use command line)
"test.exe", // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
{
printf( "CreateProcess failed (%d)\n", GetLastError() );
return;
}
/* HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;*/
HANDLE me_hStdInput = GetStdHandle(STD_INPUT_HANDLE);
HANDLE me_hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE proc_hStdInput = si.hStdInput;
HANDLE proc_hStdOutput = si.hStdOutput;
char buff[64];
DWORD chars;
while (!ReadConsole(me_hStdInput, buff, sizeof(buff), &chars, NULL))
{
for (DWORD written = 0, writtenThisTime; written < chars; written += writtenThisTime)
if (!WriteConsole(proc_hStdOutput, buff + written, chars - written, &writtenThisTime, NULL))
{
//handle error - TODO
}
}
//possibly handle error for ReadConsole - TODO
// Wait until child process exits.
//WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}