業務システム開発・保守の実務メモを発信中

VBAのDeclareが64bit版Excelでエラーになる原因と修正方法|PtrSafe・LongPtrを実例で解説

32bit版Excelで動いていたVBAを64bit版Excelで開くと、Declare文で「このプロジェクトのコードは、64ビットシステムで使用するために更新する必要があります」と表示されることがあります。

原因は、Windows APIを呼び出す古いDeclare文です。

修正の出発点はPtrSafeですが、付けるだけでは終わりません。ハンドルやポインターを受ける戻り値・引数は、何を表す値かを確認してLongPtrへ直す必要があります。Longを一括でLongPtrへ置換するのも誤りです。

この記事では、64bit版ExcelでDeclare文のコンパイルエラーが出る原因と、PtrSafe・LongPtrを使った修正方法を説明します。PtrSafeを付けるだけでは不十分な理由も、GetDesktopWindowを使った検証例で確認します。

目次

VBAで「64ビットシステム用に更新する必要があります」エラーが発生する原因

64bit版OfficeでVBAProjectをコンパイルすると、PtrSafeのないDeclare文はコンパイル対象になりません。Windowsが64bitかどうかではなく、VBAを実行するOfficeが64bit版かどうかが基準です。

今回の検証環境は、Microsoft 365の64bit版Excel、VBE 7.01です。PtrSafeなしの独自ケースをVBEでコンパイルすると、以下のようなコンパイルエラーのメッセージが表示されます。

エラーが発生するDeclare文

再現用に、デスクトップウィンドウのハンドルを返すGetDesktopWindowを使います。

GetDesktopWindow functionでは、戻り値がHWNDであることを確認できます。

VB
Option Explicit

Private Declare Function GetDesktopWindow Lib "user32" () As Long

Public Sub TestBeforePtrSafe()
    Dim desktopHandle As Long

    desktopHandle = GetDesktopWindow()
    Debug.Print "Desktop HWND: "; desktopHandle
End Sub

見てわかるように、この宣言にはPtrSafeがありません。64bit版Excelでは、マクロを実行する前にコンパイルで止まります。したがって、まず確認するべきなのは「実行時エラー」ではなく、VBEのデバッグからVBAProjectのコンパイルを実行した結果です。

Windowsが64bitだからではなくOfficeが64bitかどうかが重要

64bit版Windowsでも、32bit版Officeを使っていることがあるので、古いDeclareの修正要否を判断するときは、Windowsのbit数ではなく、対象ブックを開くExcelのbit数を確認します

Excelが32bit版か64bit版か確認する

Excelのbit数は、ファイルアカウントExcelのバージョン情報の順に開くと確認できます。表示されるダイアログに「64 ビット」とあれば、この記事で扱うDeclare修正の対象です。

ここで64bit版と分かった後に、VBEでDeclare文を調べます。

Officeが32bit版なら、PtrSafeなしの古いDeclareが直ちに同じコンパイルエラーを起こすわけではありません。ただし、将来64bit版Officeで開く可能性があるなら、移行対象として洗い出しておく価値はあります

32bit用のDeclareを64bit版Excelでコンパイルしてエラーを再現する

エラー画面だけを見ると、PtrSafeを付ければ終わりに見えます。まずは、どの操作で止まるのかを確認します。

VBAProjectをコンパイルする

Excelで対象ブックを開き、Alt + F11でVBEを開きます。メニューのデバッグからVBAProjectのコンパイルを選びます。

マクロの実行ボタンを押す前にコンパイルする理由は、PtrSafeなしの問題が実行時の失敗ではなく、宣言のコンパイルエラーだからです。ここで止まるなら、業務処理の入力値やシート内容を調べ始める段階ではありません。

Declare文でコンパイルエラーになることを確認する

PtrSafeなしのモジュールでは、Declare行が選択された状態でコンパイルが止まります。これは、64bit版Officeに古い32bit向けDeclareを残したときの入口の症状です。

