CMD Exception Handling C/C++ - c++

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);
}

Related

Run command in c++ via CreateProcess and handle its input and output

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.

Detecting a process crash in C++/Win32

I'm working on a software that contains 2 programs : the Qt Main exe + an OpenGL Game exe
We use always the Qt Main exe at the first. When we click on the button "start game", we execute the OpenGL Game exe. No probleme to do that.
The problem is that sometime we have a crash in the OpenGL Game exe and we want to send a crash report containing the log to our compagny crash-report mail.
I found a function (registerwaitforsingleobject) in Win32 API but I don't know if the process has crashed or not :
https://learn.microsoft.com/en-gb/windows/desktop/api/winbase/nf-winbase-registerwaitforsingleobject
I would like to use only the win32 api (WinXP-WinVist to Win10)
Thanks in advance
I found the solution to my problem : I used GetExitCodeProcess function from the Win32 API (https://learn.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess).
This is the example code :
int main(int argc, char** arv)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
const char * prgm = "CrashedProcess.exe";
LPSTR prgmLpstr = const_cast<LPSTR>(prgm);
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
prgmLpstr, // 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 -1;
}
// Wait until child process exits.
auto ret = WaitForSingleObject(pi.hProcess, INFINITE);
printf("WaitForSingleObject ret = %x\n", ret);
if (ret == WAIT_OBJECT_0)
{
printf("WaitForSingleObject ret ret == WAIT_OBJECT_0\n");
}
BOOL b = FALSE;
DWORD n = 0;
b = GetExitCodeProcess(pi.hProcess, &n);
if (n == 0xC0000005)
{
printf("Process Crashed !!!\n");
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
printf("WaitForSingleObject end\n");
return 0;
}
CrashedProcess.exe source code :
int main()
{
int * ptr = nullptr;
*ptr = 123;
return 0;
}
Use a ready-made solution such as CrashRpt http://crashrpt.sourceforge.net/. It will install a proper handler and report/submit what you need when exceptions are thrown.
You can use Interprocess Communications with a pipe.
Send a message at time laps (for example, 1 second). If there's no response you can be almost sure that the other program is crashed.
Another approach, less elegant, is testing for a log file, modified every x-seconds, with a value meaning "I'm still alive, not crashed".
Assuming the program you care about has a normal Windows event loop, the more or less standard way to check that it's processing messages in a reasonably timely fashion is to use SendMessageTimeout to send it a WM_NULL message.
The program won't do anything in response to the WM_NULL, but SendMessageTimeout will time out if the message stays in its message queue for too long (where you pick what "too long" means when you make the call). So you send it, and then check whether it timed out or not.
But note: this only applies to programs that does process Windows messages. If it doesn't have a message loop, it'll always timeout. Oh, and a program is multi-threaded, one thread can "crash" (e.g., go into an infinite loop) while others continue to operate normally, so it's not always easy to even define what "crashed" means.

How do I hide a command window and display all potential errors in an EditBox control?

