VBAからWin32 API呼び出し

Excel 2007で試した。

Win32 APIのMessageBoxをサンプルに。

int MessageBox(
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType
);

文字列は内部でUnicodeで扱われているので、MessageBoxWを利用する。

Declare Function MessageBox Lib "user32.dll" Alias "MessageBoxW" (ByVal hWnd As Long, ByVal lpText As Long, ByVal lpCaption As Long, ByVal uType As Long) As Long

呼び出し。

retVal = MessageBox(0, StrPtr("はろー・わーるど"), StrPtr("きゃぷしょん"), 0)

たぶん、内部での文字の扱いがUnicodeに変わっているMS Office製品なら*1、上のコードは問題なく動くのじゃないかと思うけれども、面倒なので旧版のMS Office製品では試してない。

Windowsの整数型とVBAの整数型が結構違うので、そこに注意して関数を宣言しないと、おかしなことになると思う。

・Win32
BOOL ... 32ビット。
BOOLEAN ... 8ビット。
BYTE ... 8ビット。
PVOID ... 32ビット。
HANDLE ... 32ビット。
HWND ... 32ビット。
int ... 32ビット。
UINT ... 32ビット。
long ... 32ビット。

VBA
Byte ... 8ビット。
Boolean ... 16ビット。
Integer ... 16ビット。
Long ... 32ビット。
Single ... 16ビット。
Double ... 32ビット。

ブール値の型も結構、嫌なんだけれども、Integerが一番のくせものかも。C/C++やらC#やらをさわってて、次にVBAをちょこっとさわったら、Integerのデータ幅、32ビットだと多くの人が思うんじゃないだろうか? わたしゃ、思ってた。変数のデータ幅をあわせるのにヘルプを調べて「Integerが16ビットかよ!?」って驚いた*2。16ビットの時代を今も引きずっているのだろうか。
とまれ、VBAのIntegerとWin32のUINTとかintとか、その辺のデータ幅を同じと考えるとまずいのだけれども、同じと考えて、intやUINTに対してIntegerを使っているサンプルも見かける。上のMessageBoxの例だと第4引数を"ByVal uType As Long"としないといけないところを、"ByVal uType As Integer"としているとか。
関数呼び出しの際に、4バイトごとにデータをスタックに積んでいるだろうから、うまく動いてしまっているのだろう。わかりにくバグの原因になりそう。

ちなみに上で使っているStrPtr()は非公開関数なので、非公開関数に頼ったコーディングというのも、ちっとどうかなと思わないでもないけれども、まあ、Excel 2007でも使えています。
StrPtr()や、類似の非公開関数については、以下の記事を。
[Visual Basic で変数のアドレスを取得する方法]

*1:Office97あたりで変わったんだっけか? ようしらんが。製品によっても違うだろうし。

*2:まあ、この辺、昔も調べて、そのときも驚いたような気がするんだけれども、覚えていない。