われこわれこ

プログラムじゃあ~りませんか!

VBAをEXCEL2007, 2010, 2013の32/64 bitの全てに対応させるにはどうすれば良いのか?解決!

2015-05-08 21:16:36 | VBA

当記事はワテの新ブログサイト、

https://www.wareko.jp/blog/how-can-i-make-vba-correspond-to-all-32-or-64-bit-of-excel-2007-2010-2013-2016-2019

に引っ越した。

以下は、その引っ越し前のオリジナル記事。

参考までに残している。

Gooでブログを始めた直後に書いた記事なので、HTMLの使い方も良く知らず、背景色が黒、文字が白と言うヘンテコなページになっている。

 

 

最近EXCELのVBAのコードを書いていて、VBAのコードをEXCEL2007, EXCEL2010, EXCEL2013の32bit, 64bitの全てに対応させるにはどうすれば良いのか
良く分からなくなったので調べてみたのだが、そのメモ。
インターネットで調べると、この件に関してはいろんな情報がヒットするが、やはりマイクロソフトの公式情報を確認するのが間違いないだろう。
 
    Office 2010 の 32 ビット バージョンと 64 ビット バージョンとの互換性
    https://msdn.microsoft.com/ja-jp/library/office/ee691831?f=255&MSPPError=-2147217396
 
この情報によると、
 
    EXCEL2007は32bit版のみ
    EXCEL2010から32bit版, 64bit版の二種類になった
    EXCEL2010以降では、VBA7 と Win64 という 2 つの条件付きコンパイル定数が用意されている。
 
である。
つまり、VBA7という定数を使うとEXCELのバージョンが2007以前か2010以降かが区別できる。
一方、Win64は、実行しているEXCELのバージョンが64か32かの区別が出来る。Win64という名前から、Windowsのビット数を表していると錯覚しがちだが、
あくまで実行しているEXCELのビット数なので注意。
従って、Windows7やWindows8の64bit版でEXCEL2010(32)を実行していると Win64は定義されない。
念のために以下の関数をいくつかの環境で試してみた。
 
    Sub EXCELのバージョンやビット数を調べる()
 
#If VBA7 Then
        Debug.Print "EXCELは2010以上です。"
#Else
        Debug.Print("EXCELは2007以下です。")
#End If
 
#If Win64 Then
        Debug.Print "EXCELは64bit版です。"
#Else
        Debug.Print("EXCELは32bit版です。")
#End If
 
    End Sub
 
Win7(64) EXCEL2013(32)での実行結果
EXCELは2010以上です。
EXCELは32bit版です。
 
Win7(64) EXCEL2003(32)での実行結果
EXCELは2007以下です。
EXCELは32bit版です。
 
Win8(64) EXCEL2013(64)での実行結果
EXCELは2010以上です。
EXCELは64bit版です。
 
WinXP(32) EXCEL2007(32)での実行結果
EXCELは2007以下です。
EXCELは32bit版です。
 
となった。
 
さて、インターネットを検索すると、これらのコンパイル定数を使う場面としてWin32API関数をVBAから使う例が良く出てくる。
 
例えば、従来の32bit環境では、このような行をModuleの先頭に書いて使っていた。
    Declare Function RegOpenKeyA Lib "advapi32.dll" (ByVal Key As LongByVal SubKey As String, NewKey As LongAs Long
この関数のC++での定義は以下のようになるが、
	LONG RegOpenKeyA ( HKEY hKey, LPCSTR lpSubKey, HKEY *phkResult );
この中にはポインタ型の引数があるが、それらは32bitのOSでは32bitであるが64bitOSなら64bitになる。
従って、この宣言文を64bit環境では
 
	Declare PtrSafe Function RegOpenKeyA Lib "advapire32.dll" (ByVal hKey as LongPtr, ByVal lpSubKey As String, phkResult As LongPtr) As Long
 
のように書くようになったらしい。このLongPtr というのが新たに導入された"ポインター" データ型だ。
また
	Declare ステートメントで PtrSafe を指定しないと、64 ビット バージョンの Office 2010 と互換性がないものと見なされます。
ということなので、つまりPtrSafeを付けておくと64bit対応の関数ということだ。
 
上記のマイクロソフトのサイトでは、この32bitと64bit環境での宣言の切り替えの例として以下の例が掲載されている。
 
#If Win64 Then
  Declare PtrSafe Function MyMathFunc Lib "User32" (ByVal N As LongLong) As LongLong	'(1)
#Else
    Declare Function MyMathFunc Lib "User32" (ByVal N As LongAs Long                   '(2)
#End If
 
#If VBA7 Then
  Declare PtrSafe Sub MessageBeep Lib "User32" (ByVal N AS Long)						'(3)
#Else
    Declare Sub MessageBeep Lib "User32" (ByVal N As Long)                               '(4)
#End If
 
これは、分かりにくいと思う。前者のWin64の有無で(1)(2)を切り替えるのは分かる。
つまり、64bitのEXCELで実行した場合は(1)を使い、32bitのEXCELで実行した場合は(2)を使うということだ。
では、後者のVB7の有無で(3)(4)を切り替えているがこれは何だ?
つまり、EXCEL2010以降を使っているならそれが64か32かにかかわらず(3)を使い、
EXCEL2007(32版しかない)を使っている場合は(4)を使うということになる。
 
ではEXCEL2010の64bitを使った場合にはどうなるのか。それは(3)が使われる。PtrSafeが付いているので64bit対応ということだ。
なんだかややこしいぞ。
要するにVBA7なんて使わなくても
 
#If Win64 Then
  Declare PtrSafe Sub MessageBeep Lib "User32" (ByVal N AS Long)						'(5)
#Else
    Declare Sub MessageBeep Lib "User32" (ByVal N As Long)                               '(6)
#End If
 
と書けばいいのではないか?
 
この件に関してインターネットを検索していると、32と64とでDeclare文の切り替えは、
 
#If VBA7 And Win64 Then
   '64ビット版
#Else
    '32ビット版
#End If
 
というふうに#If VBA7 And Win64 Then と記述してある例をよく見る。
でも、これも何か変だぞ。
 
 
#If VBA7 And Win64 Then というのは、
EXCEL2010以上で64bit版なら
と言う事になるが、64bit版があるのは2010以上と決まっている訳だからWin64に加えてVBA7を使うのは無駄な判定のような気がする。
 
要するに32/64で区別するのか、EXCEL2007以下/2010以上で区別するのかの問題である。
まあ、それは好き好きじゃないかなあと思うが、私の場合は32/64で区別するほうが分かりやすいような気がするのだが。。。
 
しばらく考えていて自分でも混乱してきたのだが、ようやくその答えが分かった。
それは、上記のマイクロソフトのサイトの中で紹介されている
http://www.jkp-ads.com/articles/apideclarations.asp
のページにあった。
このページは、
 
Excel MVP の Jan Karel Pieterse 氏の サイトで、そこには、沢山あるWin32API関数のDeclare宣言文で、32bit/64bit対応したものの一覧がある。
その中にのほとんどは、32bit/64bitの両方の関数定義が書かれているので、やはり、それらを
#If Win64 then
#Else  
#End If
で使い分ければいいと思ったのだが、自信が無いので本人に質問してみた。
 
そしたら、
 
Hi Wareko, 
VBA7 suffices in almost all cases. In case there is an exception it is mentioned in the sample code above.
 
というお返事を頂いた。なので、ほとんどの場合、VBA7を使って
#If VBA7 then 
#Else  
#End If
 
切り替えればよいというアドバイスなので、私もそうしようと思う。
なので、ネットでよく見かける#If VBA7 And Win64 Then と書く必要は無いのだ。
 
例外としては、Jan Karel Pieterse 氏のコメントにあるように、
 
SetWindowLongPtr
 
などの関数では、
This is one of the few API functions that requires the Win64 compile constant:
 
#If VBA7 Then
    #If Win64 Then
        Private Declare PtrSafe Function SetWindowLongPtr Lib "USER32" Alias "SetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
    #Else
        Private Declare Function SetWindowLongPtr Lib "USER32" Alias "SetWindowLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
    #End If
#Else
    Private Declare Function SetWindowLongPtr Lib "USER32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long 
#End If
 
のように、Win64とVBA7を使ってややこしい切替が必要になるものもあるらしい。
 
ということで、当初の目的である、
VBAを32/64の両方に対応させるためのWin32API関数の宣言のやり方は、
 
http://www.jkp-ads.com/articles/apideclarations.asp
のページを見て、必要な関数をコピペして使えばよいと言う事だ。
それ以外のインターネットの情報に惑わされてはいけない。この私のサイトの情報も正しいとは限らないので、注意が必要です。


最新の画像もっと見る

2 コメント

コメント日が  古い順  |   新しい順
Unknown (r)
2015-09-15 12:09:37
はじめまして。
VBAの32/64bit対応問題で検索していて記事を参考にさせていただいています。

#If VBA7 Then
で分岐させることで解決したということですが、
この判定だと2010以降だということはわかっても32bitか64bitかは見ていないのではないでしょうか?
32bitでPtrSafeを入れても特に問題ないということなのでしょうか?
だとしたら分岐せず全部に入れればいいような気もするのですが・・・
返信する
Unknown (wareko)
2015-09-17 20:01:31
Unknown (r)さん
こんにちは

このGooブログは、新サイトwareko.jpに引っ越したので、あまりチェックしていたかったので
お返事遅くなってしまいました。すみません。

>VBAの32/64bit対応問題で検索していて記事を参考にさせていただいています。
>#If VBA7 Then
>で分岐させることで解決したということですが、
>この判定だと2010以降だということはわかっても32bitか64bitかは見ていないのではないでしょうか?
>32bitでPtrSafeを入れても特に問題ないということなのでしょうか?
>だとしたら分岐せず全部に入れればいいような気もするのですが・・・

えーっと、最近VBAから遠ざかっていたので思い出すのに時間が掛かりましたが、
https://msdn.microsoft.com/ja-jp/library/office/gg264421.aspx
によりますと、

PtrSafe – PtrSafe キーワードは、Declare ステートメントが 64 ビット版の Office で実行しても安全であることを示します。
Declare ステートメントと PtrSafe キーワードが推奨される構文です。PtrSafe を含む Declare ステートメントは、32 ビットと 64 ビットの両方のプラットフォーム上の VBA7 開発環境で正常に動作します。VBA7 以前との下位互換性を保証するには、次の構造を使用してください。
#If Vba7 Then
  Declare PtrSafe Sub...
#Else
  Declare Sub...
#EndIf

と言う事です。ですので、Vba7だけを使って分岐しています。マイクロソフトが言うのだから間違いないでしょう。

>この判定だと2010以降だということはわかっても32bitか64bitかは見ていないのではないでしょうか?
見ていないですね。でも、2010以降のExcel向きに書かれたAPI 関数は、上記マイクロソフトの解説のように
『PtrSafe を含む Declare ステートメントは、32 ビットと 64 ビットの両方のプラットフォーム上の VBA7 開発環境で正常に動作します。 』
ということですので、32bitか64bitかは見なくても良いという事です。
>32bitでPtrSafeを入れても特に問題ないということなのでしょうか?
そうですね。でもそれは、Vba7(2010)以上の場合です。

>だとしたら分岐せず全部に入れればいいような気もするのですが・・・
そうしてしまうと、2007などのExcelで実行した場合にもDeclare PtrSafeが出てきますが、確かPtrSafeというのがエラーになったと思います。

以下のサイトにも関連する質問がありますね。
http://stackoverflow.com/questions/5506912/how-should-i-make-my-vba-code-compatible-with-64-bit-windows

ということで、思い出しながらお返事を書きましたが、間違っていたらごめんなさい。
私の力作の
http://wareko.jp/map/
も一度開いてみて下さい。
よろしく。
返信する

コメントを投稿