リダイレクトされたI/OでProcess.Start
を使用して、文字列でPowerShell.exe
を呼び出し、出力を取得しようとしています。すべて TF-8 です。しかし、私はこの仕事をすることができないようです。
私が試したもの:
-Command
パラメーターを介して実行するコマンドを渡すConsole.OutputEncoding
を設定する$OutputEncoding
を設定する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);
}
}
このスクリプトを作成すると、次のことがわかりました。
[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);
最初の行は破損していますが、2番目の行は破損していません。
H?llo! 48,EF,BF,BD,6C,6C,6F,21
Héllo 48,C3,A9,6C,6C,6F
これは、リダイレクトコードがすべて正常に機能していることを示唆しています。 PowerShellでConsole.WriteLine
を使用すると、期待どおりにUTF-8が取得されます。
これは、単にWrite-Output
を呼び出すのではなく、PowerShellのWrite-Host
コマンドとConsole.WriteLine
コマンドが出力で異なる処理を行う必要があることを意味します。
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
これは.NETのバグです。 PowerShellが起動すると、出力ハンドル(Console.Out)がキャッシュされます。そのテキストライターのEncodingプロパティは、StandardOutputEncodingプロパティの値を取得しません。
PowerShell内から変更すると、キャッシュされた出力ライターのEncodingプロパティはキャッシュされた値を返すため、出力は引き続きデフォルトのエンコードでエンコードされます。
回避策として、エンコードを変更しないことをお勧めします。 Unicode文字列として返されます。この時点で、エンコードを自分で管理できます。
キャッシングの例:
102 [C:\Users\leeholm]
>> $r1 = [Console]::Out
103 [C:\Users\leeholm]
>> $r1
Encoding FormatProvider
-------- --------------
System.Text.SBCSCodePageEncoding en-US
104 [C:\Users\leeholm]
>> [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
105 [C:\Users\leeholm]
>> $r1
Encoding FormatProvider
-------- --------------
System.Text.SBCSCodePageEncoding en-US
エンコーディングの専門家ではなく、これらを読んだ後...
... $ OutputEncoding変数がネイティブアプリケーションにパイプされたデータにのみ影響することはかなり明らかです。
PowerShellを使用してファイルに送信する場合、エンコードは-encoding
コマンドレットのout-file
パラメーターによって制御できます。
write-output "hello" | out-file "enctest.txt" -enuting utf8
PowerShellのフロントでは他にできることは何もありませんが、次の投稿が役立ちます。
[Console]::OuputEncoding
を任意のエンコードとして設定し、[Console]::WriteLine
で出力します。
Powershell ouputメソッドに問題がある場合は、使用しないでください。それは少し悪い感じですが、魅力のように動作します:)
私の問題の解決に時間を費やし、それが興味があるかもしれないと思った。 Windows 8でPowerShell 3.0を使用してコード生成を自動化しようとして問題が発生しました。ターゲットIDEは、MDK-ARM Essential Toolchain 5.24.1を使用するKeilコンパイラーでした。ビルド前のステップでPowerShellをネイティブに使用しているため、OPとは少し異なります。生成されたファイルを#includeしようとしたときに、エラーが表示されました
致命的なエラー:UTF-16(LE)バイトオーダーマークは '..\GITVersion.h'を検出しましたが、エンコードはサポートされていません
出力ファイルを生成する行を次のように変更することで問題を解決しました。
out-file -FilePath GITVersion.h -InputObject $result
に:
out-file -FilePath GITVersion.h -Encoding ascii -InputObject $result