[VB.NET] System.Text.Encoding.Default は使わないほうがよい

2015年8月13日

 System.Text.Encoding.Default は使わないほうがよいです。
 正確には「真にその挙動が得たいときには Encoding.Default を使えばよいのですが、多くの場合はその挙動を期待しているわけではないと思われる」です。

 System.Text.Encoding.Default を使いたい局面とは、主に ShiftJIS のバイト列を得たいときが大半だと思います。 (正確には コードページ932 ですが、以下 ShiftJIS で統一します)
 しかしその場合、System.Text.Encoding.GetEncoding(932) または System.Text.Encoding.GetEncoding(“Shift-JIS”) と書くほうが適切です。また、そう書くことでソースコード上でも得たい文字コードを明示でき可読性も良くなります。

 また、多言語対応を考えた場合で何らかの理由でローカルの文字コードが欲しい場合は、System.Text.Encoding.Default ではなく System.Globalization.CultureInfo クラスの TextInfo.ANSICodePage を参照したほうが良い気がします。
 具体的には以下のような記述にします。(もっとよい記述があるかもしれませんが)

System.Text.Encoding.GetEncoding(
    System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage
) 

 こうすることで、カルチャの変更にコードページを連動させることが可能です。

 

 System.Text.Encoding.Default は Win32API の GetACP 関数で戻ってくるコードページで評価されるようです。
 GetACP 関数がどこの設定値を参照するのかわかりませんが、以下のリファレンスを見ると「current Windows ANSI code page identifier for the operating system」であるのと、システムで一意の設定と思われます。

 https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd318070.aspx

GetACP function
Retrieves the current Windows ANSI code page identifier for the operating system.
Caution The ANSI API functions, for example, the ANSI version of TextOut, implicitly use GetACP to translate text to or from Unicode. For the Multilingual User Interface (MUI) edition of Windows, the system ACP might not cover all code points in the user’s selected logon language identifier. For compatibility with this edition, your application should avoid calls that depend on GetACP either implicitly or explicitly, as this function can cause some locales to display text as question marks. Instead, the application should use the Unicode API functions directly, for example, the Unicode version of TextOut.

 したがって、確認はしていませんが GetACP 関数が取得する値は、おそらく「コントロール パネル」→「時計、言語、および地域」→「地域」にある「Unicode 対応ではないプログラムの現在の言語」で設定された値(以下「システムロケール」と記述)ではないかと思います。

 このため、たとえば 日本語 OS の場合は期待通り ShiftJIS のコードが得られますが、日本語 OS でない場合は ShiftJIS のコードは得られません。日本語 OS 以外で Encoding.Default で ShiftJIS を得るためには、上記のシステムロケールを変更する必要があります。
 また、Encoding.Default はシステムロケールに従うので、カルチャ(System.Threading.Thread.CurrentThread.CurrentCulture など) のコードページと不一致が発生する場合があります。
 したがって、多言語対応などで「カルチャの設定ではなくシステムロケールの文字コードを得たい」という強い意志がある場合を除いて Encoding.Default は使用しないほうが無難です。

 

 ちなみに Encoding.Default が GetACP 関数に依存している事は、以下から確認することができます。
 mscorlib/system/text/encoding.cs, Default プロパティ

 引用は以下。(但し個人的に見やすいように順序の入れ替え・省略等をしているので、正確な内容は上記リンクを参照してください)

public static Encoding Default {
    [System.Security.SecuritySafeCritical]  // auto-generated
    get {
        if (defaultEncoding == null) {
            defaultEncoding = CreateDefaultEncoding();
        }
        return defaultEncoding;
    }
}

private static Encoding CreateDefaultEncoding()
{
    Encoding enc;

#if FEATURE_CODEPAGES_FILE            
    int codePage = Win32Native.GetACP();
    if (codePage == 1252)
        enc = new SBCSCodePageEncoding(codePage);
    else
        enc = GetEncoding(codePage);
~略~
    return (enc);
}

 

 ところで、ソースコードが修正できない DLL (サードパーティー製など) が Encoding.Default を使っていると思われる案件で、非日本語 OS 上で文字化けしてまい、かつシステムロケールの変更や Microsoft AppLocale Utility の使用による解決が難しい場合、以下のようなコードで無理やり Encoding.Default の エンコードを変更することで文字化けを抑止できるかもしれません。

' リフレクションを使って Private フィールドにアクセスし、強引に Default Encoding を ShiftJIS にする。
' 要 Imports System.Text / Imports System.Reflection
Dim a = GetType(Encoding)
Dim b = a.GetField("defaultEncoding",
                   BindingFlags.NonPublic Or BindingFlags.Static)
b.SetValue(a, Encoding.GetEncoding("Shift-JIS"))

 対策できる範囲が狭く、かつ副作用が大きいので上記のようなことはすべきではありませんが、それらを理解したうえで他に手がないようであれば、上記で解決できるかを試してみるのも良いかもしれません。






posted in Program, VB.NET by tsuda.a@gmail.com

Follow comments via the RSS Feed | Leave a comment | Trackback URL

Leave Your Comment

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org