[インデックス 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環境では、異なる言語のエラーメッセージが返されていました。
この変更では、FormatMessage
のdwLanguageId
引数に、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) (具体的なスレッドは特定できませんでしたが、関連する議論が存在する可能性を示唆しています。)