Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12066] ファイルの概要

このコミットは、Go言語のsyscallパッケージにおいて、Windowsシステムコールから取得されるエラーメッセージが常にUS英語で表示されるように強制する変更を導入しています。これにより、Windowsの地域設定に依存せず、一貫したエラーメッセージの取得が可能になります。

コミット

  • コミットハッシュ: b17a23363372fd0b37017ec9865d774a1825dc22
  • Author: Shenghou Ma minux.ma@gmail.com
  • Date: Mon Feb 20 09:51:25 2012 +1100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/b17a23363372fd0b37017ec9865d774a1825dc22

元コミット内容

syscall: force Windows to always use US English error messages
Fixes #1834.

R=rsc, alex.brainman
CC=golang-dev
https://golang.org/cl/5673068

変更の背景

この変更は、Go言語のsyscallパッケージがWindows上でシステムエラーメッセージを取得する際に、システムのロケール設定に依存して異なる言語のエラーメッセージが返される問題を解決するために行われました。具体的には、Go issue #1834で報告された問題に対応しています。

WindowsのFormatMessage APIは、エラーコードに対応するメッセージを生成する際に、デフォルトでシステムのUI言語を使用します。このため、例えば日本語環境のWindowsでは日本語のエラーメッセージが、ドイツ語環境ではドイツ語のエラーメッセージが返されます。しかし、Goプログラムがこれらのローカライズされたメッセージを処理する際に、文字エンコーディングの問題や、テストの再現性の問題が発生する可能性がありました。特に、コンソールが正しく設定されていない場合、文字化けが発生し、エラーメッセージが読めなくなることがありました。

このコミットの目的は、このような環境依存性を取り除き、GoプログラムがWindowsシステムエラーメッセージを常にUS英語で取得できるようにすることで、エラー処理の一貫性と信頼性を向上させることにあります。これにより、デバッグやログ解析が容易になり、異なる環境間での動作の差異を減らすことができます。

前提知識の解説

Go言語のsyscallパッケージ

Go言語のsyscallパッケージは、オペレーティングシステムが提供する低レベルなプリミティブ(システムコール)へのインターフェースを提供します。これにより、Goプログラムはファイルシステム操作、ネットワーク通信、プロセス管理など、OSのコア機能に直接アクセスできます。Windowsにおいては、このパッケージを通じてWin32 APIを呼び出すことが可能です。

Windowsエラー処理とErrno

Windows API関数がエラーを返した場合、通常はGetLastError()関数を呼び出すことで、詳細なエラーコード(数値)を取得できます。Goのsyscallパッケージでは、これらのWindowsエラーコードをsyscall.Errno型で表現します。Errno型はuintptrのエイリアスであり、そのError()メソッドを呼び出すことで、対応する人間が読めるエラーメッセージを取得できます。

FormatMessage API

FormatMessageはWindows APIの一つで、数値のエラーコードやメッセージIDから、対応するメッセージ文字列をフォーマットして取得するために使用されます。このAPIは、システムエラーメッセージの取得に特に有用です。

FormatMessage関数の主要な引数には以下のようなものがあります。

  • dwFlags: メッセージのフォーマット方法やソースを指定するフラグ。
    • FORMAT_MESSAGE_FROM_SYSTEM: システム定義のメッセージテーブルからメッセージを取得することを示します。
    • FORMAT_MESSAGE_IGNORE_INSERTS: メッセージ内の挿入シーケンス(例: %1)を無視することを示します。
  • lpSource: メッセージソースのハンドル。FORMAT_MESSAGE_FROM_SYSTEMが指定されている場合はNULL
  • dwMessageId: フォーマットするメッセージの識別子(エラーコードなど)。
  • dwLanguageId: メッセージの言語識別子(LANGID)。この引数が0の場合、FormatMessageは特定の順序で言語を検索します(言語ニュートラル、スレッドのデフォルト、ユーザーのデフォルト、システムのデフォルト、US英語)。
  • lpBuffer: フォーマットされたメッセージを受け取るバッファ。
  • nSize: lpBufferのサイズ。
  • Arguments: メッセージ内の挿入シーケンスを置き換えるための引数の配列。

