Accessの既存ACCDBを64ビット版で開いた途端、Declare Functionを含むモジュールがコンパイルエラーで止まる――その主因はVBA7(Office 2010以降)のポインタ仕様とWin64 APIの差異にあります。本記事では、PtrSafe/LongPtrを用いた正しい宣言への書き換え、型の見直し、条件コンパイル、動作検証、そしてつまづきやすいAPI個別対策までを一気通貫で解説。現場でそのまま使えるレシピとチェックリストを提供します。
Access VBAで「Declare Functionが機能しない」問題の正体
32ビット版で作成されたVBAモジュールは、多くの場合、Win32時代の宣言(Declare Function / Declare Sub、戻り値や引数にLongを多用)を前提にします。64ビット版Accessでは、以下が変わります。
- PtrSafe属性の必須化:VBA7で64ビット版Office上では、外部関数宣言に
PtrSafeが必須。 - ポインタ幅の拡張:ハンドルやポインタは32→64ビットへ拡張。VBA側では
LongPtrで受ける必要があります。 - 一部API名の差し替え:
GetWindowLong/SetWindowLongは64ビットではGetWindowLongPtr/SetWindowLongPtrへ移行。
発生しやすいエラー例:
- コンパイル エラー: Declare ステートメントを 64 ビットに対応させるには PtrSafe 属性が必要です
- コンパイル エラー: このプロジェクトでは 64 ビット バージョンの Office に対して更新された宣言が必要です
最短解決:PtrSafeとLongPtrで32/64ビット共存宣言にする
まずはベースの移行手順です。既存コードのDeclare Function/Declare Subを機械的に見直します。
- 全モジュールを検索し、
Declareを列挙。 PtrSafeを付与。- ポインタ/ハンドルを
LongPtrへ。単なる数値(サイズ、フラグ、戻り値がDWORD等)は通常LongのままでOK。 - 条件コンパイルで32/64両対応に(
#If VBA7、必要に応じて#If Win64)。 - コンパイル→実行テストで最終確認。
最小例(GetTickCount)
#If VBA7 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
#Else
Private Declare Function GetTickCount Lib "kernel32" () As Long
#End If
GetTickCountの戻り値はDWORDでサイズは32ビット固定なので、64ビットでもLongのままで問題ありません。逆にハンドルやポインタはLongPtrに必ず置換します。
64ビット移行の「型マッピング表」
Windows APIの引数や戻り値を、VBAの型に正しく対応付けることが重要です。
| Windows型 | 意味 | VBA (32bit) | VBA (64bit) | 備考 |
|---|---|---|---|---|
| HWND / HANDLE / HDC など | ハンドル(ポインタ) | Long | LongPtr | 最重要。すべてLongPtrへ。 |
| LPARAM / WPARAM / LRESULT | ポインタ互換整数 | Long | LongPtr | メッセージAPIで頻出。 |
| INT / UINT / DWORD | 32ビット整数 | Long | Long | 長さ32bit固定。Longで保持。 |
| BOOL | ブール(4バイト) | Long | Long | VBAのBoolean(2バイト)ではなくLongが安全。 |
| SIZE_T / ULONG_PTR | ポインタサイズ整数 | Long | LongPtr | バッファサイズ等に使われる。 |
| LONG_PTR | ポインタサイズの符号付き | Long | LongPtr | Get/SetWindowLongPtrなど。 |
| LONG / DWORD32 | 32ビット整数 | Long | Long | |
| LONG64 / LONGLONG | 64ビット整数 | (未対応) | LongLong | LongLongはWin64のみ。32bit側には書かない。 |
| LPCSTR / LPSTR | ANSI文字列(ポインタ) | ByVal As String | ByVal As String | 宣言で ...A を呼ぶ(例:FindWindowA)。 |
| LPCWSTR / LPWSTR | Unicode文字列(ポインタ) | ByVal As String | ByVal As String | 宣言で ...W を呼ぶ(例:FindWindowW)。 |
代表APIの「32/64ビット対比レシピ」
SendMessage(wParam/lParamが落とし穴)
' 64bit
#If VBA7 Then
Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageW" ( _
ByVal hWnd As LongPtr, ByVal wMsg As Long, _
ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
#Else
' 32bit
Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" ( _
ByVal hWnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
#End If
wParam/lParam/戻り値はポインタ幅に依存するため、64ビットではLongPtrに統一します。
GetWindowLong/SetWindowLong → GetWindowLongPtr/SetWindowLongPtr
' 64bit
#If VBA7 Then
Private Declare PtrSafe Function GetWindowLongPtr Lib "user32" Alias "GetWindowLongPtrW" ( _
ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
Private Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrW" ( _
ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
' 32bit
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongW" ( _
ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongW" ( _
ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If
64ビットではAPI名自体が*Ptrに変わる点が重要です。引数・戻り値はLongPtrで受けます。
CopyMemory(RtlMoveMemory)
' 64bit
#If VBA7 Then
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As LongPtr)
#Else
' 32bit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
#End If
LengthはSIZE_T相当であり、64ビット環境ではLongPtrが安全です。
ShellExecute
#If VBA7 Then
Private Declare PtrSafe Function ShellExecute Lib "shell32" Alias "ShellExecuteW" ( _
ByVal hWnd As LongPtr, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As LongPtr
#Else
Private Declare Function ShellExecute Lib "shell32" Alias "ShellExecuteW" ( _
ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
#End If
LoadLibrary / GetProcAddress(動的ロード時の典型)
#If VBA7 Then
Private Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryW" ( _
ByVal lpLibFileName As String) As LongPtr
Private Declare PtrSafe Function GetProcAddress Lib "kernel32" ( _
ByVal hModule As LongPtr, ByVal lpProcName As String) As LongPtr
#Else
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryW" ( _
ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" ( _
ByVal hModule As Long, ByVal lpProcName As String) As Long
#End If
戻り値・ハンドルはLongPtr。ANSI関数名を使う場合は...AをAliasに指定します。
現場で使える「条件コンパイル」テンプレート
最初に標準モジュールの先頭へ置くと移行が楽になります。
Option Compare Database
Option Explicit
#If VBA7 Then
#If Win64 Then
Public Const PTR_BYTES As Long = 8
#Else
Public Const PTR_BYTES As Long = 4
#End If
#Else
Public Const PTR_BYTES As Long = 4
#End If
#If VBA7 ThenでVBA7かどうか、#If Win64 Thenでビット数を切り替えます。64ビット専用のLongLong等を使う宣言は、必ず#If Win64ブロックに閉じ込めましょう。
ビット数(bitness)の確認と参照設定の見直し
- Accessのビット数を確認:ファイル → アカウント → バージョン情報(Access のバージョン情報)から、32ビット/64ビット表示をチェック。
- VBAの参照設定:VBEのツール → 参照設定で、外部ライブラリ(自作DLL/他人のモジュールなど)が64ビット対応版かを確認。32ビット専用DLLは読み込めません。
プロジェクト全体の移行手順(実務向け)
- Declare列挙:テキスト検索で
Declare Function/Declare Subを洗い出す。 - 宣言の更新:
PtrSafe付与、ポインタ・ハンドルをLongPtrへ。必要に応じて*PtrAPIへ差し替え。 - 条件コンパイル適用:
#If VBA7・#If Win64のテンプレートを適用し、共存可能に。 - コンパイル:デバッグ → VBAProject をコンパイルで残エラーを消し込む。
- 実行テスト:主要フォームやレポートでAPI呼び出し箇所を網羅テスト。
Declare箇所の抽出サンプル(即使えるツール)
Public Sub ListDeclareLines()
Dim cm As CodeModule, i As Long
For Each cm In Application.VBE.VBProjects(1).VBComponents
With cm.CodeModule
For i = 1 To .CountOfLines
Dim s As String
s = .Lines(i, 1)
If InStr(1, s, "Declare ", vbTextCompare) > 0 Then
Debug.Print cm.Name & ":" & i & " " & s
End If
Next
End With
Next
End Sub
イミディエイト(Ctrl+G)で実行すれば、見直し対象を一覧化できます。
よくあるエラーと対処表
| 症状 | 原因 | 対処 |
|---|---|---|
| PtrSafeが必要と表示 | VBA7/64bitでPtrSafe未指定 | Declare文にPtrSafeを追加 |
| 型が一致しない/オーバーフロー | ハンドルやポインタをLongのまま使用 | LongPtrへ変更 |
| エントリポイントが見つからない | Win64でGetWindowLong等を使用 | GetWindowLongPtr等の*Ptr版を宣言 |
| 呼び出し先DLLをロードできない | 32ビット専用DLLを64ビットAccessで参照 | 64ビット版DLLへ置換(または32ビットOfficeで実行) |
| 文字化け/想定外の結果 | A/W(ANSI/Unicode)関数の不一致 | ...A/...Wと引数のString宣言を揃える |
コンパイルでLongLongが未定義 | 32ビット側にLongLongが露出 | #If Win64内に限定 |
| スペルミスによる読み込み失敗 | ライブラリ名/関数名の誤記(例:kerne132) | 綴りを正確に修正(例:kernel32) |
実践例:EnumWindowsコールバックを64ビット対応
コールバック(AddressOf)を伴うAPIは、宣言だけでなくデリゲート側のシグネチャ(引数型)にもLongPtrを反映します。
' 宣言部
#If VBA7 Then
Private Declare PtrSafe Function EnumWindows Lib "user32" ( _
ByVal lpEnumFunc As LongPtr, ByVal lParam As LongPtr) As Long
#Else
Private Declare Function EnumWindows Lib "user32" ( _
ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
#End If
' コールバックのシグネチャ(戻りLong、引数はハンドルとユーザーデータ)
Public Function EnumProc(ByVal hWnd As LongPtr, ByVal lParam As LongPtr) As Long
Debug.Print "hWnd=" & hWnd
EnumProc = 1 ' TRUEで列挙継続
End Function
' 呼び出し側
Public Sub TestEnum()
#If VBA7 Then
Call EnumWindows(AddressOf EnumProc, 0&)
#Else
Call EnumWindows(AddressOf EnumProc32, 0&)
#End If
End Sub
' 32ビット用の別シグネチャ
#If Not VBA7 Then
Public Function EnumProc32(ByVal hWnd As Long, ByVal lParam As Long) As Long
Debug.Print "hWnd=" & hWnd
EnumProc32 = 1
End Function
#End If
64ビット時は、コールバックの引数もLongPtrに合わせるのが肝です。
API移行の「前後比較」サンプル集
| 用途 | 旧(32bit前提) | 新(両対応) |
|---|---|---|
| ウィンドウハンドル取得 | Declare Function FindWindow Lib "user32" _ Alias "FindWindowA" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long | #If VBA7 Then Declare PtrSafe Function FindWindow Lib "user32" _ Alias "FindWindowW" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As LongPtr #Else Declare Function FindWindow Lib "user32" _ Alias "FindWindowW" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long #End If |
| プロシージャ置換 | Declare Function SetWindowLong Lib "user32" _ Alias "SetWindowLongW" (ByVal hWnd As Long, _ ByVal nIndex As Long, ByVal dwNewLong As Long) As Long | #If VBA7 Then Declare PtrSafe Function SetWindowLongPtr Lib "user32" _ Alias "SetWindowLongPtrW" (ByVal hWnd As LongPtr, _ ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr #Else Declare Function SetWindowLong Lib "user32" _ Alias "SetWindowLongW" (ByVal hWnd As Long, _ ByVal nIndex As Long, ByVal dwNewLong As Long) As Long #End If |
| メッセージ送信 | Declare Function SendMessage Lib "user32" _ Alias "SendMessageW" (ByVal hWnd As Long, _ ByVal wMsg As Long, ByVal wParam As Long, _ ByVal lParam As Long) As Long | #If VBA7 Then Declare PtrSafe Function SendMessage Lib "user32" _ Alias "SendMessageW" (ByVal hWnd As LongPtr, _ ByVal wMsg As Long, ByVal wParam As LongPtr, _ ByVal lParam As LongPtr) As LongPtr #Else Declare Function SendMessage Lib "user32" _ Alias "SendMessageW" (ByVal hWnd As Long, _ ByVal wMsg As Long, ByVal wParam As Long, _ ByVal lParam As Long) As Long #End If |
補足:32ビットのまま使える型/注意が必要な型
- 32ビットのままでOK:
DWORD、UINT、INT、BOOL、各種フラグ列挙体。 - 要見直し:ハンドル(
HWND、HANDLE等)、LPARAM、WPARAM、LRESULT、SIZE_T、ULONG_PTR。 - 文字列:
Stringで渡す場合、宣言のA(ANSI)/W(Unicode)を適切に。VBAは内部UnicodeなのでWの利用が自然です。
実運用でのチェックリスト
- 全
DeclareにPtrSafeを付けたか。 - ハンドル/ポインタ/戻り値を
LongPtrへ置換したか。 Get/SetWindowLongをGet/SetWindowLongPtrへ差し替えたか。SendMessage系のwParam/lParam/戻り値をLongPtrにしたか。#If VBA7・#If Win64で32/64共存を確保したか。- 外部DLLが64ビット版か(またはプロセス境界を跨がずに呼べるか)。
- スペル/エイリアス(
Alias)の整合性を確認したか。
現場TIPS:安全なヘルパーとパターン
- ポインタと数値の相互変換:表示/ログ用途でのみ行い、演算は避ける。例:
Public Function PtrToText(ByVal p As LongPtr) As String #If Win64 Then PtrToText = "&H" & Hex$(p) #Else PtrToText = CStr(p) #End If End Function - 構造体(Type)のポインタ欄:ハンドル/ポインタフィールドは
LongPtrで定義。サイズが合わないとクラッシュの原因に。 - コールバック関数:引数にハンドルや
LPARAMがある場合はLongPtrへ。戻り値も仕様に合わせる。
既存プロジェクトを崩さないための戦略
移行は「小さく切って確実に」行うのが鉄則です。まずAPI宣言だけを直し、実体コード(フォーム・レポートのイベント)には手を入れない方針でコンパイルを通し、次に実行テストで問題箇所を特定。外部DLLが32ビットしかない場合、Officeのビット数を32ビットへ合わせるという選択肢も現実解です(ただし将来的な64ビット移行を見据え、DLLの64ビット化または別手段への置換も検討)。
よくある質問(FAQ)
Q. すべてLongPtrにすれば安全?
A. いいえ。DWORD等の32ビット固定整数はLongのままにしないと、呼び出し規約や構造体のメモリレイアウトが崩れる恐れがあります。
Q. As Anyは使い続けて良い?
A. コンパイルは通りますが型安全性を損ねます。具体の型(LongPtrやStringなど)に絞るのが推奨です。
Q. 64ビットでLongLongを使いたい。32ビットでコンパイルエラーに。
A. LongLongは64ビット専用です。#If Win64 Thenブロック内に閉じ込めてください。
Q. すべて直したのに動かない。
A. 外部DLL本体が32ビットのままの可能性。64ビットAccessからは読み込めません。DLL自体の64ビット化、もしくは32ビットOfficeで実行してください。
「この順で直す」と失敗しない:実務フローの再掲
- 全
Declareを抽出。 PtrSafe付与、ポインタ関連をLongPtrへ。- 必要な箇所を
*PtrAPIへ差し替え。 - 条件コンパイルテンプレートを適用。
- コンパイル&実機テストで詰める。
まとめ
Access VBAでDeclare Functionが64ビット環境で通らない原因の大半は、宣言と型の不一致です。PtrSafeの付与、ポインタ類のLongPtr化、一部APIの*Ptr版への置換、そして条件コンパイルを徹底すれば、同一ACCDBを32/64ビットのどちらでも堅実に動かせます。記事のレシピとチェックリストをそのまま適用し、まずはプロジェクトを「コンパイルが通る」状態に整えることから始めましょう。
補足情報(参照・学習のガイド)
- Microsoft公式ドキュメント:64ビットOffice移行時のAPI宣言ガイド。
- 解説動画・ブログ:“Access 64‑bit API migration”等で実例を俯瞰。
- VBA7環境では64ビット対応が原則。
#If VBA7がTrueになるかを確認。 - スペルミスや
Alias不整合も頻出の原因。細部まで検査。
最後に:プロジェクト横断で規約(「ハンドル・ポインタは必ずLongPtr」「Get/SetWindowLongは使わない」など)を決め、標準モジュールにテンプレートを置くと、今後の保守が飛躍的に楽になります。

コメント