VBAでWin32APIを使うには

# 概要

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

# 基本的な構文

https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/declare-statement

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

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

# VBAのバージョンの確認

実行するVBAのバージョンによっては使用できない記述があるため、どのバージョンで実行するのかを確認する必要があります。
![VBAVer.PNG](https://qiita-image-store.s3.amazonaws.com/0/142842/5bffd2d6-099f-d7d5-14b5-7cd190e4031c.png)

![VBA7.1.PNG](https://qiita-image-store.s3.amazonaws.com/0/142842/106a0394-8be2-4183-64c3-97c19058ad92.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

Microsoft Officeでは2010以降が該当します（2017/1/8現在）。
32bit版と64bit版が存在します。

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

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


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

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

> [Office 2010 Help Files: Win32API_PtrSafe with 64-bit Support](https://www.microsoft.com/en-us/download/details.aspx?id=9970)

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](https://msdn.microsoft.com/ja-jp/library/cc411211.aspx?f=255&MSPPError=-2147217396)関数の引数の`nCmdShow`はVBA組み込み列挙型の`VbAppWinStyle`とほぼ同じものです。

## 省略可能な引数にする

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

## サンプル

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

マウスの挙動を再現するには[mouse_event関数](https://msdn.microsoft.com/ja-jp/library/cc410921.aspx?f=255&MSPPError=-2147217396)を使用するのが手軽です。
リファレンスにはmouse_event関数は以下のように定義されています（横長になるため改行を追加しています）。

```vb
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)
```

また、動作を示す定数として以下が定義されています。
```vb
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です。

### べた書き

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

```vb:べた書き
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にしておきます。

```vb:修正
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 String`を`ByVal 引数 As LongPtr`に書き換え、呼び出し時は`StrPtr(文字列)`を渡すようにすれば、多くの場合は動作するはずです。

## ポインタ関連関数

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


# 参考

[Office 2010 Help Files: Win32API_PtrSafe with 64-bit Support](https://www.microsoft.com/en-us/download/details.aspx?id=9970 "Office 2010 Help Files: Win32API_PtrSafe with 64-bit Support")

[Microsoft® Office 2013 マクロ互換性について](https://download.microsoft.com/download/8/5/C/85CA5B92-0829-4966-AECE-C7B4FB2EDE07/Office2013_compatibility_for_macro.pdf "Microsoft® Office 2013 マクロ互換性について")

---

https://qiita.com/nekotadon/items/f376d17de85dfb84fbd5#win32api%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

C#に関する記事ですが、Win32APIに関する内容が一通りまとめられており、一度目を通しておいて損はない内容です。
[型](https://qiita.com/nekotadon/items/f376d17de85dfb84fbd5#%E5%9E%8B)の箇所はVBAで記述する際にも参考になると思います。


# 関連記事


https://qiita.com/nukie_53/items/92cfb6bd08652e4fdbc1

https://qiita.com/nukie_53/items/745d3b15c45cd3885811

https://qiita.com/nukie_53/items/d4a803080d4c1f3ab35d

https://qiita.com/nukie_53/items/8642f74ba4902e941ff2

https://qiita.com/nukie_53/items/39c28d000d521329548b