[インデックス 15095] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部である src/pkg/syscall/dll_windows.go
ファイルに対する変更です。このファイルは、Windowsオペレーティングシステム上でシステムコール(Windows API関数)を呼び出すための低レベルな機能を提供します。具体的には、ダイナミックリンクライブラリ(DLL)内のプロシージャ(関数)を呼び出すための Proc
および LazyProc
型の Call
メソッドに関連する修正が含まれています。
コミット
- コミットハッシュ:
dcf16bd83d3c36430771f36df53044be3dda67fa
- 作者: Shenghou Ma minux.ma@gmail.com
- コミット日時: 2013年2月3日(日)01:42:17 +0800
- 変更ファイル:
src/pkg/syscall/dll_windows.go
(1ファイル) - 変更行数: 16行追加、4行削除
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dcf16bd83d3c36430771f36df53044be3dda67fa
元コミット内容
syscall: (*Proc).Call should return nil error when no error occurs
Fixes #4686.
R=alex.brainman, rsc
CC=golang-dev
https://golang.org/cl/7174047
このコミットメッセージは、syscall
パッケージの(*Proc).Call
メソッドが、エラーが発生していない場合にnil
エラーを返すように修正することを示しています。これはGoのイディオムに沿ったエラーハンドリングであり、Fixes #4686
という記述から、GoのIssueトラッカーで報告された問題4686を解決するためのものであることがわかります。
変更の背景
Windows APIの呼び出しにおいて、多くの関数は成功/失敗を示す戻り値を持ち、詳細なエラー情報はGetLastError
という関数を通じて取得されます。Goのsyscall
パッケージは、これらのWindows APIをGoから呼び出すためのラッパーを提供しています。
従来の(*Proc).Call
および(*LazyProc).Call
メソッドは、Windows APIの呼び出し結果に関わらず、常にGetLastError
の結果をerror
戻り値として返していました。これは、API呼び出しが成功した場合でも、GetLastError
が以前のエラーコードを保持している可能性があるため、nil
ではないエラーが返されるという問題を引き起こしていました。
Goのエラーハンドリングの慣習では、エラーが発生しなかった場合はnil
を返すことが期待されます。この乖離は、syscall
パッケージを利用する開発者にとって混乱の原因となり、エラーチェックのロジックを複雑にしていました。Issue #4686は、この問題点を指摘し、Call
メソッドがGoのエラーハンドリングの慣習に従うべきであることを提唱していました。
このコミットは、Call
メソッドのerror
戻り値のセマンティクスを明確にし、Windows API呼び出しが成功した場合にはnil
エラーを返すように修正することで、この問題を解決することを目的としています。
前提知識の解説
Go言語のsyscall
パッケージ
Go言語のsyscall
パッケージは、オペレーティングシステムが提供する低レベルなプリミティブ(システムコール)へのアクセスを提供します。これにより、ファイル操作、プロセス管理、ネットワーク通信など、OS固有の機能に直接アクセスできます。特にWindows環境では、このパッケージを通じてWindows API関数を呼び出すことが可能です。
Windows APIとGetLastError
Windows APIは、Windowsオペレーティングシステムの機能にアクセスするためのプログラミングインターフェースです。多くのWindows API関数は、成功した場合は非ゼロの値や特定の成功コードを返し、失敗した場合はゼロやエラーを示す値を返します。詳細なエラー情報(エラーコード)は、GetLastError
という関数を呼び出すことで取得できます。GetLastError
は、現在のスレッドで最後に発生したエラーのコードを返します。このエラーコードは、エラーの種類を特定するために使用されます。
uintptr
型
Go言語のuintptr
型は、ポインタを保持するのに十分な大きさの符号なし整数型です。これは、GoのポインタとC言語のポインタ(またはOSのハンドルやアドレス)の間で変換を行う際に使用されます。syscall
パッケージでは、Windows API関数に引数を渡したり、戻り値を受け取ったりする際に、このuintptr
型が頻繁に用いられます。
技術的詳細
このコミットの核心は、syscall
パッケージ内のProc
およびLazyProc
型のCall
メソッドのエラーハンドリングのセマンティクスを修正することです。
Windows APIの呼び出しでは、関数が成功したかどうかは通常、その関数の戻り値によって判断されます。例えば、CreateFile
関数は成功するとファイルハンドルを返し、失敗するとINVALID_HANDLE_VALUE
を返します。そして、失敗した場合にGetLastError
を呼び出すことで具体的なエラーコードを取得します。
Goのsyscall.Syscall
関数(およびそのバリアント)は、Windows APIを呼び出すための低レベルなラッパーであり、r1
, r2
, err
の3つの戻り値を持ちます。ここでerr
は、GetLastError
の結果に基づいて設定されます。
問題は、(*Proc).Call
や(*LazyProc).Call
が、Syscall
のerr
戻り値をそのまま自身のerr
戻り値として公開していた点にありました。これにより、Windows API呼び出し自体は成功しているにもかかわらず、GetLastError
が以前の呼び出しのエラーコードを保持しているために、Call
メソッドがnil
ではないエラーを返してしまうという状況が発生していました。これはGoのエラーハンドリングの慣習(エラーがない場合はnil
を返す)に反していました。
このコミットでは、Call
メソッドのコメントを更新し、err
戻り値のセマンティクスを明確にしています。新しいセマンティクスでは、lastErr
(以前のerr
)は常に非nil
であり、GetLastError
の結果から構築されることが明記されています。そして、呼び出し元は、実際にエラーが発生したかどうかを判断するために、r1
やr2
といった主要な戻り値を検査する必要がある、と強調されています。これにより、Goの慣習に沿ったエラーハンドリングを維持しつつ、Windows APIのGetLastError
の挙動を正確に反映できるようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/dll_windows.go
ファイルに集中しています。
--- a/src/pkg/syscall/dll_windows.go
+++ b/src/pkg/syscall/dll_windows.go
@@ -114,8 +114,14 @@ func (p *Proc) Addr() uintptr {
return p.addr
}
-// Call executes procedure p with arguments a.
-func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, err error) {
+// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
+// are supplied.
+//
+// The returned error is always non-nil, constructed from the result of GetLastError.
+// Callers must inspect the primary return value to decide whether an error occurred
+// (according to the semantics of the specific function being called) before consulting
+// the error. The error will be guaranteed to contain syscall.Errno.
+func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
switch len(a) {
case 0:
return Syscall(p.Addr(), uintptr(len(a)), 0, 0, 0)
@@ -260,8 +266,14 @@ func (p *LazyProc) Addr() uintptr {
return p.proc.Addr()
}
-// Call executes procedure p with arguments a.
-func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, err error) {
+// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
+// are supplied.
+//
+// The returned error is always non-nil, constructed from the result of GetLastError.
+// Callers must inspect the primary return value to decide whether an error occurred
+// (according to the semantics of the specific function being called) before consulting
+// the error. The error will be guaranteed to contain syscall.Errno.
+func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
p.mustFind()
return p.proc.Call(a...)
}
コアとなるコードの解説
このコミットでは、Proc
とLazyProc
のCall
メソッドのシグネチャとコメントが変更されています。
-
戻り値の変数名変更:
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, err error)
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
err
という変数名がlastErr
に変更されました。これは、このエラーが常にGetLastError
の結果であり、API呼び出し自体の成功/失敗を示すものではないことをより明確にするためのものです。
-
コメントの追加と修正: 最も重要な変更は、
Call
メソッドのドキュメンテーションコメントです。// Call executes procedure p with arguments a.
(変更前)// Call executes procedure p with arguments a. It will panic, if more then 15 arguments
// are supplied.
//
// The returned error is always non-nil, constructed from the result of GetLastError.
// Callers must inspect the primary return value to decide whether an error occurred
// (according to the semantics of the specific function being called) before consulting
// the error. The error will be guaranteed to contain syscall.Errno.
(変更後)
新しいコメントは以下の点を明確にしています。
- 引数の制限: 15個を超える引数が渡された場合にパニックが発生すること。
- エラーのセマンティクス:
- 返されるエラー(
lastErr
)は**常に非nil
**であり、GetLastError
の結果から構築されること。 - 呼び出し元は、主要な戻り値(
r1
,r2
)を検査して、実際にエラーが発生したかどうかを判断する必要があること。これは、呼び出された特定のWindows API関数のセマンティクスに従うべきです。 - 返されるエラーは
syscall.Errno
型であることが保証されること。
- 返されるエラー(
この変更により、syscall.Call
を使用する開発者は、lastErr
がnil
であるかどうかでエラーの有無を判断するのではなく、Windows APIのドキュメントに従ってr1
やr2
などの主要な戻り値をチェックし、その上でlastErr
から詳細なエラー情報を取得するという、より正確なエラーハンドリングを行うことができるようになります。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/dcf16bd83d3c36430771f36df53044be3dda67fa
- Go Issue #4686: https://github.com/golang/go/issues/4686 (GoのIssueトラッカーでこのコミットが解決した問題の詳細を確認できます)
- Go CL 7174047: https://golang.org/cl/7174047 (Goのコードレビューシステムにおけるこの変更のレビューページ)
参考にした情報源リンク
- Go言語の
syscall
パッケージに関するドキュメント(Go公式ドキュメント) - Windows APIの
GetLastError
に関するMicrosoft Learnのドキュメント - Stack OverflowのGo言語における
GetLastError
の扱いに関する議論 - Go言語の
syscall
パッケージのソースコード(src/pkg/syscall/syscall_windows.go
など)