2

Previously, I have asked Qt translations in C++ project are rendered as question marks in cmd and powershell question. The point of the question was that the help message of .\app -h, translated to Cyrillic, is rendered as ?????????.

Since then, I discovered that in https://github.com/RSATom/Qt/blob/master/qtbase/src/corelib/tools/qcommandlineparser.cpp, the Qt messages are printed with qInfo("%ls", qUtf16Printable(message)) on Windows.

In cmd.exe or poweshell.exe I get question marks ?????????, when trying to output a Cyrillic message this way.

I tried to change the font of cmd.exe and powershell.exe to Lucida Console and to execute chcp 10000 (as proposed here UTF-16 on cmd.exe), but this does not help.

Here is the minimum reproducible example:

// > cl main.cpp /I C:\Qt\5.12.12\msvc2017_64\include
// > link main.obj /LIBPATH C:\Qt\5.12.12\msvc2017_64\lib\Qt5Core.lib

#include "QtCore/QtGlobal"
#include "QtCore/QString"

int main()
{
    qInfo("%ls", qUtf16Printable("Привет"));
    return 0;
}

// > main.exe
// ?????? 

I would really appreciate any help. There are two problems here. The first: how can I use QCommandLineParser under Windows. The second: if QCommandLineParser (using qInfo("%ls", qUtf16Printable(message))) is okay and not a bug, then how can I make cmd show it all right.

JenyaKh
  • 2,040
  • 17
  • 25
  • What encoding is your cpp file in? Did you tell your compiler that? When you look at the raw bytes output by your program, are they the correct UTF-16 encoding of your input string? – Botje Oct 14 '22 at 11:43
  • The encoding of my cpp file in vscode is shown as UTF-8. – JenyaKh Oct 14 '22 at 11:51

2 Answers2

2

I don't pretend on the answer, but on Windows for Unicode console output you should do something similar to:

#include <QDebug>
#include <iostream>
#include <string>

#include <fcntl.h>
#include <io.h>
#include <stdio.h>

void myMessageOutput( QtMsgType type, const QMessageLogContext & context, const QString & msg )
{
    Q_UNUSED( context );

    std::wcout << msg.toStdWString() << std::endl;

    if( type == QtFatalMsg )
        abort();
}

int main()
{
    _setmode( _fileno( stdout ), _O_U16TEXT );

    qInstallMessageHandler( myMessageOutput );

    const std::wstring rs = L"Привет";
    const auto s = QString::fromStdWString( rs );

    std::wcout << s.toStdWString() << std::endl;

    qDebug() << s;

    qInfo() << s;

    return 0;
}
Igor Mironchik
  • 582
  • 4
  • 17
  • Thank you for the answer! I upvote it. With `_setmode( _fileno( stdout ), _O_U16TEXT );` I also managed to do it. My question is more about QCommandLineParser though. Because for now it looks like that for Cyrillic letters, one must write their own QCommandLineParser or something. – JenyaKh Oct 14 '22 at 13:23
  • 1
    I afraid that you will not find any argument parser that can print help and messages with `_setmode( _fileno( stdout ), _O_U16TEXT );`. Just forgot abot this idea - my $0.02. My own parser can do it, but with `wmain()` function, that is not compatible with `QApplication`, – Igor Mironchik Oct 14 '22 at 14:22
  • Why don't they exist, such parsers (using this _setmode statement)? Interestingly, why Qt doesn't use the statement. Though honestly I don't fully understand what it does and how it works, just found it on this site. – JenyaKh Oct 15 '22 at 04:16
  • MSVC asserts on `fputs()`, for example, if `stdout` is unicoded. That is why with Qt you will not reach Russian in Windows console. You can use that approach with `std::wcout` only, so you should be tied to `std::wstring`... – Igor Mironchik Oct 15 '22 at 14:19
  • I can suggest to forget such OS as Windows, they started wrongly with theirs code pages and lost. Linux won, at these times Linux is a candy, cherry on the cake. Lol :) – Igor Mironchik Oct 15 '22 at 14:22
1

Accidentally I found the solution. You should create following manifest file for your executable:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
    <application>
        <windowsSettings>
            <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
        </windowsSettings>
    </application>
</assembly>

Let it will be named as app.manifest. In CMake project just add app.manifest into sources of your executable.

In the main() function add two lines of code:

#include <Windows.h>

int main( int argc, char ** argv )
{
    SetConsoleCP( GetACP() );
    SetConsoleOutputCP( GetACP() );
}

And voila...

Printing should be done using UTF-8, so:

// 1 - works.
QTextStream s( stdout );
s << QString( "Вот оно!!!\n" ) << Qt::flush;

// 2 - works.
fputs( qPrintable( QString( "Опа-на...\n" ) ), stdout );

// 3 - works.
std::cout << u8"std::cout работает!!!\n";

// 4 - DOES NOT work.
std::wcout << L"wcout НЕ работает!!!\n";
Igor Mironchik
  • 582
  • 4
  • 17
  • If I understand right this will print UTF-8? How is the printing itself should be done? – JenyaKh Oct 31 '22 at 04:03
  • This approach will allow you to use Russian in `QCommandLineParser`. You can print with `QTextStream s( stdout ); s << QString( "Вот оно!!!\n" ) << Qt::flush;` or `fputs( qPrintable( QString( "Опа-на...\n" ) ), stdout );`. But `std::wcout` WILL NOT print Russian, so `std::wcout << L"wcout НЕ работает!!!" << std::endl;` will print only English letters. – Igor Mironchik Oct 31 '22 at 04:55
  • So the help message of QCommandLineParser will be printed normally in Russian? – JenyaKh Oct 31 '22 at 05:10
  • Sure, you will see normal Russian letters! – Igor Mironchik Oct 31 '22 at 05:15
  • Thank you very much! I upvote. I will try to implement this in my project. May I ask one more question. As this is UTF-8, as far as I understood, I should use Lucida console? – JenyaKh Oct 31 '22 at 05:35
  • I tested on my Windows 10, I DID NOT set any font for the `cmd.exe`. I just launched `cmd.exe` and run there test application. I saw normal Russian text. So, I'm not sure about `Lucida console`, I guess, that you should not set any font explicitly. – Igor Mironchik Oct 31 '22 at 05:47
  • I have just tested it for my app. It works!!! Thank you! I accept your answer. Much time has passed, but maybe could you remember where have you found the solution? Just curious. – JenyaKh Dec 14 '22 at 06:59
  • Alas, I have just discovered that SetConsoleCP/SetConsoleOutputCP calls breaks the console. My batch files stop working with "is not recognized as an internal or external program" – JenyaKh Dec 14 '22 at 09:01
  • Learnt how to circumvent the problem and also simplified your solution. The problem is that after your code `chcp` in `cmd.exe` gets 65001 instead of 437 and so my batch file breaks down. I use `auto tmp = GetConsoleOutputCP(); SetConsoleOutputCP( CP_UTF8 ); std::cout << "абв\n"; SetConsoleOutputCP( tmp );`. And what I meant by simplifying -- this all works without any manifest file at all. – JenyaKh Dec 14 '22 at 09:17
  • Hi. You better know what is the problem on your machine. Possibly you did something wrong with the solution. This approach wasn't invented by me, I saw it in the KeePassXC sources. But ok, you wrote that you solved, great, have a nice day. Good luck. – Igor Mironchik Dec 15 '22 at 07:07