AccessVBA32bitを64bitに変換するには?DLLが動かない!

Accessの旧バージョン(32bit)で作成されたVBAマクロを最新バージョンのAccessや最新Windows OS(64bit)で動かそうしてみたところ、よくわからないエラーが発生してしまい動かない!といった現象に発生したことはないでしょうか?

今まで動いていたAccess VBA 32bitが64bitで動かなくなる例
  • PCの入れ替えに伴いWindowsのバージョンを変えた(Windows7→Windows10)ら、今まで使えていたAccessマクロが動かない!
  • Accessをバージョンアップしたら動かない!
  • 32bit版のAccessから64bit版のAccessに変えたら動かない!
  • AccessからAccess Runtimeに変えたら動かない!

今まで使っていたAccessが急に動かないようなことになると、非常に困りますよね。

そこでこの記事では、32bit版DLLを使用したAccess VBAを変換して64bit環境でも動くように修正対応する方法をご紹介します。

パソコンを買えたりAccessのバージョンを上げたらなぜか動かなくなったという方は、本記事を確認してエラーが発生している箇所を変換し、動かせるようにしましょう!

本記事は主にAccess VBAの知識がある方に向けての変換手順について書いています。

Access VBA 32bit版のマクロが64bit環境で動かない原因・状況

旧バージョンで作られたAccessが新バージョンで動かないような場合、原因はDLLにあります。

ここで指している「バージョン」というのは、Access 2010/2013/2016/2019のようなバージョンではなく、32bit版なのか64bit版なのか、ということです。

想定される環境
  1. 32bit版Accessで作られたファイルである
  2. 64bit版Officeまたは最新OSの環境で動かそうと思ったら、エラーが出て動かない!

元々、Windowsは32bit版のみで、それに伴いOfficeも32bit版しかありませんでした。

それが、PCに搭載されるメモリが2GBを超え始めたころから性能を最大限利用するために64bit版が出始めて、Officeも32bit版と64bit版の2種類存在するようになりました。

Officeの64bit版が出始めたころ、32bit版から64bit版にAccessを変えた際に色々と問題が発生する人もいたようで、Microsoftとしても当初は32bit版を推奨していました。

で、時が経過して現在(2018年頃)には、一般的なOfficeとしては64bit版が普及するような形になっています。

参考:64bit版または 32bit版の Office を選択する

ということで、

  1. 旧バージョン(32bit版)で作成したAccessを、32bit版Office環境で利用していた(例えばWindows7 & Office2010 32bit版)
  2. パソコンが古くなったのでWindows10への買い替えついでにOfficeも最新バージョン(64bit版)にした(この時に特に気にせず64bit版のオフィスを購入)
  3. Windows10 & Microsoft Office(64bit環境)でVBAマクロを動かしたところ、動かなくなった

というのが、今まで動いていたAccessVBAマクロが動かない状態に経緯としてて多いのではないかと思います。

Access 32bitを64bitに変換する手順

32bit版Accessで使用されているDLLはWin32API。つまり32bit用のAPIです。

そのため、これを変換して64bit環境でも動くように対応することで、引き続き64bit環境でもAccessを継続して利用できるようになります。

次の手順で該当箇所を64bit環境に変換対応することができます。

AccessVBAソース内を「.dll」「Declare」で検索して修正箇所をブックマーク

はじめに、Accessファイルを開きます。

Accessファイル起動時の補足

Accessファイルの起動時にマクロが動いてしまってエラーメッセージが表示され強制終了してしまうようなケースがあります。

このような時は、Shiftキーを押しながらファイルアイコンをダブルクリックしてマクロが実行されずにAccessファイルを開くことができます。

次に、Visual Basic Editorを開き、VBAのソースをどれでもいいので表示します。

Visual Basic Editor

この状態でctrl+fをクリックして検索ボックスを表示し、dllと入力、対象を「カレント プロジェクト(C)」にチェックして、次を検索ボタンをクリックします。

AccessVBAソースの検索ボックス

すると、ファイル内で32bitDLLを利用している箇所が表示されます。(例えばこの例の場合、comdlg32.dllがヒットしています)

この状態でツールバーの青い旗をクリックしてブックマークします。

DLLの検索&ブックマーク前

カーソルがある行にブックマークが設定されます。ブックマーク後はこのように目印が表示されるようになります。

VBAのDLL部分のブックマーク後

これをVBAソース内のすべての該当箇所に対して行います。

  1. dll または Declareで検索する
  2. ヒットした行をブックマークする
  3. 次を検索する
  4. ヒットした行をブックマークする・・・

これで、修正すべき32bitDLLが利用されている箇所に対して全てブックマークすることができました。

ブックマーク時の補足1

