ねらい:VBAからWin32 APIを呼んで「できない」を減らす
Win32 APIは、Excel標準では届かない領域(高精度タイマー、ウィンドウ制御、ネイティブのファイル操作、システム情報)に手を伸ばすための入口です。初心者でも安全に使えるように、64ビット対応・Unicode対応・ByVal/ByRef・構造体の扱いを“型”として固定し、貼って動くテンプレと例題でかみ砕いて説明します。
重要ポイントの深掘り
失敗の多くは「型が合っていない」ことが原因です。64ビットでは PtrSafe と LongPtr、文字列は基本的に Unicode(“W”関数)を使い、Stringを直接渡さず StrPtrでアドレスを渡します。構造体(Type)はメモリレイアウトが命なので、定義順・桁(Long/LongPtr)を間違えないことが安定運用の鍵です。
基本の型:64ビット対応とUnicode対応を最初に固める
宣言の型(Declare PtrSafe/LongPtr/LongLong)
標準モジュールに、64ビットと32ビット両対応の宣言を置きます。文字列は “W” 関数(Unicode版)を使い、文字列ポインタで渡すのが安全です。
' ModWin32.bas(共通宣言)
Option Explicit
#If VBA7 Then
' 64/32ビットVBA共通(VBA7)— PtrSafe と LongPtr を使用
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Public Declare PtrSafe Function GetTickCount64 Lib "kernel32" () As LongLong
Public Declare PtrSafe Function MessageBoxW Lib "user32" ( _
ByVal hWnd As LongPtr, _
ByVal lpText As LongPtr, _
ByVal lpCaption As LongPtr, _
ByVal uType As Long) As Long
Public Declare PtrSafe Function GetUserNameW Lib "advapi32" ( _
ByVal lpBuffer As LongPtr, _
ByRef pcbBuffer As Long) As Long
Public Declare PtrSafe Function FindWindowW Lib "user32" ( _
ByVal lpClassName As LongPtr, _
ByVal lpWindowName As LongPtr) As LongPtr
Public Declare PtrSafe Function SetForegroundWindow Lib "user32" ( _
ByVal hWnd As LongPtr) As Long
Public Declare PtrSafe Function ShowWindow Lib "user32" ( _
ByVal hWnd As LongPtr, _
ByVal nCmdShow As Long) As Long
Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Public Declare PtrSafe Function GetWindowRect Lib "user32" ( _
ByVal hWnd As LongPtr, _
ByRef lpRect As RECT) As Long
Public Declare PtrSafe Function CopyFileW Lib "kernel32" ( _
ByVal lpExistingFileName As LongPtr, _
ByVal lpNewFileName As LongPtr, _
ByVal bFailIfExists As Long) As Long
Public Declare PtrSafe Function QueryPerformanceCounter Lib "kernel32" ( _
ByRef lpPerformanceCount As LongLong) As Long
Public Declare PtrSafe Function QueryPerformanceFrequency Lib "kernel32" ( _
ByRef lpFrequency As LongLong) As Long
#Else
' 旧VBA(32ビット)— PtrSafeなし、LongPtrは Long として扱う
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Declare Function GetTickCount Lib "kernel32" () As Long
Public Declare Function MessageBoxW Lib "user32" ( _
ByVal hWnd As Long, _
ByVal lpText As Long, _
ByVal lpCaption As Long, _
ByVal uType As Long) As Long
Public Declare Function GetUserNameW Lib "advapi32" ( _
ByVal lpBuffer As Long, _
ByRef pcbBuffer As Long) As Long
Public Declare Function FindWindowW Lib "user32" ( _
ByVal lpClassName As Long, _
ByVal lpWindowName As Long) As Long
Public Declare Function SetForegroundWindow Lib "user32" ( _
ByVal hWnd As Long) As Long
Public Declare Function ShowWindow Lib "user32" ( _
ByVal hWnd As Long, _
ByVal nCmdShow As Long) As Long
Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Public Declare Function GetWindowRect Lib "user32" ( _
ByVal hWnd As Long, _
ByRef lpRect As RECT) As Long
Public Declare Function CopyFileW Lib "kernel32" ( _
ByVal lpExistingFileName As Long, _
ByVal lpNewFileName As Long, _
ByVal bFailIfExists As Long) As Long
Public Declare Function QueryPerformanceCounter Lib "kernel32" ( _
ByRef lpPerformanceCount As Currency) As Long ' 旧VBAの代替(推奨はVBA7)
Public Declare Function QueryPerformanceFrequency Lib "kernel32" ( _
ByRef lpFrequency As Currency) As Long
#End If
VBUnicodeを安全に扱うためのラッパ(StrPtrで渡す)
“W” 関数は文字列そのものではなく“文字列のアドレス”を受け取ります。VBAの String をそのまま渡さず、StrPtrでアドレスを渡すラッパを用意します。
' ModWin32Helpers.bas(使いやすいラッパ)
Option Explicit
Public Function MsgBoxW(ByVal text As String, ByVal caption As String, Optional ByVal uType As Long = 0) As Long
#If VBA7 Then
MsgBoxW = MessageBoxW(0, StrPtr(text), StrPtr(caption), uType)
#Else
MsgBoxW = MessageBoxW(0, StrPtr(text), StrPtr(caption), uType)
#End If
End Function
Public Function ApiGetUserName() As String
Dim buf As String
Dim size As Long
buf = String$(256, vbNullChar)
size = Len(buf)
#If VBA7 Then
If GetUserNameW(StrPtr(buf), size) <> 0 Then
ApiGetUserName = Left$(buf, size - 1)
End If
#Else
If GetUserNameW(StrPtr(buf), size) <> 0 Then
ApiGetUserName = Left$(buf, size - 1)
End If
#End If
End Function
Public Function CopyFileUnicode(ByVal src As String, ByVal dst As String, Optional ByVal failIfExists As Boolean = False) As Boolean
CopyFileUnicode = (CopyFileW(StrPtr(src), StrPtr(dst), IIf(failIfExists, 1, 0)) <> 0)
End Function
VB即効API例:メッセージ、スリープ、ユーザー名、時刻
メッセージボックス(日本語を安全表示)
Sub Demo_MessageBoxW()
Call MsgBoxW("処理が完了しました。", "お知らせ", 0)
End Sub
VBスリープ(UIを固めずに短時間待つ)
Sub Demo_Sleep()
Application.StatusBar = "3秒待機中..."
Sleep 3000
Application.StatusBar = False
End Sub
VBWindowsログオン名の取得
Sub Demo_GetUserName()
Dim name As String
name = ApiGetUserName()
If Len(name) = 0 Then
Call MsgBoxW("取得に失敗しました。", "ユーザー名")
Else
Call MsgBoxW("ユーザー名: " & name, "ユーザー名")
End If
End Sub
VB高精度経過時間の測定(QueryPerformanceCounter)
Sub Demo_HighPrecisionTimer()
Dim freq As LongLong, start As LongLong, finish As LongLong
QueryPerformanceFrequency freq
QueryPerformanceCounter start
' ここで重い処理を実行
Sleep 500
QueryPerformanceCounter finish
Dim ms As Double
ms = (finish - start) * 1000# / freq
Debug.Print "Elapsed(ms): "; Format(ms, "0.000")
End Sub
VBウィンドウ制御:他アプリを前面化・状態変更・位置取得
タイトルからウィンドウハンドルを取得して前面に出す
Sub Demo_Foreground_Notepad()
Dim hWnd As LongPtr
hWnd = FindWindowW(0, StrPtr("メモ帳")) ' タイトルが完全一致する例(日本語OS)
If hWnd <> 0 Then
ShowWindow hWnd, 9 ' SW_RESTORE=9(最小化解除して前面化しやすく)
SetForegroundWindow hWnd
Else
Call MsgBoxW("対象ウィンドウが見つかりません。", "前面化")
End If
End Sub
VBウィンドウ矩形(位置・サイズ)を取得する
Sub Demo_GetWindowRect()
Dim hWnd As LongPtr, rc As RECT
hWnd = FindWindowW(0, StrPtr("メモ帳"))
If hWnd = 0 Then Call MsgBoxW("見つかりません。", "位置取得"): Exit Sub
If GetWindowRect(hWnd, rc) <> 0 Then
Debug.Print "L=" & rc.Left & " T=" & rc.Top & " R=" & rc.Right & " B=" & rc.Bottom
End If
End Sub
VBファイルAPI:ネイティブの高速コピー/上書き制御
Unicodeファイル名を安全にコピー(上書き可/不可)
Sub Demo_CopyFileW()
Dim ok As Boolean
ok = CopyFileUnicode(ThisWorkbook.Path & "\入力.csv", ThisWorkbook.Path & "\バックアップ.csv", False)
Call MsgBoxW(IIf(ok, "コピー成功", "コピー失敗"), "CopyFileW")
End Sub
VB運用の重要ポイント(深掘り)
64ビット前提にする
最近のExcelは64ビットが主流です。Declareは必ず PtrSafe、ポインタは LongPtr に統一します。古いサンプル(Long/Any)はそのまま使わず、変換してから貼りましょう。
Unicode前提にする
WindowsはUnicodeが標準です。“A”関数(ANSI版)は日本語が化けます。“W”関数+StrPtrで渡す形に揃えれば、国際化・日本語双方で安定します。
ByVal/ByRefと構造体の整合
APIの引数は“呼び方の約束”です。ByVal(値渡し)をByRef(参照渡し)で宣言してしまうとクラッシュ要因になります。構造体(Type)は公式の並びと型に一致させ、パディングの違いが出ないようにします。
例外でも必ず戻す枠を徹底
APIはExcelの保護外に出ます。失敗してもExcelの環境が戻るよう、開始・終了枠(描画・イベント・再計算)を挟み、MsgBoxWなどUI出力も“短く・安全に”行います。
例題の通し方と確認ポイント
最初にラッパ関数を導入してから動作確認する
- MsgBoxW/ApiGetUserName/CopyFileUnicode を貼り、個別に実行して日本語・Unicodeが正しく扱えるかを確認します。
- Sleep/QueryPerformanceCounter で待機と高精度計測を試し、Debug.Printの出力で値が妥当か確認します。
ウィンドウ操作は対象アプリを準備してから試す
- メモ帳を開き、タイトル一致の前面化/位置取得が成功するかを確認します。日本語タイトルは環境依存なので、実行PCの表示に合わせて文字列を調整します。
まとめ:型を固めれば、Win32 APIは“頼れる味方”になる
PtrSafe/LongPtr/Unicode“W”関数/StrPtrのセットを標準にし、構造体とByVal/ByRefの整合を守れば、Win32 APIはExcelを一段深く拡張してくれます。まずはメッセージ・スリープ・ユーザー名・ファイルコピー・ウィンドウ前面化の5本を自分の環境で動かし、手応えを掴みましょう。