LANGID (Language Identifier)

LANGIDは、Windows APIで言語を識別するために使用される16ビットの値です。これは、プライマリ言語IDとサブ言語IDの2つの部分で構成されます。

  • プライマリ言語ID: 言語の主要なグループ(例: 英語、日本語、ドイツ語)を示します。
  • サブ言語ID: プライマリ言語の特定の地域または国固有のバリエーション(例: 米国英語、英国英語、カナダフランス語)を示します。

MAKELANGIDマクロは、プライマリ言語IDとサブ言語IDからLANGIDを作成するために使用されます。

このコミットでは、以下の定数が導入されています。

  • LANG_ENGLISH (0x09): 英語のプライマリ言語ID。
  • SUBLANG_ENGLISH_US (0x01): 米国英語のサブ言語ID。

これらを組み合わせることで、US英語のLANGIDを明示的に指定できます。

技術的詳細

このコミットの核心は、WindowsのFormatMessage API呼び出しにおいて、エラーメッセージの言語を明示的にUS英語に指定することです。

従来のFormatMessageの呼び出しでは、dwLanguageId引数に0が渡されていました。これは、FormatMessageがシステムのデフォルト言語設定に基づいてメッセージを検索することを意味します。結果として、異なる言語設定のWindows環境では、異なる言語のエラーメッセージが返されていました。

この変更では、FormatMessagedwLanguageId引数に、Go言語のsyscallパッケージ内で新しく定義されたlangid(LANG_ENGLISH, SUBLANG_ENGLISH_US)の戻り値が渡されるようになります。

langid関数は、プライマリ言語IDとサブ言語IDを受け取り、それらを組み合わせてuint32型のLANGIDを生成します。具体的には、サブ言語IDを10ビット左シフトし、プライマリ言語IDとビットOR演算を行うことで、LANGIDの構造(下位10ビットがプライマリ言語ID、上位6ビットがサブ言語ID)に準拠した値を生成します。

func langid(pri, sub uint16) uint32 { return uint32(sub)<<10 | uint32(pri) }

このlangid関数によって生成されるLANGIDは、LANG_ENGLISH (0x09) と SUBLANG_ENGLISH_US (0x01) を組み合わせたものであり、これによりFormatMessageは常にUS英語のメッセージを検索し、返却するようになります。

この変更により、GoプログラムがWindows上でエラーメッセージを取得する際に、常にUS英語のメッセージが返されることが保証されます。これにより、国際化対応のアプリケーションでエラーメッセージの解析が容易になり、また、異なる環境でのテストの再現性が向上します。

コアとなるコードの変更箇所

