Access VBAのDeclare Functionが64bitで動かない|PtrSafeとLongPtrで直す完全ガイド

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を機械的に見直します。

  1. 全モジュールを検索し、Declareを列挙。
  2. PtrSafeを付与。
  3. ポインタ/ハンドルをLongPtrへ。単なる数値(サイズ、フラグ、戻り値がDWORD等)は通常LongのままでOK。
  4. 条件コンパイルで32/64両対応に(#If VBA7、必要に応じて#If Win64)。
  5. コンパイル→実行テストで最終確認。

最小例(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 などハンドル(ポインタ)LongLongPtr最重要。すべてLongPtrへ。
LPARAM / WPARAM / LRESULTポインタ互換整数LongLongPtrメッセージAPIで頻出。
INT / UINT / DWORD32ビット整数LongLong長さ32bit固定。Longで保持。
BOOLブール(4バイト)LongLongVBAのBoolean(2バイト)ではなくLongが安全。
SIZE_T / ULONG_PTRポインタサイズ整数LongLongPtrバッファサイズ等に使われる。
LONG_PTRポインタサイズの符号付きLongLongPtrGet/SetWindowLongPtrなど。
LONG / DWORD3232ビット整数LongLong 
LONG64 / LONGLONG64ビット整数(未対応)LongLongLongLongはWin64のみ。32bit側には書かない。
LPCSTR / LPSTRANSI文字列(ポインタ)ByVal As StringByVal As String宣言で ...A を呼ぶ(例:FindWindowA)。
LPCWSTR / LPWSTRUnicode文字列(ポインタ)ByVal As StringByVal 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は読み込めません。

プロジェクト全体の移行手順(実務向け)

  1. Declare列挙:テキスト検索でDeclare Function/Declare Subを洗い出す。
  2. 宣言の更新:PtrSafe付与、ポインタ・ハンドルをLongPtrへ。必要に応じて*PtrAPIへ差し替え。
  3. 条件コンパイル適用:#If VBA7・#If Win64のテンプレートを適用し、共存可能に。
  4. コンパイル:デバッグ → VBAProject をコンパイルで残エラーを消し込む。
  5. 実行テスト:主要フォームやレポートで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で実行してください。

「この順で直す」と失敗しない:実務フローの再掲

  1. 全Declareを抽出。
  2. PtrSafe付与、ポインタ関連をLongPtrへ。
  3. 必要な箇所を*PtrAPIへ差し替え。
  4. 条件コンパイルテンプレートを適用。
  5. コンパイル&実機テストで詰める。

まとめ

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は使わない」など)を決め、標準モジュールにテンプレートを置くと、今後の保守が飛躍的に楽になります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次