Win32 API VBA移行ガイド:PtrSafe, LongPtr, LongLongで実現する64bit対応の極意

Tech

【META】tech_level=expert; domain=VBA; topic=Win32API_64bit; lang=ja; 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

Win32 API VBA移行ガイド:PtrSafe, LongPtr, LongLongで実現する64bit対応の極意

【背景と目的】

VBAの実行環境が32bitから64bitへ移行する中、既存のWin32 API利用コードが「型が一致しません」エラーで停止する課題に直面します。本記事では、ポインタのサイズ変更に対応するための必須キーワード PtrSafe と、新しいデータ型 LongPtr および LongLong の使い分けを徹底解説し、環境依存しない堅牢なVBAコード設計を実現します。

【処理フロー図】

32bit時代のDeclareステートメントを64bit対応させるためのロジックフローを示します。

graph TD
    A["既存Declareステートメントの発見"] --> B{"VBA7環境か?
(Office 2010以降)"}; B -- Yes --> C["PtrSafeキーワードを追加"]; B -- No --> D["元の宣言を維持"]; C --> E{"引数/戻り値が
ポインタ(HANDLE)か?"}; E -- Yes --> F["LongPtrへ置換"]; E -- No --> G{"引数/戻り値が
64bit整数か?"}; G -- Yes --> H["LongLongへ置換"]; G -- No --> I["Longを維持 (4バイト値)"]; F --> J["テスト実行"]; H --> J; D --> J; I --> J;

【実装:VBAコード】

以下のコードは、条件コンパイル #If VBA7 Then を使用し、実行環境が32bitか64bitかを自動で判断し、適切なAPI定義を適用するテンプレートです。ここでは、64bit環境でのみ利用可能な GetTickCount64 APIを例に、PtrSafeLongLong の使用法を示します。

Option Explicit

'=== 64bit (VBA7) 環境向けAPI宣言 ===
#If VBA7 Then

    ' PtrSafeは、64bit環境でのAPI宣言に必須。
    ' GetTickCount64は64bitの戻り値を持つため、LongLongで受ける。
    Private Declare PtrSafe Function GetTickCount64 Lib "kernel32" () As LongLong

    ' FindWindow APIの例:ウィンドウハンドル(hWnd)はポインタサイズを持つためLongPtrを使用。
    Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As LongPtr, _
        ByVal lpWindowName As LongPtr) As LongPtr
#Else

'=== 32bit 環境向けAPI宣言 ===
    ' 32bit環境ではPtrSafeは不要(コンパイルエラーになる)。
    ' GetTickCount64は32bit環境では利用できないか、代替APIを使う。
    ' FindWindow APIの例:ハンドルは32bit環境ではLong。
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As Long, _
        ByVal lpWindowName As Long) As Long
#End If