Visual Basic Editorにブックマーク用のツールバーが表示されていない場合、

メニューの「表示」→「ツールバー」→「編集」

にチェックを入れることで表示されるようになります。

合わせて「標準」にもチェックを入れておくとよいでしょう。

VBA編集バーの表示方法

ブックマークの補足2

ブックマークをすることで、ブックマーク移動ボタンから次のブックマークに遷移することができるようになります。

ブックマークの移動ボタン

ブックマークをすることで都度検索の手間が省けますので、使ってみましょう。

32bit用外部API宣言部分のソースを64bit環境で動作する形に変換

次に、32bit用の外部API宣言部分のソースを、64bit環境でも動作するように変換します。

変換時のポイントは次の3つです。

DLL修正時のポイント

関数の宣言に「PtrSafe」を付ける

64bit版での動作用に条件分岐して定義する(必要な場合)

Long型で参照型パラメータとなっている部分をLongPtrに変更する

関数の宣言に「PtrSafe」を付ける

32bitDLLを64bit版で動作させるようにする場合、API呼び出し用の関数にPtrSafeを付ける必要があります。64bit版で動作させるためにこれは必須の設定となるため、必ず設定します。

Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _
(ByVal lxDigital As Long, _
ByVal lxFolderPath As String) As LongPtr

Private Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _
    (ByVal lxDigital As LongPtr, _
     ByVal lxFolderPath As String) As LongPtr

64bit版での動作用に条件分岐して定義する(必要な場合)

Accessを利用するパソコン環境が人によって32bit版Accessを使っていたり64bit版を使っていたりと混在するような場合、それぞれの環境で動かすために条件分岐をして関数の定義を行います。

条件分岐は次のように、IFの前に#を追加したコードを書きます。

'API:フォルダのパスを取得する
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _
    (ByVal lxDigital As Long, _
     ByVal lxFolderPath As String) As LongPtr

#If VBA7 Then    
    'API:フォルダのパスを取得する
    Private Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _
        (ByVal lxDigital As LongPtr, _
         ByVal lxFolderPath As String) As LongPtr
         
#Else
    'API:フォルダのパスを取得する
    Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" _
        (ByVal lxDigital As Long, _
         ByVal lxFolderPath As String) As LongPtr
 
#End If

If VBA7 Then の中に64bit版用の宣言を書き、#Elseの中に32bit版用の宣言を書きます。

厳密にはVBA7での判定だけではなくWin64での条件分岐も必要なようですが、VBA7だけでも動くのでここではWin64での判定は行っていません。

Long型で参照型パラメータとなっている部分をLongPtrに変更する

関数でLong型を使用している場合、LongPtrに変更します。

注意点としては、参照型の部分のみLongPtrに変えるということです。Long型の部分を全てLongPtrに変えるということではありません

また、パラメータの他に、関数の戻り値関数のパラメータで使用している構造体もあわせて変更する必要があるので、忘れずに変換するようにしましょう。

とはいえ、使いたい関数がLong型を使っている場合に、どの部分がLongの参照型でLongPtrに変える必要があるのかを見つけるのは中々めんどくさいです。

そんな方のために、以下のサイトにWin32API_PtrSafe.TXTがあるのを見つけました。

このテキスト内を関数名で検索し、その関数部分をコピペすれば基本的には64bit環境で使えるようになりますので、参考にしてみて下さい。(構造体の宣言も記載されているので、合わせてコピペしましょう)

Win32API_PtrSafe.TXTの参考ページへ

関数を使用している箇所のソースを修正

基本的には関数の宣言部分のみ変更すれば64bit版環境でもVBAが動くようになりますが、中にはそれだけでは動かないケースがあります。

例えば、GetSaveFileNameを利用してダイアログ表示する場合です。

GetSaveFileNameの場合、この関数を利用して構造体のサイズをlStructSizeに設定しますが、64bit環境ではLenBを使用する必要があります。32bit環境ではLenで動きますが、64bit環境ではLenでは動かない(エラーが出ない代わりにダイアログも出ない)という状況になります。

   Dim OFN As OPENFILENAME                     'OPENFILENAME構造体
   Dim Ret As Long                             '戻り値

   With OFN                                 '構造体の設定
     .flags = OFN_PATHMUSTEXIST Or _
              OFN_FILEMUSTEXIST Or _
              OFN_HIDEREADONLY Or _
              OFN_OVERWRITEPROMPT
             'Or OFN_SHOWHELP
     .hInstance = 0                          'インスタンスハンドルを設定
     .hwndOwner = nHandle                    'ウインドウハンドルを設定
     .lpstrTitle = "ファイルを保存する"       'コモンダイアログのタイトルを設定
     .lpstrFilter = nFilter                  'フィルターを設定
