64bit対応が必要な条件

Excel 2007までは32bit版のみでしたが、Excel2010から32bit版と64bit版の2つになりました。Excel2010当時は32bit版のインストールが推奨されていましたが、2020年現在主流のOffice 365では64bit版が一般的になっています。

ただ、違うPCや違うバージョンのExcelを使うとしても、過去に作成したブックを新しいバージョンでもそのまま使うことは普通にあります。そのような場合に、32bit版のExcelでWin32APIを使っているVBAのコードやブックを64bit版のExcelでそのまま使用しようとすると以下のようなエラーメッセージが表示されます。

「このプロジェクトのコードは、64ビットシステムで使用するために更新する必要があります。Declareステートメントの確認および更新を行い、次にDeclareステートメントにPtrSafe属性を設定してください。」

要するに、”Declare Function 関数名”と書いてたものに「PtrSafe」を付けて”Declare PtrSafe Function 関数名”と書け、と言ってるわけですが、日本語がおかしいのでまあ読み取れませんね。また、PtrSafeを付けて終わりではなく、引数の型が変わっている場合があるためそれにも対応が必要です。

以下で対応方法の詳細を説明します。

細かいことはいいから変換方法教えて!な方

エラーメッセージに書いてあるPtrSafeを付けるだけでは実際にはダメで、正しくは以下の3ステップで対応できます。

  1. Win32API_PtrSafe.TXTを入手する。下記入手方法がめんどいので当サイトからダウンロードできるようにしてます。
    ダウンロード「Win32API_PtrSafe.TXT(676KB)(Microsoftサイトからの入手方法は「VBAでWin32APIを使う方法と定義一式」をご参照ください。)
  2. エラーが発生しているWin32APIの関数名をWin32API_PtrSafe.TXTで検索する。
  3. 関数名で検索すると複数見つかるので、Declearが先頭に書いてある行の内容をコピーして、VBAの該当行部分に置き換える。

以下のような感じになります。PtrSafeが付いて、変数の型が変わる部分があります。

‘// 32bit版から64bit版へ変更
‘ Declare Function DrawMenuBar Lib “user32” (ByVal hwnd As Long) As Long
Declare PtrSafe Function DrawMenuBar Lib “user32” (ByVal hwnd As LongPtr) As Long

VBAでコードが書ける人への説明はこれで十分だと思います。VBAが書けない方は以下を。

VBAとか分かりません、という初心者向け

「VBAは分からないがとにかく動くようにしたい。」

こういう方は少なくないと思います。ネットで落としてきたコードや仕事のマクロとかですね。

そういう場合は上の説明だけだとわからないと思うので、もう少し丁寧に説明します。

  1. 最初に、上に書いているWin32API_PtrSafe.TXTを入手してメモ帳などのテキストエディタで開いておきます。後で使います。
  2. そしてExcelを起動します。起動したらAltキー+F11キーを押してください。そうすると「Microsoft Visual Basic for Applications」というタイトルが付いた画面が表示されます。これがVBAの画面です。既にエラーダイアログが表示されていればこの画面が表示されている場合があります。
  3. 次に、VBAのコードを書く画面を開きます。具体的には、VBA画面の左上にある「プロジェクト」のところに「VBAProject (ブック名)」があり、その中に「Microsoft Excel Objects」→「Sheet1」や「ThisWorkbook」がありますので、どちらでもいいのでダブルクリックして右側に表示させます。ここではSheet1を開いています。
  4. 右側にウィンドウが開いたら、Ctrl + Fキー(もしくは編集メニュー→検索)を押して検索ダイアログを表示し、以下の画像のように検索する文字列に「Declare」と入力し、対象に「カレントプロジェクト」を選択して、「次を検索」ボタンを押します。
  5. すると、Win32APIを使用している箇所が見つかります。
  6. 例えば、「Private Declare Function GetWindowLong Lib “user32” Alias “GetWindowLongA” (ByVal hwnd As Long, ByVal nIndex As Long) As Long」という行が見つかったとします。ここでの関数名は「Function」と書かれた右側の部分になりますので、例では「GetWindowLong」が関数名になります。
  7. そうしたら、関数名「GetWindowLong」を事前に開いておいたWin32API_PtrSafe.TXTで検索します。
  8. GetWindowLongをメモ帳で検索すると以下の4行が見つかります。
  9. この中で関数名として完全に一致しているもので、かつ、行の先頭に「Declare」と書かれている行が対象になるため、見つかった4つの一番下の内容を採用します。
    Declare PtrSafe Function GetWindowLong Lib “user32” Alias “GetWindowLongA” (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
  10. あとは、元のGetWindowLongの行をWin32API_PtrSafe.TXTの内容で置き換えます。
  11. 置き換えの際に、元の行の先頭に「Private」や「Public」などが付いていることがあります。その場合は置き換え後の先頭にも元と同様に付けます。
    Private Declare PtrSafe Function GetWindowLong Lib “user32” Alias “GetWindowLongA” (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long

あとは他にもDeclareと書かれている箇所がないかを検索して探して、同様に対応します。

単に書き換えるだけでよいのであればこれで終わりです。

#IFでの分岐はしない方がよい

32bit版と64bit版が混在する環境の場合に、#IFを使ってWin32APIの記述を32bit版用と64bit版用に分けて書くことができます。こんな感じです。

しかし、こういうことをしなければならないような状況に陥っているのならば、その状況自体が問題です。そして#IFでの分岐なんかやめてしまった方がいいです。

「VBA7定数で分岐できるならいいのではないか」とか「会社で使っているPCのOSやExcelのバージョンがバラバラなので仕方ない」とかの話もよく聞く話ですが、そういう状況であればVBAだけの話に留まらず色んなところに問題が派生していくため、Win32APIのバージョン分岐を入れたのでOK、なんて話で済むことはまずありません。むしろそういう場合はWin32APIを使うことで他の足かせになる恐れがあります。

なので、#IF分岐をするぐらいならVBAでのWin32APIの利用は64bit化に限定するか、そもそもWin32APIを利用しない判断をした方がいいです。その方がVBAが足かせにならずに64bit化対応の影響範囲も少なくなります。

2010年のOffice2010で64bit版が登場してから10年が経過し、そのOffice2010は2020/10/13にサポートが切れます。それを過ぎても32bit版を使っている方はとても多くいらっしゃいます。おそらく10年後、20年後でもWindowsXPやWindows7で32bit版を使っている方はいるでしょう。

ただ、その状態がその時々における標準仕様と言えるかというと、おそらくそうではないでしょう。

数年前までは「64bit版をインストールするといろいろ面倒だから32bit版を入れましょう」という話がいたるところにありました。その当時は周辺環境なども64bit対応への過渡期だったためそういう理屈も一理ありますが、2020年現在では64bitは当たり前の話で「32bit版を入れましょう」なんて話も聞かなくなりました。実際32bit版のPCなんて中古や在庫物で扱っているのがほとんどで、「Windows7 + 32bit + 新品PC」とかがあったとしても新品であっても最新ではありません。

そういうわけで、個人的にはWin32APIのバージョン分岐はしないことをお勧めします。