連載
プロフェッショナルVB.NETプログラミング
第28回 属性(前編)
(株)ピーデー
川俣 晶
2002/12/07
|
|
Declare文の引数型を変換するMarshalAs属性
VB.NETは.NET Frameworkの幅広いクラス・ライブラリにアクセスできるため、VB 6と異なり、Win32 APIを呼び出すDeclare文を使用する機会は減少したのではないかと思う。しかし、Win32 APIを呼び出す機会がゼロになったわけではなく、依然としてDeclare文は存在している。このとき、問題になるのが、文字列などのデータ型がVB.NETとWin32 APIで互換性がまったくないことだ。VB.NETの文字列は、System.Stringクラスの文字列クラスにより表現されるが、Win32 APIでは文字型の配列として表現される。この相違を乗り越えるには、API呼び出し時に適切なデータ型の変換が必要となる。このときの変換方法を指定する手段として、MarshalAs属性が使用される。以下は、実際にMarshalAs属性を使用したサンプル・プログラムである。
1: Imports System.Runtime.InteropServices
2:
3: Public Class Form1
4: Inherits System.Windows.Forms.Form
5:
6: …Windows フォーム デザイナで生成されたコード…
7:
8: Declare Auto Sub MessageBox Lib "user32.dll" ( _
9: ByVal hWnd As Integer, _
10: <MarshalAs(UnmanagedType.LPTStr)> ByVal lpText As String, _
11: <MarshalAs(UnmanagedType.LPTStr)> ByVal lpCaption As String, _
12: ByVal uType As Integer)
13:
14: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
15: MessageBox(0, "Hello!", "Sample Program", 0)
16: End Sub
17: End Class
|
|
Declare文の使用時に、データ型の変換方法を指定するMarshalAs属性を使用したサンプル・プログラム5 |
これを実行すると以下のようになる。
|
サンプル・プログラム5の実行結果 |
ここで注目すべき点は、8〜12行目のDeclare文の中で使用されているMarshalAs属性である。10行目と11行目の文字列は、そのままWin32 APIに渡すことができないので、変換する必要がある。そこで、MarshalAs属性はその必要性を指定している。具体的にどんなデータ型に変換するかは、UnmanagedType列挙型の値で指定している。MarshalAs属性とUnmanagedType列挙型は、System.Runtime.InteropServices名前空間に属しているので、1行目のImports文でこの名前空間を指定している。
さて、Win32 API呼び出し時の文字列に関するもう1つの問題は、Win32 APIには文字コードの相違によって同じ機能を持ったAPIが実際には2つ存在している場合があることだ。つまり、その2つのどちらを呼び出すかの明示的な指定と、どちらの文字コードを指定して引数を変換させるかという指定が必要とされる。上記のサンプル・プログラムの場合、Declareキーワードの次のAutoキーワードが、OSの種類に応じて、自動的に2つのWin32 APIの中から選択することを指定している。Windows 9X系ではANSI系を、Windows NT系ではUnicode系のAPIが呼ばれることになると思う。引数の変換に指定したUnmanagedType.LPTStrという値は、状況に応じて適切な文字コードを選択するという機能を持つ。しかし、自動判定に頼らず、明示的に指定することもできる。以下は、明示的にUnicode系のWin32 APIを選ぶように記述したソースである。
1: Imports System.Runtime.InteropServices
2:
3: Public Class Form1
4: Inherits System.Windows.Forms.Form
5:
6: …Windows フォーム デザイナで生成されたコード…
7:
8: Declare Auto Sub MessageBoxW Lib "user32.dll" ( _
9: ByVal hWnd As Integer, _
10: <MarshalAs(UnmanagedType.LPWStr)> ByVal lpText As String, _
11: <MarshalAs(UnmanagedType.LPWStr)> ByVal lpCaption As String, _
12: ByVal uType As Integer)
13:
14: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
15: MessageBoxW(0, "Hello!", "Sample Program", 0)
16: End Sub
17: End Class
|
|
Unicode系のWin32 APIを呼び出すようにサンプル・プログラム5を修正したサンプル・プログラム6 |
ソース・コードの変更点は、2つある。1つは、8行目のメソッド名をMessageBoxからMessageBoxWに変更し、Unicode系のMessageBox APIの本当の名前を指定したことだ。もう1つは、10〜11行目のMarshalAs属性で、Unicode文字列を意味するUnmanagedType.LPWStrという値を指定したことである。なお、いうまでもなく、メソッドの名前はDeclare文のAliasキーワードを活用すれば変更可能なので、MessageBoxという名前で呼び出すようにもできる。
逆に、ANSI系(日本ではシフトJIS)を用いて文字列を受け渡すように変更したサンプル・プログラムを以下に示す。
1: Imports System.Runtime.InteropServices
2:
3: Public Class Form1
4: Inherits System.Windows.Forms.Form
5:
6: …Windows フォーム デザイナで生成されたコード…
7:
8: Declare Auto Sub MessageBoxA Lib "user32.dll" ( _
9: ByVal hWnd As Integer, _
10: <MarshalAs(UnmanagedType.LPStr)> ByVal lpText As String, _
11: <MarshalAs(UnmanagedType.LPStr)> ByVal lpCaption As String, _
12: ByVal uType As Integer)
13:
14: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
15: MessageBoxA(0, "Hello!", "Sample Program", 0)
16: End Sub
17: End Class
|
|
ANSI系のWin32 APIを呼び出すようにサンプル・プログラム5を修正したサンプル・プログラム7 |
ここでは、Win32 APIの名前はANSI系のWin32 APIとしての本名であるMessageBoxAに変更し、変換するデータ型をUnmanagedType.LPStrとしている。UnmanagedType.LPStrはANSI系の文字列を示す値である。
次回予告
今回解説した属性はシステムで用意されたものだけだったが、プログラマーが独自に新しい属性を定義することも可能だ。次回では、独自の属性を自作し、それを利用してみる。またWebサービス作成時に用いられる属性についても解説する予定だ。
Insider.NET 記事ランキング
本日
月間