[英]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)中编码,因此希伯来语和俄语字符以“?”形式出现。 由于字符已经丢失,因此无需进一步处理即可恢复它们。
显然它是可能的,因为控制台窗口中的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按预期进行了! 请注意,这是没有记录的,但它确实对我有用。
控制台应用程序可以使用不同的输出方式
WriteConsoleW
作为UNICODE
输出。 WriteConsoleA
或WriteFile
用于控制台句柄需要首先通过WideCharToMultiByte
使用GetConsoleOutputCP()
将UNICODE
文本转换为多字节CodePage :=
GetConsoleOutputCP()
UNICODE
文本(比如UTF-8
或Ansi
),首先需要通过MultiByteToWideChar
(使用CP_UTF8
或CP_ACP
)将其转换为UNICODE
,然后再将其转换为多字节WideCharToMultiByte(GetConsoleOutputCP(), ..)
通常(默认情况下) GetConsoleOutputCP()
返回与GetOEMCP()
相同的值,因此在MultiByteToWideChar
和WideCharToMultiByte
与CP_OEMCP
相同的效果(此常量值转换为GetOEMCP()
)
当输出句柄被重定向到文件时,只需要使用WriteFile
。 但是应用程序可以以任何格式将数据写入文件: UNICODE
, Ansi
( CP_ACP
), UTF-8
( CP_UTF8
)等。将使用什么格式 - 非常依赖于具体应用程序。 你不能完全控制这个。 通常,您将在CP_OEMCP
编码中接收多字节输出。 然后你需要决定如何处理它 - 所有你需要的更快,首先将它转换为UNICODE
并使用unicode
形式。 如果你需要Ansi
- 你需要做一次转换。
如果你尝试使用OutputDebugStringA
在CP_OEMCP
编码中使用管道输出 - 你得到非英语文本的错误(不可读)输出。 但经过2次转换CP_OEMCP
- > UNICODE
- > CP_ACP
您可以使用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
进行输出(包括控制台和文件),并通过WideCharToMultiByte(CP_OEMCP,..)
将此UNICODE
文本转换为多字节 - 如果尝试显示字符,这将是问题CP_OEMCP
代码页(当前系统区域设置)中不存在。 如果您有CP437
- 如果使用UNICODE
- > CP_OEMCP
,希伯来语和俄语字符将丢失,只需要使用unicode直接输出到控制台和文件。 这是可能的 - 取决于具体的应用。 对于说route.exe
这是不可能的。 对于ipconfig.exe
这是可能的,因为它总是以unicode格式写入控制台,并且如果将"OutputEncoding"
设置为"Unicode"
或"UTF-8"
也可以在unicode
或utf-8
写入文件
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.