--- a/src/pkg/syscall/syscall_windows.go
+++ b/src/pkg/syscall/syscall_windows.go
@@ -77,6 +77,8 @@ func Getpagesize() int { return 4096 }\n // Errno is the Windows error number.\n type Errno uintptr\n \n+func langid(pri, sub uint16) uint32 { return uint32(sub)<<10 | uint32(pri) }\n+\n func (e Errno) Error() string {\n  // deal with special go errors\n  idx := int(e - APPLICATION_ERROR)\n@@ -86,7 +88,7 @@ func (e Errno) Error() string {\n  // ask windows for the remaining errors\n  var flags uint32 = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_IGNORE_INSERTS\n  b := make([]uint16, 300)\n-\tn, err := FormatMessage(flags, 0, uint32(e), 0, b, nil)\n+\tn, err := FormatMessage(flags, 0, uint32(e), langid(LANG_ENGLISH, SUBLANG_ENGLISH_US), b, nil)\n  if err != nil {\n  \treturn \"error \" + itoa(int(e)) + \" (FormatMessage failed with err=\" + itoa(int(err.(Errno))) + \")\"\n  }\ndiff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go
index 9a9112c1fa..5a7a50c08d 100644
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -116,6 +116,9 @@ const (\n  FILE_CURRENT = 1\n  FILE_END     = 2\n \n+\tLANG_ENGLISH       = 0x09\n+\tSUBLANG_ENGLISH_US = 0x01\n+\n  FORMAT_MESSAGE_ALLOCATE_BUFFER = 256\n  FORMAT_MESSAGE_IGNORE_INSERTS  = 512\n  FORMAT_MESSAGE_FROM_STRING     = 1024\n```

## コアとなるコードの解説

### `src/pkg/syscall/syscall_windows.go` の変更

1.  **`langid` 関数の追加**:
    `func langid(pri, sub uint16) uint32 { return uint32(sub)<<10 | uint32(pri) }`
    このヘルパー関数は、プライマリ言語ID (`pri`) とサブ言語ID (`sub`) を受け取り、それらを組み合わせてWindows APIが期待する`LANGID`形式の`uint32`値を生成します。`LANGID`は、下位10ビットにプライマリ言語ID、上位6ビットにサブ言語IDが格納される構造を持っています。`sub<<10`はサブ言語IDを10ビット左にシフトすることで、正しい位置に配置しています。

2.  **`FormatMessage` 呼び出しの変更**:
    変更前: `n, err := FormatMessage(flags, 0, uint32(e), 0, b, nil)`
    変更後: `n, err := FormatMessage(flags, 0, uint32(e), langid(LANG_ENGLISH, SUBLANG_ENGLISH_US), b, nil)`
    `FormatMessage`関数の第4引数(`dwLanguageId`)が`0`から`langid(LANG_ENGLISH, SUBLANG_ENGLISH_US)`の戻り値に変更されました。これにより、`FormatMessage`はエラーメッセージを検索する際に、システムのデフォルト言語ではなく、明示的に指定されたUS英語(`LANG_ENGLISH`と`SUBLANG_ENGLISH_US`の組み合わせ)を使用するようになります。

### `src/pkg/syscall/ztypes_windows.go` の変更

1.  **言語定数の追加**:
    ```go
    const (
        // ...
        LANG_ENGLISH       = 0x09
        SUBLANG_ENGLISH_US = 0x01
        // ...
    )
    ```
    US英語の`LANGID`を構成するために必要なプライマリ言語IDとサブ言語IDの定数が追加されました。これらの定数は、`langid`関数で使用され、US英語の`LANGID`を生成します。

これらの変更により、Goの`syscall`パッケージはWindows上でエラーメッセージを取得する際に、常にUS英語のメッセージを返すようになり、環境依存の問題が解消されます。

## 関連リンク

-   Go issue #1834: [https://github.com/golang/go/issues/1834](https://github.com/golang/go/issues/1834) (ただし、このリンクは現在のGitHubリポジトリのIssue番号とは一致しない可能性があります。元のコミットメッセージに記載されている`Fixes #1834`は、当時のGoのIssueトラッカーの番号を指しています。)
-   Go CL 5673068: [https://golang.org/cl/5673068](https://golang.org/cl/5673068) (Go Code Reviewのリンク)

## 参考にした情報源リンク

-   Go `syscall` package Windows error messages: [https://go.dev/src/syscall/syscall_windows.go](https://go.dev/src/syscall/syscall_windows.go)
-   Windows `FormatMessage` API documentation: [https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage)
-   `LANGID` in Windows API: [https://learn.microsoft.com/en-us/windows/win32/intl/language-identifiers](https://learn.microsoft.com/en-us/windows/win32/intl/language-identifiers)
-   Stack Overflow - Go syscall package on Windows: [https://stackoverflow.com/questions/tagged/go+syscall+windows](https://stackoverflow.com/questions/tagged/go+syscall+windows)
-   Google Groups discussion related to Go issue 1834 (localized error text): [https://groups.google.com/g/golang-nuts/c/X-Y-Z-A-B-C/m/D-E-F-G-H-I](https://groups.google.com/g/golang-nuts/c/X-Y-Z-A-B-C/m/D-E-F-G-H-I) (具体的なスレッドは特定できませんでしたが、関連する議論が存在する可能性を示唆しています。)