こんにちは!Visio VBAの自動化の世界へようこそ。
マクロの記録ボタンを押してコードを覗いてみたものの、「なんだか難しそうだな…」「動かしているうちに、だんだん動作が重くなってフリーズしてしまう…」そんな壁にぶつかっていませんか?
今回は、数千個の図形(シェイプ)が配置された巨大なVisioファイルを軽快に操るための、最も重要で、そしてプログラミング初学者が一番見落としがちなテーマ「オブジェクト変数の確実な解放と `Nothing` 代入の作法」について、現場の知見をたっぷり詰め込んで優しく解説していきます。
ここをクリアすれば、あなたの書くコードは見違えるほど洗練され、メモリリーク(メモリの肥大化)とは無縁のプロフェッショナルなものになりますよ。それでは、一緒に本質を紐解いていきましょう!
—
1. なぜVisio VBAはメモリリークしやすいのか?
Visioで図面を描くとき、私たちは無意識のうちに膨大な数のオブジェクトを扱っています。
- アプリケーション (`Application`)
- 図面ファイル (`Document`)
- ページ (`Page`)
- 個々の図形 (`Shape`)
VBA(Visual Basic for Applications)でこれらのオブジェクトをコードから操作するとき、裏側ではWindowsのメモリ上に「参照(ポインタ)」という名の見えない橋が架かります。
「ゾンビオブジェクト」の恐怖
ループ処理などで `Set myShape = activePage.Shapes(1)` のように変数へオブジェクトを代入し続けると、VBAはメモリ上にその場所を確保し続けます。
処理が終わったからといって、うっかり変数をそのまま放置したり、別のオブジェクトを上書きしたりすると、古いオブジェクトへの参照が宙ぶらりんのままメモリに居座り続ける現象(メモリリーク)が発生します。
これが積み重なると、Visioが突然フリーズしたり、「メモリ不足です」という冷酷なエラーメッセージを吐いて強制終了したりする原因になります。数メガバイトある巨大な図面を扱うなら、なおさら厳密なメモリ管理が必要不可欠なのです。
—
2. 鉄則:使い終わったオブジェクトは `Nothing` で「お片付け」する
このメモリリークを防ぐための特効薬が、`Set 変数名 = Nothing` というコードです。
「使い終わったら、その変数とオブジェクトのつながりを自らハサミでチョキンと断ち切り、メモリを解放しなさい」というVBAへの命令になります。
図解するなら、こんなイメージです:
1. `Set shp = …` = 部屋(メモリ)の鍵を渡してオブジェクトを自分の手元に呼ぶ
2. (処理)
3. `Set shp = Nothing` = 「用事は済んだので鍵を返します。この部屋はもう使わないので掃除してください」とOSに伝える
この後始末をコードのルーティンとして組み込めるかどうかが、初心者から中級者へステップアップするための大きな分かれ道です。
—
3. 【実践】巨大な図面を安全に処理するリファレンスコード
それでは、実際に「ページ内の全シェイプの色を安全に変更し、メモリを綺麗に掃除する」プロ仕様のコードを見てみましょう。
そのままコピペして、VBE(VBAエディタ)の標準モジュールに貼り付けて試すことができます。
Sub CleanUpShapesExample()
‘ 宣言セクション
Dim vsoApp As Vis.Application
Dim vsoPage As Vis.Page
Dim vsoShape As Vis.Shape
Dim i As Long
On Error GoTo ErrorHandler ‘ エラー時に確実にクリーンアップへ飛ばすお作法
‘ 1. アプリケーションの参照を取得
Set vsoApp = Visio.Application
‘ アクティブページを取得
Set vsoPage = vsoApp.ActivePage
‘ 2. 効率的なループ処理(シェイプの数が多い場合は後ろから回すのが鉄則)
Dim shapeCount As Long
shapeCount = vsoPage.Shapes.Count
If shapeCount > 0 Then
For i = shapeCount To 1 Step -1
‘ 個別のシェイプを変数に格納
Set vsoShape = vsoPage.Shapes(i)
‘ — ここに実際の処理を書く(例:シェイプの塗りつぶし色を赤にする) —
‘ ※VisioのColorはRGB(R, G, B)関数で指定します
vsoShape.CellsU(“FillForegnd”).ResultIU = RGB(255, 0, 0)
‘ 3. ループの都度、一時的なオブジェクト変数を必ずNothingで解放する!
Set vsoShape = Nothing
Next i
End If
MsgBox “すべての処理がメモリリークなしで完了しました!”, vbInformation
‘ — 4. 終了前の大掃除(上位オブジェクトの解放) —
Set vsoPage = Nothing
Set vsoApp = Nothing
Exit Sub
ErrorHandler:
‘ エラーが発生した場合でも、メモリが取り残されないようにここで必ず解放する
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical
Set vsoShape = Nothing
Set vsoPage = Nothing
Set vsoApp = Nothing
End Sub
コードの重要ポイント解説
- ループを後ろから回す (`Step -1`)
シェイプを削除したり動的に操作する場合、前から回すとインデックスがズレてバグの温床になります。巨大なコレクションを安全に舐めるときは「後ろから数える」のがエンジニアの常道です。
- ループ内での `Set vsoShape = Nothing`
ここが一番のキモです!`For` ループの中で `vsoShape` を使い回す際、ループの最後に `Nothing` を代入しておかないと、古い参照がメモリに残ったまま次のループに入ってしまい、じわじわとメモリを圧迫します。
- `On Error GoTo` による保険
途中で予期せぬエラー(シェイプが保護されている等)でコードが中断したとき、解放処理がスキップされてVisioが不安定になるのを防ぎます。エラーハンドラ内でも必ず `Nothing` を呼ぶのがプロのこだわりです。
—
4. ここをクリアすれば、Visio VBAの基本はバッチリ!
いかがでしたでしょうか?
「ただ動くだけのコード」から「メモリ負荷を考慮した美しいコード」へ一歩踏み出すことができましたね。
- オブジェクト変数を取得したら、責任を持って最後に `Nothing` を代入する。
- 数が多いループ処理では、一時変数をこまめにクリアする。
- エラー時を見据えた後始末(エラーハンドリング)を忘れない。
この作法を身につけておけば、どれほど巨大で複雑なVisioファイルが相手でも、軽快に、そして安定して動作するマクロツールを自在に組み上げることができます。
あなたの自動化ライフが、より快適で知的でワクワクするものになりますように。それでは、次のステップの現場でお会いしましょう!