この段階では、まずはPtrSafeなしのDeclareをすべて洗い出します。コンパイルが通らないプロジェクトでは、個別のAPI呼び出し結果を確認できません。

エラーを消した後も作業は続きます。次は、PtrSafeだけを付けた状態がどこまで確認できるかを見ます。

DeclareにPtrSafeを追加してみる

旧DeclareへPtrSafeを追加すると、64bit版Officeに対応する宣言であることをコンパイラへ示せます。

VB
Option Explicit

Private Declare PtrSafe Function GetDesktopWindow Lib "user32" () As Long

Public Sub TestDesktopWindowPtrSafeOnly()
    Dim desktopHandle As Long

    desktopHandle = GetDesktopWindow()
    ThisWorkbook.Worksheets("Results").Range("B2").Value = CStr(desktopHandle)
    Debug.Print "Desktop HWND: "; desktopHandle
End Sub

PtrSafeを追加するとコンパイルエラーは消える

この宣言を含むブックは、64bit版Excelでコンパイルでき、テストマクロも実行できました。今回の環境では、結果シートとイミディエイトウィンドウに65548が出力されました。

ただしPtrSafeを付けただけでは64bit対応とは限らない

今回の戻り値65548はLongに収まる値でした。そのため、PtrSafeだけのブックでも実行結果だけを見ると問題がないように見えます。しかし、これはLongでハンドルを受けてよいことの証明ではありません。

GetDesktopWindowの戻り値はウィンドウハンドルです。PtrSafeだけは「このDeclareは64bitを想定している」という宣言であり、値を格納する型そのものを直すものではありません。

PtrSafe keyword (VBA)では、PtrSafeの追加後に戻り値と引数の型を確認する必要があるとしています。

PtrSafeとLongPtrの違い

PtrSafeとLongPtrは、似た位置に現れるため混同しやすいものです。役割を分けると、修正範囲を判断しやすくなります。

PtrSafeはデータ型ではない

まず、PtrSafeはDeclare文に付けるキーワードです。64bit版Officeで使うことを前提にしたDeclareであると明示します。

そのため、PtrSafeを付けても、戻り値や引数、呼び出し側の変数の型は自動で変わりません。旧Declareのコンパイルエラーを解消する役割と、値を正しい幅で持つ役割は別です。

LongPtrはポインター・ハンドル用の型

LongPtrは、32bit版OfficeではLong、64bit版OfficeではLongLong相当として扱われる型エイリアスです。ウィンドウハンドル、ファイルハンドル、メモリポインターのように、API定義でポインターサイズの値として扱うものに使います。

今回のGetDesktopWindowはHWNDを返すため、戻り値と受け側の変数をLongPtrにします。

LongLongとの違い

LongLongは64bit整数そのものを扱う型です。ポインターやハンドルだからLongLongを選ぶのではありません。

API定義が「64bitの整数値」を要求しているならLongLong、ポインターやハンドルを表すならLongPtrです。LongPtrはOfficeのbit数に合わせて幅が変わるため、32bit版と64bit版の両方で同じ意味のハンドルを扱うときに使えます。

正しくDeclareを64bit対応する

ここまでの比較を、同じAPIで3つのパターンとして並べて比較してみます。

VB
' Before: 64bit版Officeでコンパイルエラー
Private Declare Function GetDesktopWindow Lib "user32" () As Long

' Incomplete: コンパイルは通るが、HWNDをLongで宣言している
Private Declare PtrSafe Function GetDesktopWindow Lib "user32" () As Long

' After: HWNDをLongPtrで宣言する
Private Declare PtrSafe Function GetDesktopWindow Lib "user32" () As LongPtr

比較の焦点は、PtrSafeを付けたかどうかだけではありません。APIが返す値がHWNDであることを確認し、その値を受ける型をLongPtrへそろえることです。

呼び出し側の変数もLongPtrに変更する

