VBAのWin32 API宣言を64bit対応させる:PtrSafeとLongPtrの標準実装パターン

Tech

[META] style_prompt: professional_technical_guide target: VBA_Developer topic: Win32_API_64bit_Compatibility [/META] 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

VBAのWin32 API宣言を64bit対応させる:PtrSafeとLongPtrの標準実装パターン

【背景と目的】

既存の32bit用VBA資産を64bit版Officeで実行した際に発生する「コンパイルエラー」を解消し、OSレベルの高度な操作を安全に実装することを目的とします。

【処理フロー図】

graph TD
A["VBA実行開始"] --> B{"VBAバージョン判定"}
B -->|VBA7以上| C["PtrSafe属性を付与"]
B -->|VBA6以前| D["従来のDeclare文"]
C --> E{"引数の種類を判定"}
E -->|ポインタ/ハンドル| F["LongPtr型を使用"]
E -->|数値データ| G["Long型を維持"]
F --> H["64bit環境で安全に実行"]
G --> H
D --> H

【実装:VBAコード】

実務で頻用される「ウィンドウタイトルの取得」と「ミリ秒単位の待機(Sleep)」を例に、高速化処理を組み込んだコードを提示します。

Option Explicit

' --- Win32 API 宣言セクション ---
' #If VBA7 定数を使用することで、Office 2010以降(VBA7)かそれ以前かを判定
#If VBA7 Then

    ' 64bit/32bit両対応:PtrSafeを記述し、ハンドル(HWND)はLongPtr型にする
    Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Declare PtrSafe Function GetForegroundWindow Lib "user32" () As LongPtr
    Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long
#Else

    ' 旧バージョン(32bit専用)
    Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
    Declare Function GetForegroundWindow Lib "user32" () As Long
    Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
#End If

''' <summary>
''' 現在のアクティブウィンドウ名を取得し、セルへ高速に書き込む
''' </summary>
Public Sub GetActiveWindowTitle()
    ' 高速化設定
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    On Error GoTo ErrorHandler

    Dim hwnd As LongPtr   ' ハンドル保持用(64bit環境では8byte、32bitでは4byteに自動調整)
    Dim buffer As String * 255
    Dim length As Long
    Dim title As String

    ' API呼び出し
    hwnd = GetForegroundWindow()
    length = GetWindowText(hwnd, buffer, Len(buffer))

    title = Left(buffer, InStr(buffer, vbNullChar) - 1)

    ' 結果の出力(配列処理を想定した単一セル書き込み)
    ActiveSheet.Range("A1").Value = "Active Window: " & title

    ' 処理待機(APIのSleepはExcelをフリーズさせるため、慎重に使用)
    Sleep 500

CleanUp:
    ' 高速化設定の解除
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

【技術解説】

  1. VBA7: 条件付きコンパイル引数です。Office 2010以降のVBA環境(VBA7)であるかを判別します。これにより、32bit/64bitが混在する組織内でも同一のコードを配布可能です。

  2. PtrSafeキーワード: 「このAPI宣言は64bit環境で安全に動作するよう考慮されている」ことをコンパイラに伝えます。

  3. LongPtr型: 64bit版Officeでは8バイト、32bit版では4バイトとして動作する可変型です。ウィンドウハンドル(HWND)やポインタ(HDC等)を扱う変数は、必ずこの型で宣言する必要があります。

  4. Long型との使い分け: Sleepの待機時間や文字列の長さなど、純粋な「数値」を示す引数は、64bit環境でもLong(4バイト)のまま維持するのがWin32 APIの基本ルールです。

【注意点と運用】

  • 暗黙の型変換の罠: LongPtrLong に代入すると、64bit環境では上位ビットが切り捨てられ、異常終了やメモリ破壊の原因となります。

  • ユーザー定義型(Type): APIに渡す構造体の中にポインタが含まれる場合、そのメンバも LongPtr に書き換える必要があります。

  • 検証環境の確保: 開発機が32bit、運用機が64bitの場合、必ず両方の環境でコンパイルが通るかテストしてください。

【まとめ】

  1. API宣言は #If VBA7 で囲み、PtrSafe を付与する。

  2. ハンドルやメモリアドレスを受け取る変数は LongPtr を指定する。

  3. 純粋な数値データ(カウント、サイズ等)は Long のまま変更しない。

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

コメント

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