【中級】CurrentDb.TableDefsでリンクテーブルの接続先を起動時に自動修復する堅牢な仕組み
ファイルサーバーの移行、NASの更新、あるいは担当者の気まぐれによるフォルダ名の変更。
Accessによる業務システム開発・運用において、これほどエンジニアの頭を悩ませるイベントはない。出社早々、「先生、なんかボタンを押したらエラーが出るんですけど!」という絶望の叫び声を聞くのは、もう終わりにしよう。
バックエンドのデータベース(Accessの共有ファイルなど)が移動させられた瞬間、リンクテーブルは「パス切れ」という名の沈黙の艦隊と化す。これを手動で「リンクテーブルマネージャー」を開いて再設定させる? 20年前に置いてきた前時代的な運用だ。
今回は、Access起動時に`CurrentDb.TableDefs`を走査し、リンクテーブルの接続先を動的に修復する、エラー耐性の極めて高い自動化アーキテクチャを伝授する。
—
なぜ「力技の再リンク」は現場で破綻するのか?
多くの初級〜中級プログラマブル・エンジニアがやりがちなのが、起動時のフォームの`Open`イベントなどで、無条件にすべてのリンクテーブルに対して `.Connect` プロパティを書き換え、`.RefreshLink` を叩く手法だ。
これは実務の現場では悪手である。なぜなら以下の致命的なリスクを孕んでいるからだ。
1. ネットワークのレイテンシによる起動遅延
存在しないパスや、応答の遅いネットワークドライブに対して無駄なアクセスを試みることで、アプリケーションの起動が数秒〜数十秒フリーズする。
2. 排他制御とトランザクションの衝突
マルチユーザー環境において、不適切なタイミングでTableDefsをいじると、予期せぬロック競合や「ODBC 接続が失われました」エラーを引き起こす。
3. 例外設計の欠如
「ファイルサーバーが物理的に落ちている」「そもそも移行先のフォルダが存在しない」といった異常系でコードがクラッシュし、エンドユーザーにVBAのデバッグ画面(または強制終了)を曝け出すことになる。
真にプロフェッショナルな設計とは、「必要なときだけ、安全に、かつトランザクションや例外を完全にコントロールして修復する」ことである。
—
堅牢な自動修復アーキテクチャの設計思想
今回構築する仕組みの要件は以下の通りだ。
- トリガー: メインメニュー等の起動時フォームの初期化(`Form_Load`)
- 判定ロジック: 接続先のファイルが実際に存在するか(Io.File.Exists相当の判定)を事前にチェックし、切断されている場合のみ修復を試みる。
- 設定の外部化: 接続先パスの「正解(マスタ)」はハードコーディングせず、ローカルテーブル(あるいは専用の設定保持用テーブル)から動的に取得する。
—
プロダクションコード:自動修復モジュールの実装
以下のコードを、標準モジュール(例: `modLinkManager`)としてプロジェクトに組み込んでほしい。
エラーハンドリング、オブジェクトのライフサイクル管理、そしてログ出力まで考慮された、現場でそのまま使える実用コードだ。
Option Compare Database
Option Explicit
‘ =========================================================================
‘ モジュール名: modLinkManager
‘ 概要: リンクテーブルの接続先を動的に検証・修復する堅牢なマネージャー
‘ =========================================================================
‘ エラー定数
Private Const ERR_TABLE_NOT_FOUND As Long = 3265
Private Const ERR_FILE_NOT_FOUND As Long = 3024
Public Function AutoRepairLinkTables() As Boolean
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim targetPath As String
Dim repairedCount As Long
On Error GoTo ErrorHandler
AutoRepairLinkTables = False
repairedCount = 0
‘ 1. 移行先の正しいバックエンドパスを環境定義(またはローカル設定テーブル)から取得
‘ ※ここでは簡略化のため、特定の関数から取得するものと仮定
targetPath = GetCorrectBackendPath()
If Len(targetPath) = 0 Then
MsgBox “バックエンドの接続先パスが定義されていません。”, vbCritical, “致命的エラー”
Exit Function
End If
‘ バックエンドファイル自体の存在確認(ファイルサーバーの死活・パスミス検知)
If Dir(targetPath) = “” Then
MsgBox “バックエンドデータベースが見つかりません。” & vbCrLf & _
“パス: ” & targetPath, vbCritical, “ネットワークエラー”
Exit Function
End If
‘ 2. 現在のデータベースオブジェクトを取得(CurrentDbは毎回インスタンスが生成されるため変数に保持)
Set db = CurrentDb()
‘ 3. TableDefsコレクションを走査
For Each tdf In db.TableDefs
‘ リンクテーブル(Connectプロパティの先頭に “;DATABASE=” があるもの)を対象とする
If Left$(tdf.Connect, 10) = “;DATABASE=” Then
‘ 現在の接続先パスと正しいパスを比較し、異なっていれば修復を実行
If Not IsCorrectConnection(tdf.Connect, targetPath) Then
‘ 接続文字列を再構築(Access間リンクの前提)
tdf.Connect = “;DATABASE=” & targetPath
‘ リンク情報の更新をコミット
tdf.RefreshLink
repairedCount = repairedCount + 1
Debug.Print “修復成功: ” & tdf.Name
End If
End If
Next tdf
If repairedCount > 0 Then
MsgBox “リンクテーブルの接続先が自動修復されました。(修復数: ” & repairedCount & “件)”, vbInformation, “システム通知”
End If
AutoRepairLinkTables = True
GoTo Finally
ErrorHandler:
‘ 予期せぬエラーの捕捉
MsgBox “リンクテーブルの修復中に予期せぬエラーが発生しました。” & vbCrLf & _
“Error ” & Err.Number & “: ” & Err.Description, vbCritical, “異常終了”
AutoRepairLinkTables = False
Finally:
‘ オブジェクトの解放(メモリリーク・ロックの防止)
Set tdf = Nothing
Set db = Nothing
End Function
‘ =========================================================================
‘ 補助関数: 現在の接続先が正しいパスを指しているかを検証
‘ =========================================================================
Private Function IsCorrectConnection(ByVal currentConnect As String, ByVal expectedPath As String) As Boolean
‘ currentConnectの形式は通常 “;DATABASE=C:\path\to\backend.accdb”
Dim currentPath As String
currentPath = Mid$(currentConnect, 11)
‘ パスの大文字小文字を区別せずに比較
IsCorrectConnection = (StrComp(currentPath, expectedPath, vbTextCompare) = 0)
End Function
‘ =========================================================================
‘ 補助関数: バックエンドのパスを動的に解決する
‘ =========================================================================
Private Function GetCorrectBackendPath() As String
‘ 実運用では、ローカルの「TblEnvironment」などの設定テーブルから取得することを推奨。
‘ 今回はサンプルとして固定パスを返す(適宜書き換えてください)
‘ 例: 共有サーバーのパス
GetCorrectBackendPath = “\\fileserver\shares\database\Backend_Data.accdb”
‘ 【実務的Tips】もし開発環境と本番環境でパスが異なる場合は、
‘ ここでEnviron関数やローカルのINIファイル、環境保持用テーブルを引くように実装する。
End Function
—
現場で差が出る!コードの重要解説とアーキテクチャの急所
1. `CurrentDb` の変数保持とライフサイクルの鉄則
VBAで `CurrentDb.TableDefs…` と書くのは実はアンチパターンになり得る。
`CurrentDb` は呼び出すたびに新しいDAO.Databaseオブジェクトのインスタンスをメモリ上に生成する。ループ内で直接叩くとパフォーマンスの低下や不安定な挙動を招く。
必ずプロシージャの冒頭で `Set db = CurrentDb()` と変数に格納し、処理が終わったら `Set db = Nothing` で明示的に解放する。これがAccessのプロセスを肥大化させないためのプロの作法だ。
2. `Left$(tdf.Connect, 10) = “;DATABASE=”` による判定
すべてのTableDefがリンクテーブルとは限らない。ローカルテーブルもこのコレクションに含まれている。
ローカルテーブルの `Connect` プロパティは長さ0の文字列であるため、この判定を行わないと型ミスマッチや予期せぬエラーで沈没する。確実に外部接続(Access間リンク)であるものだけをフィルタリングする必要がある。
3. サイレント失敗をさせない設計
「パスが切れていたから勝手に直しました」というログは裏で残しつつ、ユーザーが業務を継続できるようにする。しかし、サーバー自体が落ちている(`Dir(targetPath) = “”` が真)場合は、無駄なリトライを避け、即座にユーザーへ明確なアラートを出すことで「システムのフリーズ」と勘違いさせないUI/UXを担保する。
—
起動時フォームへの組み込み方
この強力な仕組みをシステムに組み込むのは極めて簡単だ。
最初に立ち上がるメインフォーム(例: `frmMainMenu`)の `Load` イベントに、たった1行記述するだけである。
Private Sub Form_Load()
‘ アプリケーション起動時にリンクテーブルの整合性を担保
If Not AutoRepairLinkTables() Then
‘ 修復に致命的な失敗をした場合はアプリケーションを安全に終了させる
DoCmd.Quit
End If
End Sub
これだけで、明日からファイルサーバーのIPアドレスが変わろうとも、フォルダ階層が再編されようとも、バックエンドの物理ファイルさえ正しければ、Accessは自律的に復活を果たす。
—
結び:属人化した保守運用からの脱却
「リンクテーブルが切れたら、開発者を呼ぶか、マニュアルを見ながら手動で直す」
そんな非効率な属人化の時代は、このコードをもって終わりにするべきだ。
堅牢なコードとは、単にエラーが出ないことではない。「異常な環境変化が起きたとき、システム自身がそれを検知し、静かに、かつ確実に自己修復を試みる構造」のことである。
ぜひ、あなたの開発するAccessシステムにもこの知見を組み込み、エンドユーザーからの無駄な問い合わせのストレスから解放されてほしい。