#If VBA7 Then    'VBA7(64bit)時
     .lStructSize = LenB(OFN)                 '構造体のサイズを設定
#Else
     .lStructSize = Len(OFN)                 '構造体のサイズを設定
#End If
     .nMaxFile = 250                         'ファイル名のバッファサイズを設定
     .lpstrFileTitle = String(250, Chr(0))   'フルパス用のバッファを確保
     .nMaxFileTitle = 250                    'フルパス用のバッファサイズを設定
     .lpstrFile = String(250, Chr(0))        'ファイル名のバッファを確保
    '.lpstrInitialDir = CurDir               'デフォルトのディレクトリを設定
   End With
    
  Ret = GetSaveFileName(OFN)

    If Ret = 0 Then
         SaveDlg = vbNullString
    Else
         SaveDlg = Left$(OFN.lpstrFile, InStr(OFN.lpstrFile, Chr$(0)) - 1)       'パスを取得
    End If

関数の引数の型を修正

64bit環境用に関数定義を変更した場合、引数がLong型からLongPtr型に変わるケースがあります。

そのため、関数の引数に渡している変数もLongPtr型に変更する必要があります。

ここは意外と忘れがちで、実行時に「型が違います」というエラーが出て発覚することがあるため、忘れずに修正します。

構造体がWin32APIの引数に定義されている場合、構造体内の定義も忘れずにLong型→LongPtr型に変わっている部分が無いかを確認します。変更を忘れた場合、処理の途中でAccessが強制終了(落ちる)ようなケースがたまに発生します。

Accessの64bit環境変換対応が難しい場合

エラーが発生しているAccessVBAマクロを64bit環境で動くように変換するためには、次の状況がそろっている必要があります。

VBAマクロを修正するには

VBAマクロを作った人に修正をお願いできる状況にある

または

自分に64bit変換対応するために必要なVBAの知識がある

エラーが発生しているAccessを作った人が近くにいるならば、まずはその人にエラー対応を依頼すれば64bit対応してくれることでしょう。

しかし、このようなケースは稀で、大抵はAccessを作った人は既にいなくなっている場合が多いです。

そうなると自力で何とかするしかないのですが、自分にAccess VBAの知識がない場合はすぐに変換対応することはできません。

旧担当から引き継いだだけだし自分にVBAの知識もなく自力の対応が難しい、だけどエラーを解消してAccessを使えるようにしないと業務が滞る。

そのような場合、頑張ってVBAを覚えてなんとか対応するのも1つの手ですが、誰か詳しい人にお願いすることを検討したほうが労力も時間も大幅に削減できます。

最近では【クラウディア】【ココナラ】【フジ子さん】オンラインアシスタント【タスカル】などのエンジニアに対してビジネスの依頼をできるクラウドサービスも色々とありますので、これらのサービスを使ってみるのもよいのではないかと思います。

Access 32bitの64bit環境変換まとめ

この記事では、32bit版DLLを使用したAccess VBAを変換して64bit環境でも動くように修正対応する方法をご紹介しました。

Accessが32bitから64bitに変わったことでもエラー発生しますが、OSの32bitから64bitに変わったこと(例えばWindows7からWindows10)によってもエラーが発生することがあります。

32bit版のDLLを使用していることがエラーの主な原因なので、1つ1つ確認をして変換対応することで、確実に64bit環境でも継続して使えるようになります。

困っている場合は64bit版変換にトライしてみましょう!

コメント一覧
  1. イチカワ より:

    前任者が作成したファイルなのですが、パソコンが新しくなって
    32bit⇒64bitになったことでVBAがエラーとなってしまい、途方に暮れ
    ネットで検索してこちらにたどり着きました。

    条件分岐のIFの前に#を追加したコードを書く、というところでお尋ねします。

    ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
    Public Function SubClassStart(strWndCpt As String) As Integer
    '
    ' ユーザーフォームのサブクラス化を開始する
    '
    ' 引数 ウィンドウのキャプション
    ' 返値 サブクラス化が成功したとき:1 サブクラス化が失敗したとき:0
    '
    '
    'VBA6のときのみコンパイルする
    #If VBA6 Then
    '
    Dim hWnd As LongPtr
    '
    'On Error Resume Next
    '
    'すでにフックされているときは終了する
    If p_hWnd 0 Then
    Exit Function
    End If
    '
    'ウィンドウハンドルを取得する
    hWnd = FindWindow(vbNullString, strWndCpt)
    'Win95・NT3.5の場合はユーザー定義メッセージ(MSWHEEL_ROLLMSG)のメッセージIDを取得する
    WM_MOUSEWHEEL95 = RegisterWindowMessage("MSWHEEL_ROLLMSG")
    'メッセージフックを開始する
    p_hWnd = SetWindowLongPtr(hWnd, GWL_WNDPROC, AddressOf SubClassProc)
    If p_hWnd = 0 Then
    SubClassStart = 0
    'フックが成功したときはウィンドウのハンドルを変数に代入する
    Else
    n_hWnd = hWnd
    SubClassStart = 1
    End If
    '
    #End If
    '
    End Function

    Public Function SubClassProc(ByVal hWnd As LongPtr, ByVal Msg As LongPtr, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
    '
    ' ウインドウプロシージャにメッセージを渡す前にそれを横取りして必要な処理を実行する
    '
    '
    'VBA6のときのみコンパイルする
    #If VBA6 Then
    '
    Dim zDelta As Integer
    Dim fwKeys As Integer
    Dim lScrll As LongPtr
    '
    'On Error Resume Next
    '
    'ホイールが回された場合はリストボックスをスクロールさせる
    If Msg = WM_MOUSEWHEEL Or Msg = WM_MOUSEWHEEL95 Then
    'Win98,NT4.0の場合(wParamのHIWORDは移動方向と移動量、wParamのLOWORDは同時に押された仮想キー)
    If Msg = WM_MOUSEWHEEL Then
    zDelta = CInt(wParam / 2 ^ 16)
    fwKeys = Val("&H" + Right$(Hex$(wParam), 4))
    'Win95・NT3.5の場合(wParamは移動方向と移動量、仮想キーコードは入ってこない)
    Else
    zDelta = wParam
    fwKeys = 0
    End If

    '
    With c_ListBox
    'コントロールキーが押されている場合はスクロール数を変える
    If (fwKeys And MK_CONTROL) = 0 Then
    lScrll = lMnScrll
    Else
    lScrll = lMxScrll
    End If
    'ホイールが奥に回転した場合
    If 0 < zDelta Then
    If .TopIndex < lScrll Then
    .TopIndex = 0
    Else
    .TopIndex = .TopIndex - lScrll
    End If
    'ホイールが手前に回転した場合
    Else
    If .ListCount < .TopIndex + lScrll Then
    If .ListCount - lMxScrll < 0 Then
    .TopIndex = 0
    Else
    .TopIndex = .ListCount - lMxScrll
    End If
    Else
    .TopIndex = .TopIndex + lScrll
    End If
    End If
    End With
    'ユーザーフォームのクライアント領域がフォーカスを失った場合はサブクラス化を終了する
    ElseIf Msg = WM_SETCURSOR And Val("&H" + Right$(Hex$(lParam), 4)) 1 Or _
    Msg = WM_WINDOWPOSCHANGING Or Msg = WM_NCHITTEST Then
    SubClassStop
    Exit Function
    End If
    '
    'デフォルトウィンドウプロシージャを呼び出すEnd Function
    SubClassProc = CallWindowProc(p_hWnd, hWnd, Msg, wParam, lParam)
    '
    #End If
    '
    End Function

    ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
    とあるのですが、このコードの後に記載の #If VBA7 Thenを書くのでしょうか?

    まったくVBAについての知識が乏して、お知恵をおかりしたくコメントしました。
    ご返答いただけると幸いです。
    どうぞよろしくお願いいたします。

  2. けいぞくま より:

    修正のポイントとしては次の通りで、
    #If VBA7 Then 'VBA7(64bit)時
    'ここに64bit環境で動かすための処理を書く
    '・・・・・
    #Else
    'ここに32bit時で動かすための処理を書く
    '・・・・・
    #End If
    というような感じで定義します。

    ただ、上記は「32bit環境でも64bit環境でも動くようにする」ために#IF文で条件分岐しています。
    そのため、「64bit環境でのみ動くようにソースを修正したい(32bit環境では動かなくてよい)」というケースにおいては、そもそも上記の#if VBA7 Then ・・・の分岐自体が不要です。

    VBA起動時にエラーが出ていると思いますので、どの部分でエラーとなっているのかを確認してみてください。
    ※「DLLを64bit環境でも動くようにするための手順」の部分を参考に。

    もし自分で手に負えないような場合は、旧PCで使い続ける、他に動くようなPC(32bitOffice)、などが対応案としてはあると思いますが、これだと暫定的な対応になるのかと思います。
    恒久的な対応としては、どこかのIT専門の会社(プロ)にお願いするのが一番だと思います。
    もちろん対応には相応の費用はかかるかと思いますが、お使いのVBAマクロが動かないことで止まってしまっているお仕事があると思いますので、自分で調べるのにかかる時間との兼ね合いでご判断するのが良いかと思います。

コメントを残す

CAPTCHA


関連キーワード

Twitterでフォローしよう