連載

プロフェッショナルVB.NETプログラミング

第28回 属性(前編)

(株)ピーデー
川俣 晶
2002/12/07

Page1 Page2 Page3

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サービス作成時に用いられる属性についても解説する予定だ。End of Article


 INDEX
  連載 プロフェッショナルVB.NETプログラミング
  第28回 属性(前編)
    1.属性とは何か?
    2.オブジェクトのシリアライズ
  3.Declare文の引数型を変換するMarshalAs属性
 
「プロフェッショナルVB.NETプログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間