Generically getting the date and time - datetime

I know that this question may have been asked before, but this is a follow up of one of my previous questions found here. What makes this unique from the other questions is that I'm looking for something that is current and relevant to my compiler: Visual Studio 2017 using C++17 - latest draft standard.
I know that in time C++20 will be released and available for Visual Studio 2019 in which I'm looking forward to. However at the time being; I'm still using Visual Studio 2017 on Windows 7, and I'm currently bounded to C++17. I have found this Q/A that is similar to this that had offered great answers, however when I started to use <ctime> or <time.h> functions such as:
std::gmtime()
std::localtime()
My compiler is yelling at me that these functions are marked as unsafe and deprecated. I was attempting to write a function like this:
-DateAndTime.h-
#pragma once
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
namespace util {
enum class TimeLocale {
LOCAL = 0x01,
GMT = 0x02,
BOTH = (LOCAL | GMT)
};
inline TimeLocale operator|(TimeLocale a, TimeLocale b) {
return static_cast<TimeLocale>(static_cast<int>(a) | static_cast<int>(b));
}
#pragma warning( push )
#pragma warning( disable : 4996 )
inline void currentDateAndTime(std::stringstream& stream, TimeLocale locale) {
std::time_t t = std::time(nullptr);
if (locale == TimeLocale::GMT) {
stream << "UTC: " << std::put_time( std::gmtime(&t), "%c, %Z") << '\n';
}
if (locale == TimeLocale::LOCAL) {
stream << "LOCAL: " << std::put_time(std::localtime(&t), "%c, %Z") << '\n';
}
if (locale == TimeLocale::BOTH) {
stream << "UTC: " << std::put_time(std::gmtime(&t), "%c, %Z") << '\n'
<< "LOCAL: " << std::put_time(std::localtime(&t), "%c, %Z") << '\n';
}
}
#pragma warning( pop )
} // namespace util
-main.cpp-
#include "DateAndTime.h"
#include <iostream>
#include <sstream>
using namespace util;
int main() {
try {
std::stringstream stream1;
getCurrentTime(stream1, TimeLocale::GMT);
std::cout << stream1.str() << '\n';
std::stringstream stream2;
getCurrentTime(stream2, TimeLocale::LOCAL);
std::cout << stream2.str() << '\n';
std::stringstream stream3;
getCurrentTime(stream3, TimeLocale::BOTH);
std::cout << stream3.str() << '\n';
std::stringstream stream4;
getCurrentTime(stream4, TimeLocale::GMT | TimeLocale::LOCAL);
std::cout << stream4.str() << '\n';
// ExceptionHandler is one of my class's and can be ignored in this context
// You can replace this with std::exception, std::cerr, etc...
} catch ( ExceptionHandler& e ) {
std::cout << "Exception Thrown: " << e.getMessage() << std::endl;
return EXIT_FAILURE;
} catch (...) {
std::cout << __FUNCTION__ << " Caught Unknown Exception" << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
This works fine if I use #pragma warning( disable : 4996 )
And I am getting a nice output like this:
UTC: 02/20/19 05:44:38, Eastern Standard Time
Local: 02/20/19 00:44:38, Eastern Standard Time
UTC: 02/20/19 05:44:38, Eastern Standard Time
Local: 02/20/19 00:44:38, Eastern Standard Time
UTC: 02/20/19 05:44:38, Eastern Standard Time
Local: 02/20/19 00:44:38, Eastern Standard Time
Which looks great. However, I'd prefer to not use any functions that are marked deprecated, that are not platform specific, that are generic, portable and cross platform as possible that is currently available in C++17. Preferably something from the standard library. I don't want to use a third party library nor boost. std::chrono would be a nice option however their calendar section won't be available until the full release of C++20. What kind of options am I left with?

The functions gmtime and localtime aren't deprecated.
Only for Visual Studio they are deprecated because Visual Studio offer the alternative gmtime_s and localtime_s, so I would use those functions.
And under Unix you have gmtime_r and localtime_r if you want to be thread-safe. See also this answer
Write an inline gmtime_r and localtime_r under Windows that calls gmtime_s and localtime_s and you have an almost standard cross plattform solution until C++20.

Related

QMediaPlayer::metaData always returns QVariant(Invalid)

I used Qt on MAC OS and try to retrieve the metadata of media. So i took the Qt's Media Player Example (Qt 5.8.0) and modified a little bit:
Instead of:
connect(controls, SIGNAL(play()), player, SLOT(play()));
i used:
connect(controls, &PlayerControls::play, player,
[this]{
qDebug() << player->isMetaDataAvailable();
qDebug() << player->metaData(QMediaMetaData::Size);
player->play();
qDebug() << player->isMetaDataAvailable();
qDebug() << player->metaData(QMediaMetaData::Resolution);
});
The results are:
false
QVariant(Invalid)
false
QVariant(Invalid)
1st question: why is the metadata not available even after the media started playing?
2nd question: i added some codes in the destructor:
Player::~Player()
{
qDebug() << player->isMetaDataAvailable();
qDebug() << player->metaData(QMediaMetaData::Duration);
}
Now the metadata is available, but no data is returned:
true
QVariant(Invalid)
Can anyone help me please?
Update Question 1:
Instead of
connect(controls, SIGNAL(pause()), player, SLOT(pause()));
i used:
connect(controls, &PlayerControls::pause, player,
[this]{
qDebug() << player->isMetaDataAvailable();
qDebug() << player->metaData(QMediaMetaData::Resolution);
});
And after about 3-5 seconds of playing, the metadata is already available, but it also returns nothing:
true
QVariant(Invalid)
This is not the exact answer for your question but this may help you. You can check which metadata available with this code:
QMetaDataReaderControl *c = qobject_cast<QMetaDataReaderControl*>(player_->service()->requestControl(QMetaDataReaderControl_iid));
if(c) {
connect(c, &QMetaDataReaderControl::metaDataAvailableChanged, [c](bool) {
qDebug() << c->availableMetaData();
});
}
I', not sure, but I think that qDebug cannot handle QVariant, you should use for example:
qDebug() << (player->metaData(QMediaMetaData::Title)).toString();

Why does reading from STDOUT work?

I encountered a curious case, where reading from STDOUT works, when running a program in terminal. The question is, why and how? Let's start with code:
#include <QCoreApplication>
#include <QSocketNotifier>
#include <QDebug>
#include <QByteArray>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const int fd_arg = (a.arguments().length()>=2) ? a.arguments().at(1).toInt() : 0;
qDebug() << "reading from fd" << fd_arg;
QSocketNotifier n(fd_arg, QSocketNotifier::Read);
QObject::connect(&n, &QSocketNotifier::activated, [](int fd) {
char buf[1024];
auto len = ::read(fd, buf, sizeof buf);
if (len < 0) { qDebug() << "fd" << fd << "read error:" << errno; qApp->quit(); }
else if (len == 0) { qDebug() << "fd" << fd << "done"; qApp->quit(); }
else {
QByteArray data(buf, len);
qDebug() << "fd" << fd << "data" << data.length() << data.trimmed();
}
});
return a.exec();
}
Here's qmake .pro file for convenience, if someone wants to test above code:
QT += core
QT -= gui
CONFIG += c++11
TARGET = stdoutreadtest
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
And here's output of 4 executions:
$ ./stdoutreadtest 0 # input from keyboard, ^D ends, works as expected
reading from fd 0
typtyptyp
fd 0 data 10 "typtyptyp"
fd 0 done
$ echo pipe | ./stdoutreadtest 0 # input from pipe, works as expected
reading from fd 0
fd 0 data 5 "pipe"
fd 0 done
$ ./stdoutreadtest 1 # input from keyboard, ^D ends, works!?
reading from fd 1
typtyp
fd 1 data 7 "typtyp"
fd 1 done
$ echo pipe | ./stdoutreadtest 1 # input from pipe, still reads keyboard!?
reading from fd 1
typtyp
fd 1 data 7 "typtyp"
fd 1 done
So, the question is, what is going on, why do the last two runs above actually read what is typed on terminal?
I also tried looking at QSocketNotifier sources here leading to here, but didn't really gain any insight.
There is no different between fd 0,1,2, all of the three fds is pointing to terminal if not redirected, they are strictly IDENTICAL!
Programs usually use 0 for input, 1 for output, 2 for error, but all of them can be different ways.
Eg, for less, ordinary usage:
prog | less
Now input of less is redirected to the prog, and less can not read any user input from stdin, so how does less get user input like scroll up/down or page up/down ?
Sure less can read user input from stdout, which is exactly what less does.
So you can use these fds wisely when you know how bash handle these fds.

QUrl, right coding for paths

I have a problem with coding. I'm using drag and drom inside my application but some text files I can not open, after some searching I find that the path is with wrong coding. The real file is 'Some_file - 01.txt' but when I try print this path (after drop) to the stdout I will get 'Some_file – 01.txt'. What I miss:
void MainWindow::dropEvent(QDropEvent *event) {
QList<QUrl> urls = event->mimeData()->urls();
...
cout << paths[1].toLocalFile() << endl; /* Some_file – 01.txt */
cout << paths[1].toEncoded() << endl; /* Some_file%20%E2%80%93%2001.txt */
}
I also try QString::fromLatin1 or fromUtf8 but without success.
I'm using QT 4.7.0 and Windows 7.
Edit:
This is my main setup:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
And unfortunately even this is not working for me:
QString fileName = QFileDialog::getOpenFileName(this, tr("Load EEPROM from HEX file"), "", tr("HEX file (*.hex)"));
ifstream hexFile(fileName.toStdString().c_str());
I'm not able to open files where the char '-' is part of file name.
EDIT2:
If I change the file name manualy from 'file.txt' to 'file-.txt' everything is working well. But when I (the same file) copy and paste this file to the same folder, windows will generate new name with postfix: 'file - copy.txt' and this file I can NOT open. So the Windows is using different character for '-' vz. '–'.
What I can do ?
Solution:
void openFile(string fileName) {
ifstream fileio(fileName.c_str());
}
QString qtFileName = QFileDialog::getOpenFileName(...)
openFile(qtFileName.toLocal8Bit().constData());
std::cout is encoded with some local encoding. What you need is to convert the QString returned by the toLocalFile() into a local 8 bit encoding.
For example:
QUrl url = ...;
QString filePath = url.toLocalFile();
QByteArray filePath8 = filePath.toLocal8Bit();
std::cout << filePath8.constData();
But really, the whole exercise is not necessary, since to access the files you should be using QFile, which takes a QString directly, and console output can be done using QTextStream. To wit:
#include <cstdio>
#include <QTextStream>
#include <QFile>
QTextStream out(stdout);
void test() {
out << filePath;
QFile file(filePath);
if (file.open(QIODevice::ReadOnly)) {
...
}
}

qDebug not showing __FILE__,__LINE__

According to qlogging.h
#define qDebug QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug
but when I use like this, file,line,function name not show.
qDebug()<< "abc"; // only show abc;
qDebug()<< ""; // show nothing;
I search for a while, it seems no one had my problem like above.
I use ubuntu14.04,g++ version 4.8.2, qt5.3 build from git.
You can reformat from default output format.
This function was introduced in Qt 5.0.
The line number does not output because the default message pattern is "%{if-category}%{category}: %{endif}%{message}". This format means that the default outputting format is not including metadata like a line number or file name.
% cat logtest.pro
TEMPLATE = app
TARGET = logtest
mac:CONFIG-=app_bundle
SOURCES += main.cpp
% cat main.cpp
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
qSetMessagePattern("%{file}(%{line}): %{message}");
QCoreApplication a(argc, argv);
qDebug() << "my output";
return 0;
}
% qmake && make
% ./logtest
main.cpp(8): my output
You can also use QT_MESSAGE_PATTERN environment variable for setting the message pattern without calling qSetMessagePattern().
See reference for other placeholder. http://qt-project.org/doc/qt-5/qtglobal.html#qSetMessagePattern
If you dig in Qt history you can find out that the __FILE__ and __FUNCTION__ are logged only in debug builds since 1 Oct 2014. The git commit hash is d78fb442d750b33afe2e41f31588ec94cf4023ad. The commit message states:
Logging: Disable tracking of debug source info for release builds
Tracking the file, line, function means the information has to be
stored in the binaries, enlarging the size. It also might be a
surprise to some commercial customers that their internal file &
function names are 'leaked'. Therefore we enable it for debug builds
only.
Here's a simple example of how you might use the captured QMessageLogContext data in a custom message handler installed using qInstallMessageHandler. I didn't output the category or version members because they didn't seem useful. If desired you could also log to a file this way.
#include <QDebug>
#include <QString>
#include <QDateTime>
#include <iostream>
void verboseMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
static const char* typeStr[] = {"[ Debug]", "[ Warning]", "[Critical]", "[ Fatal]" };
if(type <= QtFatalMsg)
{
QByteArray localMsg = msg.toLocal8Bit();
QString contextString(QStringLiteral("(%1, %2, %3)")
.arg(context.file)
.arg(context.function)
.arg(context.line));
QString timeStr(QDateTime::currentDateTime().toString("dd-MM-yy HH:mm:ss:zzz"));
std::cerr << timeStr.toLocal8Bit().constData() << " - "
<< typeStr[type] << " "
<< contextString.toLocal8Bit().constData() << " "
<< localMsg.constData() << std::endl;
if(type == QtFatalMsg)
{
abort();
}
}
}
int main()
{
//Use default handler
qDebug() << "default handler";
qWarning() << "default handler";
qCritical() << "default handler";
//Install verbose handler
qInstallMessageHandler(verboseMessageHandler);
qDebug() << "verbose handler";
qWarning() << "verbose handler";
qCritical() << "verbose handler";
//Restore default handler
qInstallMessageHandler(0);
qDebug() << "default handler";
qWarning() << "default handler";
qCritical() << "default handler";
return 0;
}
You can use standard C++'s __LINE__ and __FILE__. Also, take a look at What's the difference between __PRETTY_FUNCTION__, __FUNCTION__, __func__ SO question. If you use GCC, you can write __PRETTY_FUNCTION__ to get information about function from where the code executes. Just prepare debug-define you like.
For example, here is small compilable application:
#include <QApplication>
#include <QDebug>
#include <iostream>
// Qt-way
#define MyDBG (qDebug()<<__FILE__<<__LINE__<<__PRETTY_FUNCTION__)
// GCC
#define MyStdDBG (std::cout<< __FILE__<<":"<<__LINE__<<" in "<<__PRETTY_FUNCTION__<<std::endl)
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Qt-way
MyDBG;
MyDBG << "Something happened!";
// GCC
MyStdDBG;
return a.exec();
}
It gives next output:
../path/main.cpp 14 int main(int, char**)
../path/main.cpp 15 int main(int, char**) Something happened!
../path/main.cpp:18 in int main(int, char**)
UPD: Added pure C++-way to output.
I think you need to define:
#define qDebug() QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug()
and use it as
qDebug() << "abc";
or
#define qDebug QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug()
and use it as:
qDebug << "abc";
According to documentation qDebug() is already a macro to QMessageLogger(). Default Message handler prints only the message to stderr. I think you might want to use qInstallMessageHandler() to install your own message handler, that uses the context
Edit:
There is a relevant section in a manual, that describes this issue. In Qt4 context variable was not passed to installed message handler, so this solution is Qt5+ only. Default message handler does not make use of passed in context, but you can easily install your own, it's just a function pointer. There is even an example in the manual.
This depends on your Qt version number, whether you are using a debug build or a release build of Qt, and whether you have customized Qt message handling in your application.
According to Qt 5.4 documentation written in source code "qlogging.cpp":
QMessageLogger is used to generate messages for the Qt logging framework. Usually one uses
it through qDebug(), qWarning(), qCritical, or qFatal() functions,
which are actually macros: For example qDebug() expands to
QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug()
for debug builds, and QMessageLogger(0, 0, 0).debug() for release builds.
So if you do not see file, line and function name information in your output, very likely you are using a release version of Qt.
If you still want to see the file, line and function name information in your output with a release version of Qt, there are several ways of achieving it, as very well explained in some of the previous answers.
If you wonder how to get the debug context in a non-debug build: Define QT_MESSAGELOGCONTEXT while compiling your code, and the information won't be stripped:
http://doc.qt.io/qt-5/qmessagelogcontext.html

