fix: UTF-8-Encoding für Claude CLI auf Windows erzwingen
Auf Windows kam es sporadisch zu Replacement-Chars (�) für Umlaute und Em-Dash, weil claude.exe Output mit CP1252/CP850 schreiben konnte. Lösung: CLI über cmd.exe wrappen und Codepage 65001 (UTF-8) erzwingen, zusätzlich LANG/LC_ALL/PYTHONIOENCODING auf UTF-8 setzen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
b47ca33259
commit
32aad7feef
1 file changed
+79
-1
@@ -39,7 +39,6 @@ public class ClaudeCliService
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = _cliPath,
|
||||
RedirectStandardInput = redirectInput,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
@@ -49,12 +48,91 @@ public class ClaudeCliService
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
};
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
// Auf Windows: über cmd.exe wrappen und Codepage 65001 (UTF-8) erzwingen.
|
||||
// Sonst kann der Child-Prozess (claude.exe) Output mit CP1252/CP850 schreiben,
|
||||
// was beim UTF-8-Decode zu Replacement-Chars (�) für Umlaute / Em-Dash führt.
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("/S /C \"chcp 65001 >nul && ");
|
||||
sb.Append(QuoteArgForCmd(_cliPath));
|
||||
foreach (var a in args)
|
||||
{
|
||||
sb.Append(' ');
|
||||
sb.Append(QuoteArgForCmd(a));
|
||||
}
|
||||
sb.Append('"');
|
||||
|
||||
psi.FileName = Environment.GetEnvironmentVariable("ComSpec") ?? "cmd.exe";
|
||||
psi.Arguments = sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
psi.FileName = _cliPath;
|
||||
foreach (var arg in args)
|
||||
psi.ArgumentList.Add(arg);
|
||||
}
|
||||
|
||||
// UTF-8-Locale für den Child setzen (Node.js / diverse Tools respektieren das).
|
||||
// Schadet auf Windows nicht, hilft auf manchen Linux-Setups (z.B. systemd-Diensten ohne LANG).
|
||||
psi.Environment["LANG"] = "en_US.UTF-8";
|
||||
psi.Environment["LC_ALL"] = "en_US.UTF-8";
|
||||
psi.Environment["PYTHONIOENCODING"] = "utf-8";
|
||||
|
||||
return psi;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quotet ein Argument im Stil von Microsoft CRT (CommandLineToArgvW),
|
||||
/// passend für die Verwendung innerhalb von cmd.exe /C "...".
|
||||
/// </summary>
|
||||
private static string QuoteArgForCmd(string arg)
|
||||
{
|
||||
if (string.IsNullOrEmpty(arg))
|
||||
return "\"\"";
|
||||
|
||||
bool needsQuoting = false;
|
||||
foreach (var c in arg)
|
||||
{
|
||||
if (c == ' ' || c == '\t' || c == '"' || c == '\n' || c == '\v')
|
||||
{
|
||||
needsQuoting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsQuoting)
|
||||
return arg;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append('"');
|
||||
int backslashes = 0;
|
||||
foreach (var c in arg)
|
||||
{
|
||||
if (c == '\\')
|
||||
{
|
||||
backslashes++;
|
||||
}
|
||||
else if (c == '"')
|
||||
{
|
||||
// 2n+1 Backslashes vor einem Anführungszeichen
|
||||
sb.Append('\\', backslashes * 2 + 1);
|
||||
sb.Append('"');
|
||||
backslashes = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append('\\', backslashes);
|
||||
sb.Append(c);
|
||||
backslashes = 0;
|
||||
}
|
||||
}
|
||||
// 2n Backslashes am Ende (vor dem schließenden ")
|
||||
sb.Append('\\', backslashes * 2);
|
||||
sb.Append('"');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string FindCliPath()
|
||||
{
|
||||
// 1. Expliziter Pfad über Umgebungsvariable (höchste Priorität)
|
||||
|
||||
Reference in new issue
Block a user