Declareの戻り値だけLongPtrにしても、呼び出し側でLongへ受ければ型の整合性が崩れます。戻り値の意味に合わせて、変数もLongPtrへ変更します。

VB
Option Explicit

Private Declare PtrSafe Function GetDesktopWindow Lib "user32" () As LongPtr

Public Function DesktopWindowHandleForVerification() As LongPtr
    DesktopWindowHandleForVerification = GetDesktopWindow()
End Function

Public Sub TestDesktopWindowLongPtr()
    Dim desktopHandle As LongPtr  'Long から LongPtrに変更

    desktopHandle = DesktopWindowHandleForVerification()
    ThisWorkbook.Worksheets("Results").Range("B2").Value = CStr(desktopHandle)
    Debug.Print "Desktop HWND: "; desktopHandle
End Sub

この例でLongPtrに変更したのは、GetDesktopWindowが返すウィンドウハンドルと、それを受け取る関数・変数だけです。次に、Longのまま残す値との違いを整理します。

Longは全部LongPtrに変更すればよいわけではない

Longを見つけるたびにLongPtrへ置換すると、API定義の意味を壊します。型はbit数ではなく、その引数・戻り値が何を表すかで判断します。

元のAPI型・意味VBA 64bitで検討する型判断
HWND、HANDLELongPtrハンドルはポインターサイズで扱う
ポインターLongPtrアドレスを格納する値として扱う
LPARAM、WPARAMAPI定義を確認値の意味とWindows APIの定義で決める
DWORD、BOOL多くの場合Long通常の32bit整数として扱う
通常の32bit整数LongLongPtrへ変更しない
64bit整数そのものLongLongポインター・ハンドルとは区別する

ポインター・ハンドルならLongPtr

ウィンドウハンドル、ファイルハンドル、メモリ位置のように、APIがポインターサイズの値として扱うものはLongPtrです。Declareの戻り値だけでなく、引数、UDTのメンバー、呼び出し側の変数まで追います。

通常の32bit整数ならLongのまま

待機時間、フラグ、BOOL、DWORDのような通常の32bit整数は、64bit版OfficeでもLongのままです。

64bit整数ならLongLong

LongLongは64bit版Officeで使う8バイトの整数型です。大きな整数値を扱うための型であり、ハンドルやポインターを表すための一般的な置換先ではありません。

tomo

型が分からない場合は、VBAの変数名ではなく、Windows APIの引数・戻り値の型定義を確認したほうが良いです。ここを確認せずに一括置換すると、コンパイルが通っても後から原因を追いにくいコードになります。

32bit版と64bit版の両方で動くVBAにする方法

Office 2010以降のVBA7環境で、32bit版と64bit版の両方を想定するなら、Declareを条件付きコンパイルで分けられます。

VB
Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function GetDesktopWindow Lib "user32" () As LongPtr
#Else
    Private Declare Function GetDesktopWindow Lib "user32" () As Long
#End If

今回の64bit版Excelでは、VBA7側が選ばれ、LongPtr対応の宣言でコンパイル・実行できました。

VBA7とWin64は意味が違う

VBA7はVBAのバージョンを判断する定数です。Office 2010以降のVBA7環境かどうかを分けます。

Win64は、実行しているOfficeが64bit版かどうかを分けます。Windowsが64bitであっても32bit版OfficeならWin64側にはなりません。

Declareの互換宣言を分けるだけなら、まず#If VBA7 ThenでPtrSafeを使える環境かを分けます。64bit版だけ別の処理が必要な場合に、内側でWin64を使うかを検討します。

Office 2010以降の32bit版・64bit版の両対応では、ハンドルとポインターにLongPtrを使うことが基本です。Win64を付ければ何でも解決するわけではありません。

PtrSafeを付けてもエラー・クラッシュする場合

コンパイルエラーが消えても、Declare周辺の型が揃っていなければ別の不具合が残ります。症状ごとに確認場所を分けます。

