search
LoginSignup
35

More than 1 year has passed since last update.

posted at

updated at

VBAでWindowsAPIを使うには

概要

VBAでWinAPIを使用する際に気を付けるべきことなどを、頭の整理も兼ねてまとめてみる。

基本的な構文

Declare ステートメント

を見て貰うのが最も確実です。

少し紛らわしい点としては
VBA内で使用するプロシージャ名が、SUb|Functionの後ろに記述したもの。
Aliasの後ろにあるのが、DLL内の本来の関数名となります。

VBAのバージョンの確認

実行するVBAのバージョンによっては使用できない記述があるため、どのバージョンで実行するのかを確認する必要があります。
VBAVer.PNG

VBA7.1.PNG

2017/1/8 では最新は7.1 MSOfficeでは2013,2016に搭載されています。

VBA 6.x

MSOfficeでは2007以前が該当します(2007で6.5)。
32bit版しか存在しません。

古い記述でも動作しますが、VBA 7.x 64bit環境に備えて
#If...Then...#Elseディレィクティブを用いてVBA 7用の記述をしておくこともできます。

VBA 7.x

MSOfficeでは2010以降が該当します(2017/1/8現在)。
32bit版と64bit版が存在します。

VBA 6.x 用の記述の場合、32bitなら動きますが、64bitでは動きません。
VBA 7.x 用の記述をすることで32bit/64bit両対応にすることができます1(代わりにVBA 6.xでは動かなくなります)。

VBA 7.x 用の記述 リファレンス

Microsoft公式のものとして以下のようなものがダウンロードできます。

Office 2010 Help Files: Win32API_PtrSafe with 64-bit Support

VBA 7.x 用の記述は解凍したファイルの中の「Win32API_PtrSafe.TXT」内に記載されています。

参考:VBA 7.x 用記述からVBA 6.x 用記述への書き換え

  • PtrSafeの削除
  • LongPtr型をLong型に変更

とすればVBA 6.x で動くようになります。

VBAの言語機能を活かす

上記のようにMicrosoft公式のリファレンスがありますが、べた写しではなくある程度修正することで多少扱いやすくすることができます。

列挙型を使う

リファレンス内では定数はConstステートメントを使って宣言されています。
この定数の中でLong型のものは列挙型(Enumステートメント)で宣言しても問題ありません(VBAの列挙型はただのLong型なので)。

そして、API関数の引数の型を列挙型にすることで簡単に選ぶことができるようになります。

また、VBA組み込みの定数・列挙型の中にも同等のものが存在する場合があります。
ShowWindow関数の引数のnCmdShowはVBA組み込み列挙型のVbAppWinStyleとほぼ同じものです。

省略可能な引数にする

API関数の中には多くの引数を持つものが存在します。
その中で特定の場合しか使わないような引数に関してはOptionalを指定することで簡略化することができます。

サンプル

ここまでの内容を踏まえて簡単なプロシージャを作ってみます。
今回作成するのは、「左クリック」するサブルーチンです。

マウスの挙動を再現するにはmouse_event関数を使用するのが手軽です。
リファレンスにはmouse_event関数は以下のように定義されています(横長になるため改行を追加しています)。

Declare PtrSafe Sub _
    mouse_event Lib "user32" ( _
    ByVal dwFlags As Long, _
    ByVal dx As Long, _
    ByVal dy As Long, _
    ByVal cButtons As Long, _
    ByVal dwExtraInfo As LongPtr)

また、動作を示す定数として以下が定義されています。

Const MOUSEEVENTF_MOVE = &H1 '  mouse move
Const MOUSEEVENTF_LEFTDOWN = &H2 '  left button down
Const MOUSEEVENTF_LEFTUP = &H4 '  left button up
Const MOUSEEVENTF_RIGHTDOWN = &H8 '  right button down
Const MOUSEEVENTF_RIGHTUP = &H10 '  right button up
Const MOUSEEVENTF_MIDDLEDOWN = &H20 '  middle button down
Const MOUSEEVENTF_MIDDLEUP = &H40 '  middle button up
Const MOUSEEVENTF_ABSOLUTE = &H8000& '  absolute move

この定数は第1引数のdwFlagsに指定します。
今回の場合「左クリック」をしたいので、動作としては「左ボタンを押して」、「左ボタンを放す」となります。

残りの引数はドラッグや追加情報を送るために使うので、今回は0指定でOKです。

