将生成的进程标准输出捕获为 unicode
Capture spawned process stdout as unicode
在我的C++/WinAPI代码中,我想运行一些命令并捕获它们的输出。为了测试非 ASCII 输出,我将网络连接重命名为Ethérnét אבג БбГгДд
并运行ipconfig
。在命令提示符下运行时,输出正确(使用支持字体(如 Courier New)时可见):
C:>ipconfig
Windows IP Configuration
Ethernet adapter Ethérnét אבג БбГгДд:
(...)
我尝试按照此答案中的示例将输出重定向到管道。但是从ReadFile()
返回的字节数组不是 unicode - 它是用CP_OEMCP编码的(在我的例子中为 CP437),因此希伯来语和俄语字符显示为"?"。s.由于字符已经丢失,因此无法进一步处理它们。
显然这是可能的,因为控制台窗口中的cmd可以做到这一点。我该怎么做?
当ipconfig
检测到输出设备是控制台时,它似乎会产生 Unicode 输出,否则会产生 ANSI 输出。 这可能是向后兼容性措施。
出于同样的原因,大多数其他内置命令行工具可能要么是仅 ANSI 的,要么以与ipconfig
相同的方式运行。 在 Windows 中,命令行工具意味着在命令行上使用;不鼓励程序员向他们掏钱并解析输出。 相反,您应该使用相应的 API。
如果您知道所需的语言,则可以选择将保留内容的代码页。
由@Jonathan添加:未记录:原来您可以使用环境变量OutputEncoding
来控制内置命令的编码。我用ipconfig进行了测试,但大概它也适用于其他内置工具:
> for %e in ("" Unicode Ansi UTF8) do (set OutputEncoding=%~e& ipconfig >ipconfig-%~e.txt)
> (set OutputEncoding= & ipconfig 1>ipconfig-.txt )
> (set OutputEncoding=Unicode & ipconfig 1>ipconfig-Unicode.txt )
> (set OutputEncoding=Ansi & ipconfig 1>ipconfig-Ansi.txt )
> (set OutputEncoding=UTF8 & ipconfig 1>ipconfig-UTF8.txt )
事实上,ipconfig-*.txt 正如预期的那样被封存了!请注意,这是没有记录的,但它确实对我有用。
附录:从 Windows 10 v1809 开始,另一种选择是创建伪控制台。
控制台应用程序可以使用不同的输出方式。
- 对于控制台句柄,我们可以将
WriteConsoleW
用于已经在UNICODE
. - 如果我们想将
WriteConsoleA
或WriteFile
用于控制台 句柄需要首先将文本UNICODE
转换为多字节WideCharToMultiByte
CodePage :=
GetConsoleOutputCP()
- 如果我们最初没有
UNICODE
文本进行输出(例如UTF-8
或Ansi
),需要先将其转换为UNICODE
MultiByteToWideChar
(带CP_UTF8
或CP_ACP
),然后 已经再次将其转换为多字节WideCharToMultiByte(GetConsoleOutputCP(), ..)
通常(默认情况下)GetConsoleOutputCP()
返回与GetOEMCP()
相同的值,因此在MultiByteToWideChar
和WideCharToMultiByte
中具有与CP_OEMCP
相同的效果(此常量值转换为GetOEMCP()
)
当输出句柄重定向到文件时,只需使用WriteFile
。 但是应用程序可以以任何格式将数据写入文件:UNICODE
,Ansi
(CP_ACP
),UTF-8
(CP_UTF8
)等。 将使用什么格式 - 很大程度上取决于具体应用。 你不能完全控制这一点。 通常,您将收到CP_OEMCP
编码的多字节输出。 然后你需要决定如何处理它 - 你会更快需要首先将其转换为UNICODE
并使用unicode
形式。如果您需要Ansi
- 您将需要做其他一次转换。
假设您尝试在CP_OEMCP
编码中使用管道输出与OutputDebugStringA
- 非英语文本的输出错误(不可读)。 但是在 ->UNICODE
->CP_ACP
CP_OEMCP
进行 2 次转换后,您可以使用OutputDebugStringA
更正显示的文本 但因为OutputDebugStringW
存在 - 这里只足以UNICODE
转换
此外,一些应用程序具有用于控制输出到文件格式的特殊选项。 假设ipconfig.exe
寻找"OutputEncoding"
环境变量,并从中依赖字符串值("Unicode"
,"Ansi"
,"UTF-8"
)产生不同的输出。 默认情况下(如果此环境变量不存在或值未知)CP_OEMCP
使用
管道读取过程示例。假定输入数据采用CP_OEMCP
编码:
void OnRead(PVOID buf, ULONG cbTransferred)
{
if (cbTransferred)
{
if (int len = MultiByteToWideChar(CP_OEMCP, 0, (PSTR)buf, cbTransferred, 0, 0))
{
PWSTR pwz = (PWSTR)alloca((1 + len) * sizeof(WCHAR));
if (len = MultiByteToWideChar(CP_OEMCP, 0, (PSTR)buf, cbTransferred, pwz, len))
{
if (g_bUseAnsi)
{
if (cbTransferred = WideCharToMultiByte(CP_ACP, 0, pwz, len, 0, 0, 0, 0))
{
PSTR psz = (PSTR)alloca(cbTransferred + 1);
if (cbTransferred = WideCharToMultiByte(CP_ACP, 0, pwz, len, psz, cbTransferred, 0, 0))
{
DoPrint(psz, cbTransferred, OutputDebugStringA);
}
}
}
else
{
DoPrint(pwz, len, OutputDebugStringW);
}
}
}
}
}
// debugger can incomplete print too big buffer, so split it on small chunks
template<typename T> void DoPrint(T* p, ULONG len, void (WINAPI* fnOutput)(const T*))
{
ULONG cb;
T* q = p;
do
{
cb = min(len, 256);
q = p + cb;
T c = *q;
*q = 0;
fnOutput(p);
*q = c;
p = q;
} while (len -= cb);
}
关于您的具体案例 -ipconfig.exe
用于输出到控制台的WriteConsoleW
。 因此,它不依赖于当前的系统区域设置,可以正确显示多语言文本。 但是另一个工具,例如route.exe
用于输出(控制台和文件)WriteFile
并在此之前将文本UNICODE
转换为多字节WideCharToMultiByte(CP_OEMCP,..)
- 结果这里会出现问题, 如果尝试显示代码页(当前系统区域设置)中不存在CP_OEMCP
字符。如果您有CP437
- 如果使用 ->CP_OEMCP
,希伯来语和俄语字符将丢失UNICODE
,只需要使用 unicode 直接输出到控制台和文件。这可能吗 - 取决于具体应用。因为说这是不可能的route.exe
。ipconfig.exe
这可能,因为它始终以 Unicode 格式写入控制台,并且如果您将"OutputEncoding"
设置为"Unicode"
或"UTF-8"
,也可以以unicode
或utf-8
写入文件
- 捕获标准输出以压缩并使用 CTRL-C 中断会给出损坏的 zip 文件
- 使用提升过程获取 shell 命令的标准输出
- 将标准输出重定向到ostream
- 如何在C++中执行命令并获取命令的返回代码标准输出和标准
- 在程序执行期间从标准输出重定向到自定义流
- 标准输出C++意外行为
- 防止 qDebug() 写入标准输出
- C++ - <<运算符重载,链表 - 地址而不是标准输出
- 有没有办法知道C++中将打印到标准输出的大小?
- 最后推迟标准输出
- 得到~对标准输出没有回应~在黑客排名问题上
- Win32 GUI C(++) 应用将标准输出和标准输出重定向到磁盘上的同一文件
- 在标准输出中执行打印会导致数组中随机分配值
- 捕获远程进程子进程的标准输出
- 使用 QProcess 读取标准输出
- 如何将值发送到标准输出流并在另一个程序中访问这些值
- 无法使用创建进程重定向标准输出
- HackerRank说~对标准输出没有回应~.C++
- 记录标准输入和标准输出
- 将生成的进程标准输出捕获为 unicode