Programming Field - プログラミング Tips

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の部分は、数値を直接指定するのではなく変数を渡すようにしてください。(直接指定しても問題ありませんが…)

これで終わりです。オマケとして、以下に代表的な型の対応表を置いておきます。

Win32APIVB 6.0VB.NET
char, CHARByte※1Byte※1, SByte
unsigned char, BYTEByteByte
short, SHORTIntegerShort
unsigned short, WORDInteger※1Short※1, UShort
int, INTLongInteger
unsigned int, UINTLong※1Integer※1, UInteger
long, LONGLongInteger
unsigned long, ULONG, DWORDLong※1Integer※1, UInteger
PSTR, LPSTR, LPCSTRStringString※2
PWSTR, LPWSTR, LPCWSTR, BSTRLong※3String※4
BOOLLongInteger
LRESULTLongInteger
WPARAMLongInteger, UInteger
LPARAMLongInteger
HANDLE, HWND, etcLongSystem.IntPtr
LPVOIDLong, ByRef Any※5System.IntPtr※6, Object※7
FARPROC, etcLong※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