QProcess problems, output of process

I am trying to figure out the use of QProcess. I looked at Qt doc with no luck.
http://doc.qt.io/qt-4.8/qprocess.html
EXAMPLES OF PROBLEM.
Example 1: Code bellow works.
#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QProcess>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
QProcess cmd;
cmd.start("cmd");
if (!cmd.waitForStarted()) {
return false;
}
cmd.waitForReadyRead();
QByteArray result = cmd.readAll();
//qout << result.data() << endl; //console junk captured, doesn't show.
//My test command
cmd.write("echo hello");
cmd.write("\n");
//Capture my result
cmd.waitForReadyRead();
//This is my command shown by cmd, I don't show it, capture & discard it.
result = cmd.readLine();
//Read result of my command ("hello") and the rest of output like cur dir.
result = cmd.readAll();
qout << result.data();
qout << "\n\n---End, bye----" << endl;
return a.exec();
}
The output of the above code is
hello
F:\Dev_Qt\expControllingExtConsoleApps-build-desktop>
---End, bye----
The problem is that if I try to use ipconfig or 7zip in this fashion via Qprocess and cmd console, I am unable to see any output from ipconfig or 7zip. I don't know if anything is even done, if something is done then why can't I see the output? Code below illustrates.
Example 2: Does not work. Can't use ipconfig.
#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QString>
#include <QProcess>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
QProcess cmd2;
cmd2.setWorkingDirectory("C:/Program Files/7-Zip"); //not needed in this example.
cmd2.setReadChannel(QProcess::StandardOutput);
cmd2.setProcessChannelMode(QProcess::MergedChannels);
cmd2.start("cmd");
if (!cmd2.waitForStarted())
{
qout << "Error: Could not start!" << endl;
return false;
}
cmd2.waitForReadyRead();
QByteArray result = cmd2.readAll();
qout << result.data() << endl; //Console version info, etc.
//My command
cmd2.write("ipconfig");
cmd2.write("\n");
//Capture output of ipconfig command
//DOES NOT WORK!!
cmd2.waitForReadyRead();
while (! cmd2.atEnd())
{
result = cmd2.readLine();
qout << result;
result.clear();
}
qout << endl;
qout << "\n\n---end----" << endl;
return a.exec();
}
Output is below, it is missing the ipconfig connection information result. No output from ipconfig is captured at all.
Microsoft Windows XP [Version
5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.
C:\Program Files\7-Zip> ipconfig
---end----
Should have been more like this (with ipconfig result).
Microsoft Windows XP [Version
5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and
Settings\noname>ipconfig
Windows IP Configuration
Ethernet adapter Local Area
Connection:
Connection-specific DNS Suffix . :
IP Address. . . . . . . . . . . . : 192.172.148.135
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.172.148.177
C:\Documents and Settings\noname>
Obviously the output should should have been a little diff than above but the Connection info which is the output of "ipconfig" should have been captured. In the same way if I try to use 7zip via cmd console... I can not see/capture any output of 7zip. So my question is how can I use command line apps like ipconfig and 7zip via QProcess and cmd console and see the result of the output of these applications?
Example 3: 7zip does not work
#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QProcess>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
QProcess cmd2;
cmd2.setWorkingDirectory("C:/Program Files/7-Zip");
cmd2.setReadChannel(QProcess::StandardOutput);
cmd2.setProcessChannelMode(QProcess::MergedChannels);
cmd2.start("cmd");
if (!cmd2.waitForStarted()) {
return false;
}
//My Command
cmd2.write("7z.exe");
cmd2.write("\n");
//Capture output of ipconfig command
cmd2.waitForReadyRead();
QByteArray result;
while (! cmd2.atEnd()) {
result = cmd2.readLine();
qout << result;
result.clear();
}
qout << endl;
qout << "\n\n---end----" << endl;
return a.exec();
}
Output below. Does not show anything from 7zip.
Microsoft Windows XP [Version
5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.
C:\Program Files\7-Zip>7z.exe
---end----
Output is expected to be along the lines of...
Microsoft Windows XP [Version
5.1.2600] (C) Copyright 1985-2001 Microsoft Corp.
C:\Documents and Settings\noname>cd
C:\Program Files\7-Zip
C:\Program Files\7-Zip>7z.exe
7-Zip 9.15 beta Copyright (c)
1999-2010 Igor Pavlov 2010-06-20
Usage: 7z [...]
<archive_name> [<file_names>...]
[<#listfiles...>]
a: Add files to archive
b: Benchmark d: Delete files from
archive e: Extract files from
archive (without using directory
names) l: List contents of archive
t: Test integrity of archive u:
Update files to archive x: eXtract
files with full paths
-ai[r[-|0]]{#listfile|!wildcard}: Include archives
-ax[r[-|0]]{#listfile|!wildcard}: eXclude archives -bd: Disable
percentage indicator
-i[r[-|0]]{#listfile|!wildcard}: Include filenames -m{Parameters}:
set compression Method
-o{Directory}: set Output directory -p{Password}: set Password -r[-|0]: Recurse subdirectories -scs{UTF-8 |
WIN | DOS}: set charset for list files
-sfx[{name}]: Create SFX archive -si[{name}]: read data from stdin -slt: show technical information for l (List) command -so: write data to
stdout -ssc[-]: set sensitive case
mode -ssw: compress shared files
-t{Type}: Set type of archive -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]:
Update options -v{Size}[b|k|m|g]:
Create volumes -w[{path}]: assign
Work directory. Empty path means a
temporary directory
-x[r[-|0]]]{#listfile|!wildcard}: eXclude filenames -y: assume Yes on
all queries
C:\Program Files\7-Zip>
eI see one big problem.
Under windows you issue a commend pressing the Enter key. Writing
cmd.write("command");
cmd.write("\n");
just isn't enough you have to write
cmd.write("command");
cmd.write("\n\r");
Notice the trailing \r. Try this, it should work better, and by better I mean 7zip. I don't know if you'll get ipconfig to work properly.
Good luck and best regards
D
EDIT
Here is a working solution:
#include <QtCore/QCoreApplication>
#include <QtCore/QProcess>
#include <QtCore/QString>
#include <QtCore/QTextStream>
// Not clean, but fast
QProcess *g_process = NULL;
// Needed as a signal catcher
class ProcOut : public QObject
{
Q_OBJECT
public:
ProcOut (QObject *parent = NULL);
virtual ~ProcOut() {};
public slots:
void readyRead();
void finished();
};
ProcOut::ProcOut (QObject *parent /* = NULL */):
QObject(parent)
{}
void
ProcOut::readyRead()
{
if (!g_process)
return;
QTextStream out(stdout);
out << g_process->readAllStandardOutput() << endl;
}
void
ProcOut::finished()
{
QCoreApplication::exit (0);
}
int main (int argc, char **argv)
{
QCoreApplication *app = new QCoreApplication (argc, argv);
ProcOut *procOut = new ProcOut();
g_process = new QProcess();
QObject::connect (g_process, SIGNAL(readyReadStandardOutput()),
procOut, SLOT(readyRead()));
QObject::connect (g_process, SIGNAL(finished (int, QProcess::ExitStatus)),
procOut, SLOT(finished()));
g_process->start (QLatin1String ("cmd"));
g_process->waitForStarted();
g_process->write ("ipconfig\n\r");
// Or cmd won't quit
g_process->write ("exit\n\r");
int result = app->exec();
// Allright, process finished.
delete procOut;
procOut = NULL;
delete g_process;
g_process = NULL;
delete app;
app = NULL;
// Lets us see the results
system ("pause");
return result;
}
#include "main.moc"
Hope that helps. It worked everytime on my machine.
Even though Dariusz Scharsig already provided a solution to the problem, I would like to point out what I believe to be the actual problem(s) which can be solved using the signal slot mechanism.
Problem 1. The condition in your while loop is based on bool QProcess::atEnd () const which is according to QProcess Documentation states:
Reimplemented from QIODevice::atEnd().
Returns true if the process is
not running, and no more data is available for reading; otherwise
returns false.
But if you looking the documentation for QIODevice::atEnd(), it states:
Returns true if the current read and write position is at the end of
the device (i.e. there is no more data available for reading on the
device); otherwise returns false.
For some devices, atEnd() can return
true even though there is more data to read. This special case only
applies to devices that generate data in direct response to you
calling read() (e.g., /dev or /proc files on Unix and Mac OS X, or
console input / stdin on all platforms).
Solution 1. Change the while loop condition to check the state of your process: while(cmd2.state()!=QProcess::NotRunning){.
Problem 2. You use cmd2.waitForReadyRead(); outside of the loop. Perhaps some data is ready for reading now and when you finished reading, some more gets made available:
you read the commands you just wrote : ipconfig\n
ipconfig takes some time to start up and send text to the console. But by then you have already exited your loop because atEnd() gave true even though your process is still running.
Solution 2. place the waitForReadyRead() inside your loop.
Consequence 2. waitForReadyRead() will tell you when there is data available, which could be more than one Line, so you should consequently also change the cmd2.ReadLine() to cmd2.ReadAll().
Problem 3. As documented in QProcess::closeWriteChannel()
Closing the write channel is necessary for programs that read input
data until the channel has been closed.
Solution 3. One of the following options should work when finished writing your inputs
End the process: cmd2.write("exit\n");
close the Writechannel: cmd2.closeWriteChannel();
Working code:
#include <QtCore/QCoreApplication>
#include <QTextStream>
#include <QByteArray>
#include <QString>
#include <QProcess>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
QByteArray result;
QProcess cmd2;
cmd2.setReadChannel(QProcess::StandardOutput);
cmd2.setProcessChannelMode(QProcess::MergedChannels);
cmd2.start("cmd");
if (!cmd2.waitForStarted()){
qout << "Error: Could not start!" << endl;
return 0;
}
cmd2.write("ipconfig\n");
cmd2.closeWriteChannel(); //done Writing
while(cmd2.state()!=QProcess::NotRunning){
cmd2.waitForReadyRead();
result = cmd2.readAll();
qout << result;
}
qout << endl << "---end----" << endl;
return a.exec();
}
I wrote this answer just to explain the way I understand your problem and found a solution but would like to emphasize that the Preferable Solution is to use the Signal/Slot Mechanism as presented by Dariusz.

Resources