QCommandLineParser: Show usage and errors in message boxes on Windows.

Use the Windows MessageBox API if no console window can be obtained.

[ChangeLog][QtCore][QCommandLineParser] Message boxes are used
to display errors and usage if no console window can be obtained
on Windows.

Change-Id: I63ee8e4d8bd78db83e688fd69374779102562aa3
Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
Friedemann Kleint 2014-11-14 15:50:18 +01:00
parent c124ac42c7
commit 09e674849a
2 changed files with 49 additions and 28 deletions

View File

@ -82,14 +82,7 @@ int main(int argc, char *argv[])
"qt.examples.imagegestures.debug=true\n"); "qt.examples.imagegestures.debug=true\n");
commandLineParser.setApplicationDescription(description); commandLineParser.setApplicationDescription(description);
if (!commandLineParser.parse(QCoreApplication::arguments())) { commandLineParser.process(QCoreApplication::arguments());
showHelp(commandLineParser, commandLineParser.errorText());
return -1;
}
if (commandLineParser.isSet(helpOption)) {
showHelp(commandLineParser);
return 0;
}
QStringList arguments = commandLineParser.positionalArguments(); QStringList arguments = commandLineParser.positionalArguments();
if (!arguments.isEmpty() && !QFileInfo(arguments.front()).isDir()) { if (!arguments.isEmpty() && !QFileInfo(arguments.front()).isDir()) {

View File

@ -37,6 +37,10 @@
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qhash.h> #include <qhash.h>
#include <qvector.h> #include <qvector.h>
#include <qdebug.h>
#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
# include <qt_windows.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -209,7 +213,10 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
platforms. These applications may not use the standard output or error channels platforms. These applications may not use the standard output or error channels
since the output is either discarded or not accessible. since the output is either discarded or not accessible.
For such GUI applications, it is recommended to display help texts and error messages On Windows, QCommandLineParser uses message boxes to display usage information
and errors if no console window can be obtained.
For other platforms, it is recommended to display help texts and error messages
using a QMessageBox. To preserve the formatting of the help text, rich text using a QMessageBox. To preserve the formatting of the help text, rich text
with \c <pre> elements should be used: with \c <pre> elements should be used:
@ -219,36 +226,20 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
case CommandLineOk: case CommandLineOk:
break; break;
case CommandLineError: case CommandLineError:
#ifdef Q_OS_WIN
QMessageBox::warning(0, QGuiApplication::applicationDisplayName(), QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
"<html><head/><body><h2>" + errorMessage + "</h2><pre>" "<html><head/><body><h2>" + errorMessage + "</h2><pre>"
+ parser.helpText() + "</pre></body></html>"); + parser.helpText() + "</pre></body></html>");
#else
fputs(qPrintable(errorMessage), stderr);
fputs("\n\n", stderr);
fputs(qPrintable(parser.helpText()), stderr);
#endif
return 1; return 1;
case CommandLineVersionRequested: case CommandLineVersionRequested:
#ifdef Q_OS_WIN
QMessageBox::information(0, QGuiApplication::applicationDisplayName(), QMessageBox::information(0, QGuiApplication::applicationDisplayName(),
QGuiApplication::applicationDisplayName() + ' ' QGuiApplication::applicationDisplayName() + ' '
+ QCoreApplication::applicationVersion()); + QCoreApplication::applicationVersion());
#else
printf("%s %s\n", QGuiApplication::applicationDisplayName(),
qPrintable(QCoreApplication::applicationVersion()));
#endif
return 0; return 0;
case CommandLineHelpRequested: case CommandLineHelpRequested:
#ifdef Q_OS_WIN
QMessageBox::warning(0, QGuiApplication::applicationDisplayName(), QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
"<html><head/><body><pre>" "<html><head/><body><pre>"
+ parser.helpText() + "</pre></body></html>"); + parser.helpText() + "</pre></body></html>");
return 0; return 0;
#else
parser.showHelp();
Q_UNREACHABLE();
#endif
} }
\endcode \endcode
@ -489,6 +480,41 @@ QString QCommandLineParser::errorText() const
return QString(); return QString();
} }
enum MessageType { UsageMessage, ErrorMessage };
#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
// Return whether to use a message box. Use handles if a console can be obtained
// or we are run with redirected handles (for example, by QProcess).
static inline bool displayMessageBox()
{
if (GetConsoleWindow())
return false;
STARTUPINFO startupInfo;
startupInfo.cb = sizeof(STARTUPINFO);
GetStartupInfo(&startupInfo);
return !(startupInfo.dwFlags & STARTF_USESTDHANDLES);
}
#endif // Q_OS_WIN && !QT_BOOTSTRAPPED && !Q_OS_WIN && !Q_OS_WINRT
static void showParserMessage(const QString &message, MessageType type)
{
#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
if (displayMessageBox()) {
const UINT flags = MB_OK | MB_TOPMOST | MB_SETFOREGROUND
| (type == UsageMessage ? MB_ICONINFORMATION : MB_ICONERROR);
QString title;
if (QCoreApplication::instance())
title = QCoreApplication::instance()->property("applicationDisplayName").toString();
if (title.isEmpty())
title = QCoreApplication::applicationName();
MessageBoxW(0, reinterpret_cast<const wchar_t *>(message.utf16()),
reinterpret_cast<const wchar_t *>(title.utf16()), flags);
return;
}
#endif // Q_OS_WIN && !QT_BOOTSTRAPPED && !Q_OS_WIN && !Q_OS_WINRT
fputs(qPrintable(message), type == UsageMessage ? stdout : stderr);
}
/*! /*!
Processes the command line \a arguments. Processes the command line \a arguments.
@ -505,7 +531,7 @@ QString QCommandLineParser::errorText() const
void QCommandLineParser::process(const QStringList &arguments) void QCommandLineParser::process(const QStringList &arguments)
{ {
if (!d->parse(arguments)) { if (!d->parse(arguments)) {
fprintf(stderr, "%s\n", qPrintable(errorText())); showParserMessage(errorText() + QLatin1Char('\n'), ErrorMessage);
::exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
} }
@ -911,7 +937,9 @@ QStringList QCommandLineParser::unknownOptionNames() const
*/ */
Q_NORETURN void QCommandLineParser::showVersion() Q_NORETURN void QCommandLineParser::showVersion()
{ {
fprintf(stdout, "%s %s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(QCoreApplication::applicationVersion())); showParserMessage(QCoreApplication::applicationName() + QLatin1Char(' ')
+ QCoreApplication::applicationVersion() + QLatin1Char('\n'),
UsageMessage);
::exit(EXIT_SUCCESS); ::exit(EXIT_SUCCESS);
} }
@ -928,7 +956,7 @@ Q_NORETURN void QCommandLineParser::showVersion()
*/ */
Q_NORETURN void QCommandLineParser::showHelp(int exitCode) Q_NORETURN void QCommandLineParser::showHelp(int exitCode)
{ {
fprintf(stdout, "%s", qPrintable(d->helpText())); showParserMessage(d->helpText(), UsageMessage);
::exit(exitCode); ::exit(exitCode);
} }