I cannot get it work to write a correct utf-8 string to a powershell sub-process. ASCII characters work but utf-8 chars, e.g. 'ü', will be interpreted differently. Same problem when reading from the same powershell sub-process.
Summarized: I want to use powershell through my program with utf-8 encoding.
Update:
Allocating a console with AllocConsole();
and then calling SetConsoleCP(CP_UTF8);
and SetConsoleOutputCP(CP_UTF8);
, as @mklement mentioned in his answer, worked for me, if you have a GUI application without any console. If you have a console application you don't have to allocate the console manually.
Update 2:
If you have a GUI and called AllocConsole()
, you can just call ShowWindow(GetConsoleWindow(), SW_HIDE);
afterwards to hide the console, as mentioned here.
What I have tried so far:
- Setting Input and Output encoding to utf-8
$OutputEncoding = [System.Console]::OutputEncoding = [System.Console]::InputEncoding = [System.Text.Encoding]::UTF8
within the process - Doing the same with UTF-16 in case there is a bug, e.g.
...ext.Encoding]::Unicode
- Doing the same with ISO-Latin 1 (cp1252)
- Using wchar_t as buffer and input for all tested encodings
- Testing byte order for the given string
- Testing Unicode (4 byte per character instead of 2)
- Building the string bit by bit by myself
- Setting compiler flag to \D UNICODE
Code Example for writing:
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());
}
Output: ls ä
Example Code:
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));
}
Note: No duplicate of redirect-input-and-output-of-powershell-exe-to-pipes-in-c, since the code only works for ASCII characters and won't handle utf-8 characters at all.
Also no duplicate of c-getting-utf-8-output-from-createprocess, because the suggested solutions won't work as mentioned above and I want to input utf-8 as well as read utf-8.