I am creating simple application in Borland C++ Builder.
My code looks like this:
void __fastcall TForm1::Button3Click(TObject *Sender)
{
system("java -jar decompile.jar -o Source/ file.jar");
}
Now I want to hide the command window and show all potential errors in an EditBox control. The EditBox control should remain empty if there are no errors.
Edit1->Text= "ERROR";
use TMemo instead of TEdit box for log
It have multiple lines and support scrollbars. It is far better for logs.
see how to redirect commnd promt output to file
I do not use JAVA but you can try:
system("java -jar decompile.jar -o Source/ file.jar > errorlog.txt");
or:
system("java -jar decompile.jar -o Source/ file.jar >> errorlog.txt");
You can also include the application path
AnsiString exepath=ExtractFilePath(Application->ExeName);
So you save the file to known location nstead of actual path that could change easily during runtime...
see How can I run a windows batch file but hide the command window?
So you need to use CreateProcess with java.exe or command.com directly. I never done the hiding thing so follow the answer in the linked Q/A. If you do not know how to use CreateProcess (it can be overwhelming for beginers) then this is how I use it (it does not hide just starts an exe...)
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES attr0,attr1;
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb=sizeof(si);
attr0.nLength=sizeof(SECURITY_ATTRIBUTES);
attr0.bInheritHandle=TRUE;
attr0.lpSecurityDescriptor=NULL;
attr1=attr0;
CreateProcess(NULL,"some_app.exe > logfile.txt",&attr0,&attr1,TRUE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
And you can use:
TerminateProcess(pi.hProcess,0);
To force terminate the app ....
Now when I put all together I got this:
AnsiString s,logfile;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES attr0,attr1;
ZeroMemory(&si,sizeof(si));
ZeroMemory(&pi,sizeof(pi));
si.cb=sizeof(si);
// hide the process
si.wShowWindow=SW_HIDE;
si.dwFlags=STARTF_USESHOWWINDOW;
attr0.nLength=sizeof(SECURITY_ATTRIBUTES);
attr0.bInheritHandle=TRUE;
attr0.lpSecurityDescriptor=NULL;
attr1=attr0;
// Application local path log filename
logfile=ExtractFilePath(Application->ExeName)+"logfile.txt";
DeleteFileA(logfile); // delete old log just to be sure
// command line string to run (instead of "dir" use "java -jar decompile.jar -o Source/ file.jar")
s="cmd /c dir > \""+logfile+"\"";
CreateProcess(NULL,s.c_str(),&attr0,&attr1,TRUE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
// wait for execution with some timeout...
for (int i=0;i<100;i++)
{
if (FileExists(logfile)) break;
Sleep(100);
}
// copy the log into TMemo mm_log ...
if (FileExists(logfile)) mm_log->Lines->LoadFromFile(logfile); else mm_log->Text="No log file found";
Where mm_log is memo where I copy the log file. This example just run the dir command to show directory info... so instead use your JAVA ... as I suggested in rem. If you have older OS then instead of cmd use command. you can also use FileExists to determine which one is it ...
You need to use the Win32 CreateProcess() function instead of the C system() funtion. CreateProcess() allows you to redirect STDIN, STDOUT, and STDERR of the launched process.
Creating a Child Process with Redirected Input and Output
You can launch java.exe directly with the desired input parameters, redirecting its output using anonymous pipes created with CreatePipe(), and then read output from those pipe using ReadFile() until the process terminates. You can parse and display the output however you want.
As for hiding the command window, when calling CreateProcess(), you can either:
specify the CREATE_NO_WINDOW flag in the dwCreationFlags parameter of CreateProcess().
in the STARTUPINFO structure passed to the lpStartupInfo parameter of CreateProcess(), include the STARTF_USESHOWWINDOW flag in the STARTUPINFO::dwFlags field, and set the STARTUPINFO::wShowWindow field to SW_HIDE.
Try something like this:
HANDLE hStdErrRd = NULL;
HANDLE hStdErrWr = NULL;
SECURITY_ATTRIBUTES saAttr = {0};
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hStdErrRd, &hStdErrWr, &saAttr, 0)) {
// error handling
}
if (!SetHandleInformation(hStdErrRd, HANDLE_FLAG_INHERIT, 0)) {
// error handling
}
String CmdLine = "\"C:\\path to\\java.exe\" -jar \"C:\\path to\\decompile.jar\" -o Source/ \"C:\\path to\\file.jar\"";
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = hStdErrWr;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi = {0};
if (CreateProcess(NULL, CmdLine.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// must close our handle to the write-side of the pipe so the pipe will close
// when the child process terminates so ReadFile() will know to stop expecting data...
CloseHandle(hStdErrWr);
AnsiString output;
char buffer[1024];
DWORD dwBytesRead;
while (ReadFile(hStdErrRd, buffer, sizeof(buffer), &dwBytesRead, NULL))
{
if (dwNumRead == 0)
break;
output += AnsiString(buffer, dwBytesRead);
do
{
int pos = output.Pos("\n");
if (pos == 0)
break;
Memo1->Lines->Add(output.SubString(1, pos-1).TrimRight());
output.Delete(1, pos);
}
while (!output.IsEmpty());
}
if (!output.IsEmpty())
Memo1->Lines->Add(output.TrimRight());
}
else
{
// error handling
CloseHandle(hStdErrWr);
}
CloseHandle(hStdErrRd);

Redirecting stdout output in cpp

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 );
}

open cmd and read and write into it

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;
}