[英]Set UTF-8 Input and Get UTF-8 Output through pipe to/from Powershell with C/C++
我無法將正確的 utf-8 字符串寫入 powershell 子進程。 ASCII 字符有效,但 utf-8 字符,例如“ü”,將被不同地解釋。 從同一個 powershell 子進程讀取時出現同樣的問題。
總結:我想通過我的程序使用 utf-8 編碼的 powershell。
更新:使用AllocConsole();
然后調用SetConsoleCP(CP_UTF8);
和SetConsoleOutputCP(CP_UTF8);
,正如@mklement 在他的回答中提到的那樣,如果您有一個沒有任何控制台的 GUI 應用程序,它對我有用。 如果您有控制台應用程序,則不必手動分配控制台。
更新 2:如果你有一個 GUI 並調用AllocConsole()
,你可以調用ShowWindow(GetConsoleWindow(), SW_HIDE);
之后隱藏控制台,如此處所述。
到目前為止我已經嘗試過:
$OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8
...ext.Encoding]::Unicode
編寫代碼示例:
std::string test("ls ä\n");
DWORD ret = WriteFile(std_in_write, test.c_str(), test.size(), &number_of_bytes_written, nullptr);
if (ret == 0) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_WRITE_TO_FILE, GetLastError());
}
輸出:ls ├ñ
示例代碼:
HANDLE std_in_read = nullptr;
HANDLE std_in_write = nullptr;
HANDLE std_out_read = nullptr;
HANDLE std_out_write = nullptr;
SECURITY_ATTRIBUTES security_attr;
STARTUPINFO startup_info;
PROCESS_INFORMATION process_information;
DWORD buffer_size = 1000000;
security_attr = {sizeof(SECURITY_ATTRIBUTES), nullptr, true};
if (!CreatePipe(&std_in_read, &std_in_write, &security_attr, buffer_size)) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_CREATE_IN_PIPE, GetLastError());
}
if (!CreatePipe(&std_out_read, &std_out_write, &security_attr, buffer_size)) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_CREATE_OUT_PIPE, GetLastError());
}
GetStartupInfo(&startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup_info.wShowWindow = SW_HIDE;
startup_info.hStdOutput = std_out_write;
startup_info.hStdError = std_out_write;
startup_info.hStdInput = std_in_read;
if (!CreateProcess(TEXT(default_powershell_path), nullptr, nullptr, nullptr, TRUE, 0, nullptr, TEXT(default_windows_path), &startup_info, &process_information)) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_CREATE_PROCESS, GetLastError());
}
std::string test("ls ä\n");
DWORD ret = WriteFile(std_in_write, test.c_str(), test.size(), &number_of_bytes_written, nullptr);
if (ret == 0) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_WRITE_TO_FILE, GetLastError());
}
DWORD dword_read;
while (true) {
DWORD total_bytes_available;
if (PeekNamedPipe(std_out_read, nullptr, 0, nullptr, &total_bytes_available, nullptr) == 0) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_COPY_FROM_PIPE, GetLastError());
}
if (total_bytes_available != 0) {
DWORD minimum = min(buffer_size, total_bytes_available);
char buf[buffer_size];
if (ReadFile(std_out_read, buf, minimum, &dword_read, nullptr) == 0) {
throw PowershellHelper::Exception(PowershellHelper::Exception::Error::COULD_NOT_READ_FILE, GetLastError());
}
std::string tmp(buf);
std::cout << tmp << std::endl;
}
if (total_bytes_available == 0) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
注意: redirect-input-and-output-of-powershell-exe-to-pipes-in-c沒有重復,因為該代碼僅適用於 ASCII 字符,根本無法處理 utf-8 字符。
也沒有c-getting-utf-8-output-from-createprocess 的副本,因為建議的解決方案不會像上面提到的那樣工作,我想輸入 utf-8 以及讀取 utf-8。
在創建 PowerShell 進程之前,您需要通過SetConsoleCP
和SetConsoleOutputCP
WinAPI 函數將控制台輸入和輸出代碼頁設置為65001
(UTF-8) ,因為 PowerShell CLI使用它們來解碼其 stdin 輸入並編碼其 stdout 輸出.
(相比之下, $OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8
在從PowerShell 進行外部程序調用時僅適用於 PowerShell 會話內.)
注意:如果調用進程本身不是控制台應用程序,您可能必須在調用SetConsoleCP
和SetConsoleOutputCP
之前分配一個控制台,使用AllocConsole
WinAPI 函數,但坦率地說,我不清楚 (a) 這是否會使該控制台立即可見(這可能是不希望的)和 (b) CreateProcess
調用是否會自動使用此控制台。
它不起作用,您可以通過cmd.exe
調用並在調用powershell.exe
之前調用chcp
,按照cmd /c "chcp 65001 >NUL & powershell -c ..."
的行; chcp 65001
將控制台代碼頁設置為65001
,即 UTF-8。
(這會帶來額外的開銷,但cmd.exe
進程與powershell.exe
進程相比相對輕量級, chcp.com
也是如此)。
下面是一個示例命令,您可以從 PowerShell 運行以進行演示:
& {
# Save the current code pages.
$prevInCp, $prevOutCp = [Console]::InputEncoding, [Console]::OutputEncoding
# Write the UTF-8 encoded form of string 'kö' to a temp. file.
# Note: In PowerShell (Core) 7+, use -AsByteStream instead of -Encoding Byte
Set-Content temp1.txt -Encoding Byte ([Text.UTF8Encoding]::new().GetBytes('kö'))
# Switch to UTF-8, pipe the UTF-8 file's content to PowerShell's stdin,
# verify that it was decoded correctly, and output it, again encoded as UTF-8.
cmd /c 'chcp 65001 >NUL & type temp1.txt | powershell -nop -c "$stdinLine = @($input)[0]; $stdinLine -eq ''kö''; Write-Output $stdinLine" > temp2.txt'
# Read the temporary file as UTF-8 and echo its content.
Get-Content -Encoding Utf8 temp2.txt
# Clean up.
Remove-Item temp[12].txt
# Restore the original code pages.
[Console]::InputEncoding = $prevInCp; [Console]::OutputEncoding = $prevOutCp
}
這將輸出以下內容,表明powershell
調用既正確讀取了 UTF-8 編碼的輸入,也將其輸出為 UTF-8:
True
ö
筆記:
您可以通過使用進程內PowerShell SDK作為創建powershell.exe
子進程的替代方法來繞過字符編碼問題,盡管我不知道 C++ 有多么痛苦。 有關 C# 示例,請參閱此答案。
我編寫了一個小型 C++ 庫,它允許在 Windows 控制台上輸入和輸出 UTF-8。 您可以將 cin >>、getline()、scanf() 等與 Unicode UTF-8 一起使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.