べた書き

まずは、リファレンスの記述そのままに作成すると以下のようになります。

べた書き
Option Explicit

Const MOUSEEVENTF_MOVE = &H1 '  mouse move
Const MOUSEEVENTF_LEFTDOWN = &H2 '  left button down
Const MOUSEEVENTF_LEFTUP = &H4 '  left button up
Const MOUSEEVENTF_RIGHTDOWN = &H8 '  right button down
Const MOUSEEVENTF_RIGHTUP = &H10 '  right button up
Const MOUSEEVENTF_MIDDLEDOWN = &H20 '  middle button down
Const MOUSEEVENTF_MIDDLEUP = &H40 '  middle button up
Const MOUSEEVENTF_ABSOLUTE = &H8000& '  absolute move

Private Declare PtrSafe Sub _
    mouse_event Lib "user32" ( _
    ByVal dwFlags As Long, _
    ByVal dx As Long, _
    ByVal dy As Long, _
    ByVal cButtons As Long, _
    ByVal dwExtraInfo As LongPtr)


Sub LeftClick()
    Call mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
    Call mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
End Sub

修正

少し扱いやすいように修正をしてみます。

マウスの動作を示す定数をMouseEventFlag列挙型とします。
それに合わせてmouse_event関数のdwFlagsの型をMouseEventFlagとします。

またmouse_event関数の第1引数以外はとりあえず使わないのでOptional指定をして既定値を0にしておきます。

修正
Option Explicit

Private Enum MouseEventFlag
    mef_MOVE = &H1 '  mouse move
    mef_LEFTDOWN = &H2 '  left button down
    mef_LEFTUP = &H4 '  left button up
    mef_RIGHTDOWN = &H8 '  right button down
    mef_RIGHTUP = &H10 '  right button up
    mef_MIDDLEDOWN = &H20 '  middle button down
    mef_MIDDLEUP = &H40 '  middle button up
    mef_ABSOLUTE = &H8000& '  absolute move
End Enum

Private Declare PtrSafe Sub _
    mouse_event Lib "user32" ( _
    ByVal dwFlags As MouseEventFlag, _
    Optional ByVal dx As Long = 0, _
    Optional ByVal dy As Long = 0, _
    Optional ByVal cButtons As Long = 0, _
    Optional ByVal dwExtraInfo As LongPtr = 0)

Sub New_LeftClick()
    Call mouse_event(mef_LEFTDOWN)
    Call mouse_event(mef_LEFTUP)
End Sub

その他

True/False

API関数の中には、戻り値が0だったら失敗、それ以外(多くの場合は1)だと成功という関数が多いです。
また、VBAでは数値をBooleanに変換するとき0の時False、それ以外の時はTrueと判定されます。

しかし、VBA本来のTrueは数値に変換すると-1になります。
VBAの論理演算子はTrue=-1を前提として動作するので、意図しない論理演算にならないように気をつける必要があります。

String型

普段は意識しませんがVBAのString型も値型では無く、参照型になります。
そのため、値渡しで渡したString型変数の値が変化するといったことがおきます。

また、~Aという名前の関数を使うと、いわゆる環境依存文字が文字列に含まれる場合に正常に処理ができないため、~Wという名前の関数の使用も検討すると良いです。

~Wの関数を使用する際は、ByVal 引数 As StringByVal 引数 As LongPtrに書き換え、呼び出し時はStrPtr(文字列)を渡すようにすれば、多くの場合は動作するはずです。

ポインタ関連関数

機能名 動作
AddressOf 演算子 プロシージャへのポインタを取得
VarPtr 関数 変数のポインタを取得
ObjPtr 関数 オブジェクトのポインタを取得
StrPtr 関数 Stringのポインタを取得

参考

Office 2010 Help Files: Win32API_PtrSafe with 64-bit Support
Microsoft® Office 2013 マクロ互換性について

関連記事

カラーダイアログボックスを表示する(MS Office用・Win32API) - Qiita
FormatMessage で DLL 関数のエラーメッセージを取得する - Qiita
[VBA]広域変数を使用せずに、EnumChildWindowsの結果を取得する - Qiita
VBAでTaskDialogを表示してみた - Qiita


  1. 厳密にいえばSet/GetWindowLongPtrなど、64bit専用の記述も存在しますが、ほとんどのAPI関数は同じ記述で32bit/64bit両対応にすることができます。 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
35