The application I'm working on needs to execute commands. Commands can be console commands or 'GUI applications' (like notepad).
I need to get the return code in both cases, and in the case of console commands I also need to catch the output from stdin and stderr.
In order to implement this feature, I based my code on the stack overflow question 'How to execute a command and get output of command within C++ using POSIX?'.
My code:
int ExecuteCmdEx(const char* cmd, std::string &result)
{
char buffer[128];
int retCode = -1; // -1 if error ocurs.
std::string command(cmd);
command.append(" 2>&1"); // also redirect stderr to stdout
result = "";
FILE* pipe = _popen(command.c_str(), "r");
if (pipe != NULL) {
try {
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
}
catch (...) {
retCode = _pclose(pipe);
throw;
}
retCode = _pclose(pipe);
}
return retCode;
}
It works perfectly with console applications, but in the case of 'GUI applications' it doesn't work as expected...
With 'GUI applications', code stops on while (!feof(pipe)) expecting to get something from pipe.
I understand that 'GUI applications' like notepad don't finish until someone interacts with them (user closes the app, kills the process, etc.),
but when I launch console applications from Windows Console, prompt comes back immediately.
I would like to obtain the same behavior from 'GUI applications'...
One possible solution would be to add the isGui variable indicating when the code should read from the pipe, but I rejected this option, as I don't want to indicate if it is a 'GUI application' or not.
Well you don't have to indicate isGui yourself but detect it by checking the subsystem of the executable (windows/console) prior to executing the command, and in case of windows skip waiting on the redirected pipes.
For example, using SHGetFileInfo with the SHGFI_EXETYPE flag:
bool isGuiApplication(const std::string& command)
{
auto it = command.find_first_of(" \t");
const std::string& executable = (it == std::string::npos ? command : command.substr(0, it));
DWORD_PTR exetype = SHGetFileInfo(executable.c_str(), 0, nullptr, 0, SHGFI_EXETYPE);
if (!exetype) {
cerr << "Executable check failed\n";
}
return ((uintptr_t)exetype & 0xffff0000);
}
Then later in the code...
if (isGuiApplication(command)) {
cout << "GUI application\n";
system(command.c_str()); // don't wait on stdin
}
else {
cout << "Console application\n";
. . .
// _popen and stuff
}
The context
I wrote a logger printing messages for the user. Messages with level "debug", "info" or "warning" are printed in std::cout and messages with level "error" or "system_error" are printed in std::cerr. My program is not multi-threaded. I work under Linux openSUSE 12.3 with gcc 4.7.2 and CMake 3.1.0.
My problem
I discovered that sometimes, when an error message (printed in std::cerr) follows a long information message (printed in std::cout) and when the output is redirected to the file LastTest.log by CTest, the error message appears in the information message (look at the example below). I don't well understand this behaviour but I suppose a writing thread is launched for std::cout, then the code continues and another writing thread is lauched for std::cerr without waiting the first one is terminated.
Is it possible to avoid that without using only std::cout ?
I don't have the problem in the terminal. It only happens when CTest redirect the output to the LastTest.log file.
Note that my buffer is flushed. It's not a problem of std::endl coming after the call to std::cerr !
Example
Expected behaviour :
[ 12:06:51.497 TRACE ] Start test
[ 12:06:52.837 WARNING ] This
is
a
very
long
warning
message...
[ 12:06:52.837 ERROR ] AT LINE 49 : 7
[ 12:06:52.841 ERROR ] AT LINE 71 : 506
[ 12:06:52.841 TRACE ] End of test
What happens :
[ 12:06:51.497 TRACE ] Start test
[ 12:06:52.837 WARNING ] This
is
a
very
long
[ 12:06:52.837 ERROR ] AT LINE 49 : 7
warning
message...
[ 12:06:52.841 ERROR ] AT LINE 71 : 506
[ 12:06:52.841 TRACE ] End of test
How I call my logger
Here is an example of how I call std::cout or std::cerr with my logger.
I call the logger with maccros like that :
#define LOG_DEBUG(X) {if(Log::debug_is_active()){std::ostringstream o;o<<X;Log::debug(o.str());}}
#define LOG_ERROR(X) {if(Log::error_is_active()){std::ostringstream o;o<<X;Log::error(o.str());}}
//...
LOG_DEBUG("This" << std::endl << "is" << std::endl << "a message");
LOG_ERROR("at line " << __LINE__ << " : " << err_id);
with
void Log::debug(const std::string& msg)
{
Log::write_if_active(Log::DEBUG, msg);
}
void Log::error(const std::string& msg)
{
Log::write_if_active(Log::ERROR, msg);
}
//...
void Log::write_if_active(unsigned short int state, const std::string& msg)
{
Instant now;
now.setCurrentTime();
std::vector<std::string> lines;
for(std::size_t k = 0; k < msg.size();)
{
std::size_t next_endl = msg.find('\n', k);
if(next_endl == std::string::npos)
next_endl = msg.size();
lines.push_back(msg.substr(k, next_endl - k));
k = next_endl + 1;
}
boost::mutex::scoped_lock lock(Log::mutex);
for(unsigned long int i = 0; i < Log::chanels.size(); ++i)
if(Log::chanels[i])
if(Log::chanels[i]->flags & state)
Log::chanels[i]->write(state, now, lines);
}
Here, the log chanel is the object dedicated to terminal output, the write function is :
void Log::StdOut::write(unsigned short int state, const Instant& t, const std::vector<std::string>& lines)
{
assert(lines.size() > 0 && "PRE: empty lines");
std::string prefix = "[ ";
if(this->withDate || this->withTime)
{
std::string pattern = "";
if(this->withDate)
pattern += "%Y-%m-%d ";
if(this->withTime)
pattern += "%H:%M:%S.%Z ";
prefix += t.toString(pattern);
}
std::ostream* out = 0;
if(state == Log::TRACE)
{
prefix += " TRACE";
out = &std::cout;
}
else if(state == Log::DEBUG)
{
prefix += " DEBUG";
out = &std::cout;
}
else if(state == Log::INFO)
{
prefix += " INFO";
out = &std::cout;
}
else if(state == Log::WARNING)
{
prefix += "WARNING";
out = &std::cout;
}
else if(state == Log::ERROR)
{
prefix += " ERROR";
out = &std::cerr;
}
else if(state == Log::SYS_ERROR)
{
prefix += "SYERROR";
out = &std::cerr;
}
else
assert(false && "PRE: Invalid Log state");
prefix += " ] ";
(*out) << prefix << lines[0] << "\n";
prefix = std::string(prefix.size(), ' ');
for(unsigned long int i = 1; i < lines.size(); ++i)
(*out) << prefix << lines[i] << "\n";
out->flush();
}
You can see that my buffer is flushed when the log instruction is executed.
I've seen this behavior before in a few forms. The central idea is to remember that std::cout and std::cerr write to two completely separate streams, so any time you see the output from both in the same place, it's because of some mechanism outside of your program that merges the two streams.
Sometimes, I see this simply due to a mistake, such as
myprogram > logfile &
tail -f logfile
which is watching the logfile as it's written, but forgot to redirect stderr to the logfile too, so writes to stdout go through at least two additional layers of buffering inside tail before getting displayed, but writes to stderr go directly to the tty, and so can get mixed in.
Other examples I've seen involve an external process merging streams. I don't know anything about CTest but maybe it's doing this. Such processes are under no obligation to sort lines by the exact time at which you originally wrote them to the stream, and probably don't have access to that information even if they wanted to!
You really only have two choices here:
Write both logs to the same stream — e.g. use std::clog instead of std::cout or std::cout instead of std::cerr; or launch the program with myprogram 2>&1 or similar
Make sure the merging is done by a process that is actually aware of what it's merging, and takes care to do it appropriately. This works better if you're communicating by passing around packets containing logging events rather than writing the formatted log messages themselves.
Answering my own question
Finally, it comes from a bug in CMake.
CTest is not able to manage the order of the two buffers, and then doesn't care about the exact order of the output.
It will be resolved in CMake >= 3.4.
I'm not a C++ expert, but this might help...
I believe that the issue you are seeing here, when redirecting to a file, is caused by the cstdio library trying to be smart. My understanding is that on Linux, the C++ iostreams ultimately send their output to the cstdio library.
At startup, the cstdio library detects if you are sending the output to a terminal or to a file. If the output is going to the terminal then stdio is line buffered. If output is going to a file then stdio becomes block buffered.
Output to stderr is never buffered, so will be sent immediately.
For solutions you could possibly try using fflush on stdout, or you could look into using the setvbuf function on stdout to force line buffered output (or even unbuffered output if you like). Something like this should force stdout to be line buffered setvbuf(stdout, NULL, _IOLBF, 0).
After a few days of searching the net on how exactly I can go about printing an arbitrary string to an arbitrary printer on windows, I finally came up with this code.
LPBYTE pPrinterEnum;
DWORD pcbNeeded, pcbReturned;
PRINTER_INFO_2 *piTwo = NULL;
HDC printer;
EnumPrinters(PRINTER_ENUM_LOCAL,NULL,2,NULL,0,&pcbNeeded,&pcbReturned);
pPrinterEnum = new BYTE[pcbNeeded];
if (!EnumPrinters(PRINTER_ENUM_LOCAL,NULL,2,pPrinterEnum,pcbNeeded,&pcbNeeded,&pcbReturned)) {
qDebug() << "In Print, could not enumerate printers";
} else {
piTwo = ((PRINTER_INFO_2*)pPrinterEnum);
for (int i = 0; i < pcbReturned; i++) {
QString name = QString::fromWCharArray(piTwo[i].pPrinterName);
if (this->m_printer_path == name) {
const WCHAR * driver = L"WINSPOOL\0";
printer = CreateDC(NULL,piTwo[i].pPrinterName,NULL,NULL);
}
}
}
if (printer == 0) {
qDebug() << "No Printer HDC";
return;
} else {
qDebug() << "Printer seems okay!";
}
qDebug() << "Starting Document";
DOCINFO di;
memset( &di, 0, sizeof( di ) );
di.cbSize = sizeof( di );
WCHAR * text = new WCHAR[ba.length()];
QString(ba).toWCharArray(text);
StartDoc(printer,&di);
qDebug() << "Writing text";
TextOut(printer,0, 0, text, ba.length());
qDebug() << "Text Written";
EndPage(printer);
qDebug() << "Page ended";
DeleteDC(printer);
qDebug() << "DC Deleted";
Some basic caveats:
1) I cannot use QPrinter. I need to write raw text, no postscript.
2) I do not know the name of the printer until the user sets it, and I do not know the size of the string to print until the user creates it.
Additional information:
a) The printer works, I can print from Notepad, Chrome, just about everything to the printer that I want.
b) I am willing to implement just about any hack. Ones like write it to a text file and issue the copy command don't seem to work, that is, I get a failed to initialize device error.
This works:
notepad /P Documents/test_print.txt
This does not work:
copy Documents\test_print.txt /D:EPSON_TM_T20
copy Documents\test_print.txt /D \MYCOMPUTER\epson_tm_t20 (leads to access denied, printer is shared)
print Documents\test_print.txt (Unable to initialize device)
I have tried just about every recommended way to print a text file from the command line, just doesn't work. I have installed, reinstalled driver, added printer, mucked with ports and done it all again.
Obviously there is something simple about windows printing that I am missing due to inexperience.
What I want to accomplish is:
1) Best Scenario( Directly write text to the printer)
2) Second best scenario (Write text to a file, then execute some program to print it for me) Notepad adds an annoying amount of space to the bottom of the printout wasting paper.
Since the program is for end users, I have to find a way to do this automagically for them, so I can't expect them to click checkbox a in tab 36 after running command obscure_configuration from a powershell.
Any help would be greatly appreciated.
/Jason
UPDATE
This is the working code, before I go through an spruce it up a bit, which prints the contents of a QByteArray to a thermal printer.
qDebug() << "Executing windows code";
BOOL bStatus = FALSE;
DOC_INFO_1 DocInfo;
DWORD dwJob = 0L;
DWORD dwBytesWritten = 0L;
HANDLE hPrinter;
wchar_t * name = new wchar_t[this->m_printer_path.length()+1];
this->m_printer_path.toWCharArray(name);
name[this->m_printer_path.length() + 1] = 0;
qDebug() << "opening printer";
bStatus = OpenPrinter(name,&hPrinter, NULL);
if (bStatus) {
qDebug() << "Printer opened";
DocInfo.pDocName = L"My Document";
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = L"RAW";
dwJob = StartDocPrinter( hPrinter, 1, (LPBYTE)&DocInfo );
if (dwJob > 0) {
qDebug() << "Job is set.";
bStatus = StartPagePrinter(hPrinter);
if (bStatus) {
qDebug() << "Writing text to printer";
bStatus = WritePrinter(hPrinter,ba.data(),ba.length(),&dwBytesWritten);
EndPagePrinter(hPrinter);
} else {
qDebug() << "could not start printer";
}
EndDocPrinter(hPrinter);
qDebug() << "closing doc";
} else {
qDebug() << "Couldn't create job";
}
ClosePrinter(hPrinter);
qDebug() << "closing printer";
} else {
qDebug() << "Could not open printer";
}
if (dwBytesWritten != ba.length()) {
qDebug() << "Wrong number of bytes";
} else {
qDebug() << "bytes written is correct " << QString::number(ba.length()) ;
}
Note: I do owe an apology to Skizz, what he wrote was actually helpful in debugging the fundamental issue. The characters in the QByteArray are preformatted specifically for the printer, the problem is, they contain several NULL bytes. When trying to send them to the printer, this causes TextOut to truncate the text, only printing the first few lines. Using WritePrinter, as suggested in the answer ignores null bytes and accepts a void * and a length, and just puts it all there.
Further, his response recommending the use of PrintDlg did work to fectch the correct printer HDC, the issus is that, the user first chooses a printer once, and then doesn't need to choose it each time they print, because they will be printing alot (It's a Point of Sale).
The problem with getting the printer HDC from the string name was due to not adding the all important NULL byte to wchar_* which was solved this way:
wchar_t * name = new wchar_t[this->m_printer_path.length()+1];
this->m_printer_path.toWCharArray(name);
name[this->m_printer_path.length() + 1] = 0;
In the above, m_printer_path is a string representation of the name of the printer taken from Print Manager.
Because the string has all the formatting necessary for the printer, there's no need to worry about new lines, or any formatting.
All three answers to this question were actually very helpful in implementing the final working solution, and I have voted up each answer, and I appreciate the time each person took in responding.
Most modern printers don't perform any form of layout processing of the data they are given. Thus, sending a sequence of characters to the printer would, at best, just print a line of text running off the side of the page in some default font. Carriage returns may work too.
What modern printers usually do is print pages using preprocessed data that the printer understands and defines what to print where and how to print it. All this preprocessing is done on the host PC and the results sent to the printer. This is why you usually install printer drivers - these drivers take the user data (whether it's a simple text file or a DTP page) and converts it into a language the printer understands.
The upshot of this is that sending raw text to the printer probably won't work.
Then you've got the problem of having multiple printers with different properties and languages.
So, in Windows, all this is abstracted into the printer device context object. This has the same interface as a graphics device context but you create it differently.
The Win32 API has a common dialog to let the user choose the printer. Use the PrintDlgEx function to allow the user to choose a printer. Then use the returned DC to draw text to the page.
There are a couple of MSDN articles describing how to send raw data (printer control codes, etc.) to a printer.
How To: Send Data Directly to a GDI Printer
How To: Send Data Directly to an XPS Printer
You have the right idea (though you should have StartPage and EndDoc calls to match up). The problem is that TextOut draws only a line of text. It won't break long strings into multiple lines, etc. You need to do that (or find code to do it).
If you know that the text will always fit on a single page, you could probably replace your TextOut with a DrawTextEx call, which can do basic line breaking, tab expansion, etc.
Why not try QPrint.. it prints raw text using a Generic Text Only driver
QString prn("^XA^FO121,41^A0N,19,15^FDABC DEFGHIJKLMNOPQRSTUVWXYZ^FS^XZ");
QPrinter printer(QPrinterInfo::defaultPrinter()); //the default printer is "Generic / Text Only"
QTextDocument doc(prn);
doc.print(&printer);
MTry the following code in C++:
#include<fstream>
Class PrinterDriver{
Private:
fstream print("PRN")
Public:
Void Print(char a[]){
print >>a;}
Char GetPrinterStatus[](){
char c[];
print<<c;
return c;}};
understand it(key)
First time poster long time reader.
I've been playing round with reading in data from a bluetooth GPS unit.
I can connect to it using hyperterm and see the data
The following log is from the hyperterm
$GPRMC,195307.109,A,5208.2241,N,00027.7689,W,000.0,345.8,310712,,,A*7E
$GPVTG,345.8,T,,M,000.0,N,000.0,K,A*07
$GPGGA,195308.109,5208.2242,N,00027.7688,W,1,04,2.1,58.9,M,47.3,M,,0000*7E
$GPGSA,A,3,19,03,11,22,,,,,,,,,5.5,2.1,5.0*3F
$GPRMC,195308.109,A,5208.2242,N,00027.7688,W,000.0,345.8,310712,,,A*73
$GPVTG,345.8,T,,M,000.0,N,000.0,K,A*07
$GPGGA,195309.109,5208.2243,N,00027.7688,W,1,04,2.1,58.9,M,47.3,M,,0000*7E
END LOG
The following log is from my C++ program
$GPGSV,3,3,12,14,20,105,16,28,18,323,,08,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š945.109,A,5208.2386,N,00027.7592,W,000.0,169.5,8,323,,08,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š310712,,,A*70
$GPVTG,169.5,T,,M,000.0,N,000.0,K,A*06
8,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š310712,,,A*70
$GPVTG,169.5,T,,M,000.0,N,000.0,K,A*06
8,07,288,,16,01,178,*7A
$GPRMC,195,3,2ÿþÿÿÿL.š$GPGGA,195946.109,5208.2386,N,00027.7592,W,1.0,K,A*06
8,07,288,,16,01,178,*7A
END LOG
THE PROBLEM
I've left the line feeds as they come, the C++ output has extra line feeds, not sure why?
The C++ log also has some funky chars...?
The Code
for (int n=0;n<100;n++) {
char INBUFFER[100];
cv::waitKey(1000);
bStatus = ReadFile(comport, // Handle
&INBUFFER, // Incoming data
100, // Number of bytes to read
&bytes_read, // Number of bytes read
NULL);
cout << "bStatus " << bStatus << endl;
if (bStatus != 0)
{
// error processing code goes here
}
LogFile << INBUFFER;
}
I'm using settings...
comSettings.BaudRate = 2400;
comSettings.StopBits = ONESTOPBIT;
comSettings.ByteSize = 8;
comSettings.Parity = NOPARITY;
comSettings.fParity = FALSE;
...which as far as I can tell are the same as the settings used by hyperterm.
Any hints on what I'm doing wrong?
cheers!
UPDATE
So after updating to use bytes_read and account for the extra LF at the end of NMEA data I now have...
if (bytes_read!=0) {
for (int i=0; i < bytes_read; i++) {
LogFile << INBUFFER[i];
}
}
Which appears to have fixed things!
$GPGGA,215057.026,5208.2189,N,00027.7349,W,1,04,6.8,244.6,M,47.3,M,,0000*41
$GPGSA,A,3,32,11,01,19,,,,,,,,,9.7,6.8,7.0*3D
$GPRMC,215057.026,A,5208.2189,N,00027.7349,W,002.0,208.7,310712,,,A*74
$GPVTG,208.7,T,,M,002.0,N,003.8,K,A*09
$GPGGA,215058.026,5208.2166,N,00027.7333,W,1,04,6.8,243.1,M,47.3,M,,0000*42
Thanks folks, your help was much appreciated.
You have a bytes_read var, but you don't do anything with it? Seems to me that you're dumping the entire INBUFFER to the file, no matter how many/few bytes are actually loaded into it?