戻り値のLongをLongPtrに変更していない

APIがハンドルやポインターを返すのに、Declareの戻り値をLongのままにしているケースです。今回のPtrSafeだけの実験は、この状態を確認するために残しています。

引数内のハンドル・ポインターがLongのまま

戻り値を直しても、引数でHWNDやポインターをLongのまま渡していれば不完全です。Declareの1行だけを見るのではなく、各引数の意味を確認します。

User Defined Type内にLongが残っている

UDTにポインターやハンドルを持つメンバーがあれば、LongPtrへの変更対象です。

Declareだけ修正して呼び出し側変数を変更していない

DeclareをLongPtrへ直しても、受け側がLongなら値を正しい幅で扱えません。関数の戻り値、代入先、ByRef引数、UDTまで経路を追います。

APIの宣言自体が間違っている

DLL名、Alias、引数の順番、ByValとByRefがAPI定義と違えば、PtrSafeやLongPtrを付けても正しく動きません。既存コードを直すときは、APIの公式定義を確認します。

32bit用DLLしか存在しない

外部DLLが32bit版しかない場合、VBAのDeclareを直すだけでは64bit版Officeから読み込めません。このケースはDLLの64bit版の有無、代替API、Officeのbit数を含めて判断します。

64bit対応できたか確認する方法

コンパイルが通ったことは、移行完了の一部でしかありません。次の順に確認します。

確認項目確認内容
コンパイルVBAProjectのコンパイルが通る
DeclareDeclare行でPtrSafe関連のエラーにならない
API呼び出し対象APIを実行できる
戻り値・引数ハンドル、ポインター、整数を型どおりに受け渡す
Excelの安定性異常終了や不正な動作がない
業務処理元の入出力結果が期待どおりである

今回の独自ケースでは、PtrSafeとLongPtrを使うブックでAPI呼び出しと戻り値の取得まで確認しました。実際の業務ブックでは、同じ確認を対象の帳票出力、ファイル操作、画面操作まで広げて検証することをおすすめします。

既存VBAを32bitから64bitへ移行するときの確認手順

移行作業では、Declareを見つけた順に一括置換するのではなく、APIごとに型の意味を確認します。

  1. VBEでDeclareを検索する
  2. API定義を確認し、戻り値と各引数の意味を把握する
  3. 64bit版Officeで使うDeclareへPtrSafeを追加する
  4. ハンドル・ポインターだけをLongPtrへ変更する
  5. 呼び出し側の変数、ByRef引数、UDTも確認する
  6. VBAProjectをコンパイルする
  7. APIを呼び出し、戻り値と業務処理を実行確認する

この順番なら、LongをすべてLongPtrへ変える誤修正を避けられます。修正箇所とAPI定義を対応付けて残しておくと、次のOffice更新や引き継ぎでも調査しやすくなります。

tomo

Declareの修正は、コンパイルエラーを消す作業で終わりません。値が何を表すかを確認して、戻り値と呼び出し側まで同じ型でそろえるところが重要です。

まとめ

64bit版ExcelでDeclareのエラーが出たら、まずPtrSafeなしのDeclareを探します。その後は、APIの戻り値と引数がハンドル・ポインターなのか、通常の整数なのかを分けて確認します。

修正・型意味
PtrSafeDeclareが64bit版Officeを想定していることを示すキーワード
LongPtrポインター・ハンドルを保持する型
LongLong64bit整数そのものを保持する型
Long通常の32bit整数。64bit版Officeでも使う
#If VBA7VBA7とVBA6以前を分ける条件付きコンパイル
#If Win64Officeの32bit版・64bit版を分ける条件付きコンパイル

PtrSafeを追加するだけで終わらせず、APIの各引数と戻り値が何を表すかを確認してください。

コンパイル、API実行、元の業務処理まで確認して初めて、64bit移行が完了したと判断できます。

よかったらシェアしてね!
  • URLをコピーしました!
目次