Skip to content

Command output is mis-decoded on non-English Windows: shell pinned to CP437, output decoded as the ANSI code page #142

Description

@bertysentry

Symptom

On a French Windows Server 2022, accented characters in command output come back mangled:

$ winrm-java -h tc-win2022 -u Administrateur -pf pass.txt cmd "dir /A"
 Le volume dans le lecteur C n'a pas de nom.
 Le num‚ro de s‚rie du volume est E6B6-D774

 R‚pertoire de C:\Users\Administrateur
...
23/07/2026  15:46           786ÿ432 NTUSER.DAT
               7 fichier(s)        2ÿ324ÿ500 octets

é becomes (U+201A) and the thousands separator (a non-breaking space) becomes ÿ. This is not
a console-rendering artifact: the String the library returns already holds the wrong characters.
Hex-dumping the CLI output shows L e n u m 202 r o — byte 0x82 is é in CP437, decoded here
as windows-1252 .

Root cause

Two halves of the code disagree about which code page the remote shell speaks:

  1. Shell creation pins the console code page to OEM 437:
    Envelopes.createShell sends
    <wsman:Option Name="WINRS_CODEPAGE">437</wsman:Option>. Verified on the host — running chcp
    through the library reports Page de codes active : 437.

  2. Output decoding follows the ANSI code page:
    WindowsRemoteProcessUtils.getWindowsEncodingCharset() runs
    SELECT CodeSet FROM Win32_OperatingSystem, which returns 1252 on this host, and decodes
    stdout/stderr as windows-1252.

Win32_OperatingSystem.CodeSet is the ANSI code page, never the console (OEM) one, so the two
settings can only ever agree on a US-English host where 437 and 1252 happen to share the ASCII
range. On any non-English locale the mapping is wrong:

byte CP437 (what the shell emits) windows-1252 (how we decode)
0x82 é U+201A
0xFF NBSP ÿ

Fix

Stop juggling code pages: create the shell with WINRS_CODEPAGE=65001 and decode command output as
UTF-8 unconditionally. This is what winrs and other WinRM clients do on supported Windows
versions, it is lossless for every locale (including CJK, which no single-byte OEM page can carry),
and it removes the SELECT CodeSet FROM Win32_OperatingSystem round trip currently made before
every command just to guess the charset.

Scope:

  • Envelopes.createShell: WINRS_CODEPAGE 437 → 65001.
  • Default the command-output charset to UTF-8 instead of detecting it over WMI
    (WinRMClient.detectCharset, WinRMCommandExecutor, WsmanClient.executeCommand).
  • Deprecate WindowsRemoteProcessUtils.getWindowsEncodingCharset() — no longer used by the
    library, and it answers a question (ANSI code page) that never matched the shell's output.
  • CommandRequest.charset(Charset) stays as an explicit override; document that the remote shell
    now emits UTF-8, so overriding is only needed for a shell whose code page was changed by the
    command itself.
  • Note the encoding behaviour in README.md.

WQL is unaffected: results travel as UTF-8 in the SOAP envelope and never went through this path.

Verification

Rerun dir /A against a French host and check the accented output is intact, plus a round trip of
non-Latin-1 characters (e.g. echo of CJK text) that CP437 could not represent at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions