问题
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
and8772
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
and8772
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