Win32APIの関数をVBで使うには…
本サイトでVBの話題をするときは、ほぼ毎回Declareステートメントを使用してWin32APIの関数を呼び出しています。Declareステートメントを使うのは一般的に中上級者向けっぽい感じですが、C/C++でWin32API関数を呼び出すのは基本であり、VBでも提供されているライブラリなどでは実現できない処理がWin32APIの利用で実現可能になることがあります。
ここでは、VBでWin32API関数を呼び出したいとき、どのようにDeclareステートメントを書けばよいのかを説明しています。例として、「GetComputerName」API関数を呼び出してみます。
まずは、呼び出したい関数のC言語での定義を調べます。MSDNライブラリの「GetComputerName」を見ると、以下のように書かれています。
[C/C++]
BOOL GetComputerName( LPTSTR lpBuffer, // コンピュータ名 LPDWORD lpnSize // 名前バッファのサイズ );
これをVBのDeclareを使った定義に書き直します。実際にGetComputerNameをVBで定義しなおすと、以下のようになります。
[VB 6.0]
Declare Function GetComputerName _ Lib "kernel32.dll" Alias "GetComputerNameA" _ (ByVal lpBuffer As String, _ ByRef lpnSize As Long) _ As Long
ここで注意したいのが、MSDNライブラリの説明の末尾を見ると、「対応情報」のところに「Unicode: Windows NT/2000はUnicode版とANSI版を実装」と書かれています。気にする点はOSではなく、この関数は「ANSI版」と「Unicode版」がある、ということです。文字列を扱う大抵のWin32API関数には、「ANSI文字列」と「Unicode文字列」が存在するのに合わせ、それぞれのバージョンの関数が用意されており(※Windows 95/98/Meの場合は、Unicode版が実装されてない場合があります)、実際の関数名は後ろに大文字の「A」や「W」を付けたものとなります。GetComputerNameの場合、ANSI版は「GetComputerNameA」、Unicode版は「GetComputerNameW」です。VBでは、Declareステートメント中にString型を入れると、その部分を「ANSI文字列」に自動的に変換します(VB.NETの場合は「Unicode」キーワードで回避可能)。したがって、煩わしさを無くすため、関数は「GetComputerNameA」を使います。
また、GetComputerName関数の引数のうち「lpnSize」の型「LPDWORD」に注意してください。Win32APIの型名は、「P」や「LP」などから始まる型名はほぼ全て「ポインタ」を表しています。VBには「ポインタ」と呼ばれるものはありません(VarPtrなどを除きます)が、似たものに「参照渡し」(ByRef)と「値渡し」(ByVal)があります。ポインタの引数に対しては、その部分を「参照渡し」にするとうまく行きます。(それ以外の引数に対しては、必ず「ByVal」を付ける必要があります。)なお、「LPTSTR」や「LPCTSTR」は一般的には文字列型なので、例外的にString型の値渡しを使います。
そのほかの型については、整数型の場合「8ビット型」→「Byte」、「16ビット型」→「Integer」(VB6.0)または「Short」(VB.NET)、「32ビット型」→「Long」(VB6.0)または「Integer」(VB.NET)とします。64ビット型については、VB.NETの場合は「Long」で大丈夫ですが、VB6.0の場合はぴったり対応する型がありませんが、引数として出てくることはほぼ無いので割愛します。また、「BOOL」型は「Boolean」としたいのですが、「BOOL」型はもともと「int」型なので、32ビットの整数型と同じ(Long…VB6.0、またはInteger…VB.NET)にします。ただし例外的に、戻り値が「BOOL」のものに関しては「Boolean」でも大丈夫です。
VBには存在しない「LPVOID」型は、「LP」から始まるのでポインタですが、この場合は32ビット型(64ビットOSの場合は64ビット型、VB.NETの場合はIntPtr型)を使い、ポインタを整数値として用いる必要があります。VB6.0の場合はVarPtrメソッドで変数のポインタの整数値を取得できます。またVB6.0の場合は、引数の型として「Any」を用いることができるので、「ByRef」と組み合わせてLPVOID型の代わりとして用いることが出来ます。
また、RECTやPOINTなどの「構造体」については、Typeステートメントを用いて自分で定義する必要があります。構造体のメンバの型については、基本的に上記の規則にしたがって書き換えればOKです。
最後に「Lib」キーワードで指定するDLLですが、なぜか日本語版MSDNライブラリのドキュメントには載っていないので、英語版を見て調べる必要があります。英語版MSDNライブラリのGetComputerNameには「Require Kernel32.dll.」と書かれているので、「Lib "kernel32.dll"」と書きます。
なお、GetComputerName関数を使うには、定数「MAX_COMPUTERNAME_LENGTH」が必要です。対応情報に「ヘッダー: Winbase.hで宣言」と書かれているので、この値を調べるには、検索エンジンなどで「Winbase.h #define MAX_COMPUTERNAME_LENGTH」というキーワードで検索をかけると、以下のような記述を見つけられると思います。
[C/C++]
#ifndef _MAC #define MAX_COMPUTERNAME_LENGTH 15 #else #define MAX_COMPUTERNAME_LENGTH 31 #endif
今回はWindowsなので、Constステートメントで以下のようにします。
[VB 6.0]
Public Const MAX_COMPUTERNAME_LENGTH As Long = 15
これで準備が整ったので、実際に呼び出してみます。今回はVBでより利用しやすい形の関数を作成しています。
[VB 6.0]
Public Function GetCompName() As String Dim s As String Dim n As Long n = MAX_COMPUTERNAME_LENGTH + 1 s = String$(n, 0) Call GetComputerName(s, n) s = Left$(s, n) GetCompName = s End Function
※ミスを防ぐため、Declareで宣言した関数の引数でByRefの部分は、数値を直接指定するのではなく変数を渡すようにしてください。(直接指定しても問題ありませんが…)
これで終わりです。オマケとして、以下に代表的な型の対応表を置いておきます。
Win32API | VB 6.0 | VB.NET |
---|---|---|
char, CHAR | Byte※1 | Byte※1, SByte |
unsigned char, BYTE | Byte | Byte |
short, SHORT | Integer | Short |
unsigned short, WORD | Integer※1 | Short※1, UShort |
int, INT | Long | Integer |
unsigned int, UINT | Long※1 | Integer※1, UInteger |
long, LONG | Long | Integer |
unsigned long, ULONG, DWORD | Long※1 | Integer※1, UInteger |
PSTR, LPSTR, LPCSTR | String | String※2 |
PWSTR, LPWSTR, LPCWSTR, BSTR | Long※3 | String※4 |
BOOL | Long | Integer |
LRESULT | Long | Integer |
WPARAM | Long | Integer, UInteger |
LPARAM | Long | Integer |
HANDLE, HWND, etc | Long | System.IntPtr |
LPVOID | Long, ByRef Any※5 | System.IntPtr※6, Object※7 |
FARPROC, etc | Long※8 | (Delegate)※9 |
※1 符号が異なるので、大小比較をするときなどは注意してください。
※2 Declareで定義するときは「Ansi」キーワードを付けてください。
※3 実際に値を渡すときは、文字列変数に対してStrPtrメソッドでポインタのアドレスを取得し、それを渡します。
※4 Declareで定義するときは「Unicode」キーワードを付けてください。
※5 Declareの引数でのみ有効です。
※6 Overloadsキーワードを用いて、いろんな種類の引数の型にして宣言することも可能です。使用目的に合わせてください。
※7 MarshalAs属性を用いて、UnmanagedType.AsAnyを付けてください。
※8 実際に値を渡すときは、AddressOf演算子を使って関数のポインタを取得し、それを渡します。
※9 コールバック関数の宣言にあわせてDelegate宣言を行い、その型を引数の型にします。実際に値を渡すときはAddressOfを使います。
最終更新日: 2008/03/07