Windows console app stops printing when I switch to Edge

落爺英雄遲暮 提交于 2021-01-29 06:45:12

问题


I have to write a console app to log the active window PID, text length and text.

It works except when I switch to Edge. The execution doesn't stop, but only the PID and text length get printed to the screen.

Please help, I don't know what else to try.

#include <iostream>
#include <Windows.h>
#include <WinUser.h>

int main()
{
    // Use environment's default locale for char type
    setlocale(LC_CTYPE, "");
    std::cout << "Hello é World!\n";

    while (1)
    {
        // Get foreground window
        HWND hwnd = GetForegroundWindow();
        if (!hwnd) continue;

        // Get window process ID
        DWORD pid = 0;
        GetWindowThreadProcessId(hwnd, &pid);
        if (pid) std::cout << "pid " << pid << " ";

        // Get window text length
        int len = GetWindowTextLength(hwnd);

        // Get window text
        WCHAR* text = new WCHAR[len + 1];
        std::cout << "len " << GetWindowText(hwnd, text, len + 1) << " ";
        //if (!text) continue; // not skipping! so text NOT null
        std::wcout << "text " << text;

        std::cout << std::endl;

        text = NULL;
        
        Sleep(1000);
    }

    return 0;
}

回答1:


If you're reading this from the future, here's what I found out:

TL;DR: Unicode char 8203 wasn't representable in the console codepage, which caused std::wcout to fail. The solution is to check for std::wcout.fail() and if true call std::wcout.clear().

Here's how I got to the bottom of it.

I started suspecting it had something to do with std::wcout because the following code would keep printing even when I switched to Microsoft Edge (and switched tabs within it):

#include <iostream>
#include <Windows.h>
#include <WinUser.h>

int main()
{
    // Use environment's default locale for char type
    setlocale(LC_CTYPE, "");

    while (1)
    {
        // Get foreground window
        HWND hwnd = GetForegroundWindow();
        if (!hwnd) continue;

        // Get window process ID
        DWORD pid = 0;
        GetWindowThreadProcessId(hwnd, &pid);
        std::cout << "pid " << pid << " ";

        // Get window text length
        int len = GetWindowTextLength(hwnd);

        // Get window text
        WCHAR* text = new WCHAR[len + 1];
        GetWindowText(hwnd, text, len + 1);
        std::cout << "text " << text << std::endl;

        text = NULL;
        Sleep(1000);
    }

    return 0;
}

Where:

  • PID 3892 - ConsoleApplication1.exe
  • PID 3144 - Visual Studio Code
  • PID 9812 - Slack
  • PID 8772 - Microsoft Edge - with tabs switched during execution (same PID)

The following code, however, would stop printing the text ... line but keep printing the pid ... line when I switched to Edge (and switched tabs within it):

// ...
std::wcout << "text " << text << std::endl; // switched 'cout' with 'wcout'
// ...

Where:

  • PID 424 - ConsoleApplication1.exe
  • PIDs 3144, 9812 and 8772 same as before

Also, if I switched both pid ... and text ... lines to wcout, then the console output would completely hang as soon as I switched to Edge, clearly showing that the wcout stream had failed:

// ...
std::wcout << "pid " << pid << " "; // switched 'cout' with 'wcout'
// ...
std::wcout << "text " << text << std::endl; // same
// ...

Where:

  • PID 13584 - ConsoleApplication1.exe
  • PIDs 3144, 9812 and 8772 same as before

Now I needed to know what was causing wcout to fail. Per the console outputs above, wcout seemed to fail right after the t in "Microsoft". Invisible faulty char, maybe? So I tweaked the code once more to print both the char and its code:

// ...
// std::wcout << "text " << text << std::endl;
for (int i = 0; i < wcslen(text); i++) std::cout << (char)text[i] << " (" << (int)text[i] << ")\n";

As expected, there was an invisible char right after the t in Microsoft: Unicode character 8203. Now, finally knowing what to throw on Google (a.k.a. "wcout fail unicode 8203"), I found this and this pivotal answers.

Specifically, the code from @dev7060's comment was the litmus test I needed (here modified):

std::wcout << "abc " << L'\u200b' << " defg" << std::endl; // L'\u200b' is unicode char 8203
if (std::wcout.fail()) {
    std::cout << "\nConversion didn't succeed\n";
    std::wcout << "This statement has no effect on the console";
    std::wcout.clear();
    std::wcout << "hello world from wcout! \n";
}
std::cout << "hello world from cout! \n";
std::wcout << "hello world from wcout again! \n";

The solution (so far) is the one below.

// std::wcout << "text " << text << std::endl;
std::wcout << "text " << text;
if (std::wcout.fail()) std::wcout.clear();
std::cout << std::endl;

EDIT: found a new (better?) solution that doesn't involve checking std::wcout.fail() and calling std::wcout.clear():

TL;DR: use setlocale(LC_CTYPE, "en_US.UTF8") or (in my case) setlocale(LC_CTYPE, "pt_BR.UTF8").

I wanted to figure out why the default environment locale (setlocale(LC_CTYPE, "")) wasn't working, so I devised the following little experiment:

std::cout << "default locale " << setlocale(LC_CTYPE, NULL) << std::endl;
std::cout << "default console output code page " << GetConsoleOutputCP() << std::endl;

setlocale(LC_CTYPE, "pt_BR.UTF8"); // 'method' column from table below
// SetConsoleOutputCP(850)

std::cout << "new locale " << setlocale(LC_CTYPE, NULL) << std::endl;
std::cout << "new console output code page " << GetConsoleOutputCP() << std::endl;
Method Default locale Default CP New locale New CP wcout fail?
setlocale(LC_CTYPE, "") C 850 Portuguese_Brazil.1252 850 Yes
setlocale(LC_CTYPE, "en_US.UTF8") C 850 en_US.UTF8 850 No
setlocale(LC_CTYPE, "pt_BR.UTF8") C 850 pt_BR.UTF8 850 No
SetConsoleOutputCP(850)
OEM Multilingual Latin 1; Western European (DOS)
C 850 C 850 Yes
SetConsoleOutputCP(1252)
ANSI Latin 1; Western European (Windows)
C 850 C 1252 Yes
SetConsoleOutputCP(65001)
Unicode (UTF-8)
C 850 C 65001 Yes

In my computer (Windows 10 x86-64), the constants CP_WINANSI and CP_WINUNICODE map to 850 and CP_UTF8 to 65001.

Recommended reading:

  • https://docs.microsoft.com/en-us/windows/console/console-code-pages


来源:https://stackoverflow.com/questions/65308197/windows-console-app-stops-printing-when-i-switch-to-edge

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!