UTF-8 вывод из PowerShell

Я пытаюсь использоватьProcess.Start с перенаправленным вводом / выводом для вызоваPowerShell.exe со строкой, и чтобы вернуть вывод, все вUTF-8,, Но я, похоже, не смогу сделать эту работу.

Что я пробовал:

Передача команды для запуска через-Command параметрЗапись сценария PowerShell в виде файла на диск с кодировкой UTF-8Запись сценария PowerShell в виде файла на диск с помощью UTF-8 сBOM кодированиеЗапись сценария PowerShell в виде файла на диск с помощью UTF-16настройкаConsole.OutputEncoding как в моем консольном приложении, так и в скрипте PowerShellнастройка$OutputEncoding в PowerShellнастройкаProcess.StartInfo.StandardOutputEncodingДелать все это сEncoding.Unicode вместоEncoding.UTF8

В каждом случае, когда я проверяю байты, которые мне дают, я получаю разные значения для моей исходной строки. Я бы очень хотел объяснить, почему это не работает.

Вот мой код:

static void Main(string[] args)
{
    DumpBytes("Héllo");

    ExecuteCommand("PowerShell.exe", "-Command \"$OutputEncoding = [System.Text.Encoding]::UTF8 ; Write-Output 'Héllo';\"",
        Environment.CurrentDirectory, DumpBytes, DumpBytes);

    Console.ReadLine();
}

static void DumpBytes(string text)
{
    Console.Write(text + " " + string.Join(",", Encoding.UTF8.GetBytes(text).Select(b => b.ToString("X"))));
    Console.WriteLine();
}

static int ExecuteCommand(string executable, string arguments, string workingDirectory, Action<string> output, Action<string> error)
{
    try
    {
        using (var process = new Process())
        {
            process.StartInfo.FileName = executable;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.WorkingDirectory = workingDirectory;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
            process.StartInfo.StandardErrorEncoding = Encoding.UTF8;

            using (var outputWaitHandle = new AutoResetEvent(false))
            using (var errorWaitHandle = new AutoResetEvent(false))
            {
                process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        output(e.Data);
                    }
                };

                process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        error(e.Data);
                    }
                };

                process.Start();

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                process.WaitForExit();
                outputWaitHandle.WaitOne();
                errorWaitHandle.WaitOne();

                return process.ExitCode;
            }
        }
    }
    catch (Exception ex)
    {
        throw new Exception(string.Format("Error when attempting to execute {0}: {1}", executable, ex.Message),
            ex);
    }
}
Обновление 1

Я обнаружил, что если я сделаю этот скрипт:

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Write-Host "Héllo!"
[Console]::WriteLine("Héllo")

Затем вызовите его через:

ExecuteCommand("PowerShell.exe", "-File C:\\Users\\Paul\\Desktop\\Foo.ps1",
  Environment.CurrentDirectory, DumpBytes, DumpBytes);

Первая строка повреждена, а вторая нет:

H?llo! 48,EF,BF,BD,6C,6C,6F,21
Héllo 48,C3,A9,6C,6C,6F

Это говорит о том, что мой код перенаправления работает нормально; когда я используюConsole.WriteLine в PowerShell я получаю UTF-8, как я и ожидал.

Это означает, что PowerShellWrite-Output а такжеWrite-Host команды должны делать что-то другое с выводом, а не просто вызыватьConsole.WriteLine.

Обновление 2

Я даже попробовал следующее, чтобы заставить кодовую страницу консоли PowerShell перейти на UTF-8, ноWrite-Host а такжеWrite-Output продолжать давать сорванные результаты, пока[Console]::WriteLine работает.

$sig = @'
[DllImport("kernel32.dll")]
public static extern bool SetConsoleCP(uint wCodePageID);

[DllImport("kernel32.dll")]
public static extern bool SetConsoleOutputCP(uint wCodePageID);
'@

$type = Add-Type -MemberDefinition $sig -Name Win32Utils -Namespace Foo -PassThru

$type::SetConsoleCP(65001)
$type::SetConsoleOutputCP(65001)

Write-Host "Héllo!"

& chcp    # Tells us 65001 (UTF-8) is being used

Ответы на вопрос(4)

Ваш ответ на вопрос