Sub CheckAPIAndTypeSizes()
    ' 処理速度向上のための基本設定
    Application.ScreenUpdating = False

    Dim RetVal As Variant
    Dim Msg As String

    ' 環境判定と型サイズ確認
    #If VBA7 Then

        Msg = "実行環境: 64ビット版VBA (VBA7以降)" & vbNewLine
        Msg = Msg & "Long型サイズ: " & Len(0&) & " バイト (32bit値)" & vbNewLine

        ' LongPtrは64bit環境では8バイトとなる
        Msg = Msg & "LongPtr型サイズ: " & LenB(0#) & " バイト (ポインタサイズ)" & vbNewLine

        ' LongLongは常に8バイト
        Msg = Msg & "LongLong型サイズ: " & LenB(0##) & " バイト (64bit値)" & vbNewLine

        ' 64bit対応APIの実行
        Dim StartTick As LongLong
        StartTick = GetTickCount64()
        Msg = Msg & vbNewLine & "--- API実行結果 ---" & vbNewLine
        Msg = Msg & "GetTickCount64結果: " & StartTick & " (LongLongで取得)"

        ' LongPtrを使用したAPIの呼び出し(ここではデスクトップウィンドウのハンドルを取得)
        Dim hWndDesktop As LongPtr
        hWndDesktop = FindWindow(0, 0) ' クラス名、ウィンドウ名をNULL(0)で渡す
        Msg = Msg & vbNewLine & "デスクトップハンドル: 0x" & Hex(hWndDesktop) & " (LongPtrで取得)"

    #Else

        Msg = "実行環境: 32ビット版VBA (VBA6以前)" & vbNewLine
        Msg = Msg & "Long型サイズ: " & Len(0&) & " バイト (32bit値)" & vbNewLine
        ' 32bit環境ではLongPtrはLongと同じ扱い
        Msg = Msg & "LongPtr型はLongと同じ: " & Len(0&) & " バイト" & vbNewLine

        ' LongLong型は存在しないため、直接は宣言できない
        Msg = Msg & "GetTickCount64は実行不可。"
    #End If

    MsgBox Msg, vbInformation, "VBA Bitness Checker"

    Application.ScreenUpdating = True
End Sub

【技術解説】

Win32 APIの宣言において、32bitと64bitの互換性を確保するために理解すべき3つの要素は以下の通りです。

1. PtrSafe (ポインタ安全性の保証)

Declare ステートメントに PtrSafe キーワードを付与することは、64bit版のOffice VBA(VBA7以降)でAPIを使用するための必須条件です。

  • 32bit環境ではこのキーワードは不要ですが、#If VBA7 Then を使用することで、両環境でコンパイルエラーを防ぎます。

  • このキーワードは、宣言内のポインタやハンドルが適切に処理されていることをVBAコンパイラに通知します。

2. LongPtr (ポインタ型)

ポインタ(メモリのアドレス)やOSが割り当てるハンドル(HWND, HDC, HANDLE など)を格納するために使用します。ポインタのサイズは実行環境に依存するため、LongPtr を使います。

  • 32bit環境 (VBA6以前)Long 型と同等(4バイト)。

  • 64bit環境 (VBA7以降)LongLong 型と同等(8バイト)。

  • APIの引数や戻り値がアドレスやハンドルである場合は、必ず LongPtr を使用することで、32bit/64bit双方での互換性を保ちます。

3. LongLong (64bit整数型)

LongLong 型は、常に8バイト(64ビット)の符号付き整数を保持します。これは、ポインタサイズとは関係なく、大きな数値を扱う必要がある場合に使用します。

  • 用途例: GetTickCount64 の戻り値、巨大なファイルサイズ(LARGE_INTEGER 構造体の一部)、タイムスタンプなど。

  • LongPtr がポインタサイズに「適応」するのに対し、LongLong は常に8バイトの「固定長」です。

【注意点と運用】

落とし穴:ByVal As Long の厳密な解釈

32bit環境の古いAPI宣言で、引数が文字列ポインタやバッファへのポインタであるにも関わらず ByVal As Long で定義されているケースが多く見受けられます。

誤った移行例

もし元のAPIが文字列ポインタを渡していた場合、32bit環境では ByVal As Long で問題なく動作します。しかし、これを単純にポインタだからと ByVal As LongPtr に変換すると、64bit環境でポインタの渡し方が変わり、予期せぬメモリ破壊やエラーを引き起こす可能性があります。

回避策:C言語のプロトタイプ確認とByVal/ByRefの判断

API宣言の引数を修正する際は、対応するC言語のプロトタイプ宣言(MSDN等で公開されている)を必ず確認し、引数が実際に何を受け取るかを厳密に判断する必要があります。

  1. ポインタまたはハンドルを値渡しする場合: ByVal As LongPtr

  2. 4バイト固定値またはポインタではない参照渡し: ByRef As Long または適切な型

特に文字列を扱うAPI(例: GetPrivateProfileString)では、文字列バッファを渡す引数は64bit環境で ByVal As LongPtr を使う必要がありますが、VBAが文字列を自動的にポインタに変換してくれる仕組み(マーシャリング)を理解していることが重要です。

【まとめ】

VBAにおけるWin32 APIの64bit対応を成功させるための運用のコツは以下の3点です。

  1. 条件コンパイルの徹底: #If VBA7 Then を使用し、32bit環境と64bit環境の宣言を明確に分け、どちらの環境でもコンパイルエラーが発生しないように設計を統一します。

  2. 役割に応じた型選択: ポインタまたはハンドルを扱う場合は LongPtr、ポインタサイズに関係なく64bitの値を扱う場合は LongLong を選択し、古い Long 型は4バイト固定の数値に限定します。

  3. PtrSafeは必須: VBA7環境下でのすべての Declare ステートメントには PtrSafe キーワードを付与することを徹底し、API宣言の安全性を保証します。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました