<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:blogChannel="http://backend.userland.com/blogChannelModule" >
  <channel>
  <title>VBA &amp; Next: VBA Lab｜GAS・Office Script移行とVBAアプリ開発</title>
  <link>http://vbafrom20260822.blog.shinobi.jp/</link>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://vbafrom20260822.blog.shinobi.jp/RSS/" />
  <description>VBAによる実用アプリケーションの構築、およびGAS（Google Apps Script）やOffice Scriptへのモダンな移行パスを検証・解説するテックブログ。現場で即戦力となるコードモジュールや再利用可能な実用部品を継続的に提供します。</description>
  <lastBuildDate>Sun, 04 Oct 2026 12:37:20 GMT</lastBuildDate>
  <language>ja</language>
  <copyright>© Ninja Tools Inc.</copyright>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" />

    <item>
    <title>【Excel VBA】On Error Resume Nextを放置してない？バグを隠蔽しないための「On Error GoTo 0」の正しい作法</title>
    <description>
    <![CDATA[<p>Excel VBAでエラーを回避しようとして、コードの冒頭に <code>On Error Resume Next</code> を書いたことはありませんか？</p>
<p>「エラーでマクロが途中で止まらなくなって便利！」と気軽に使いがちですが、実はこれ、使い方を誤ると「重大なバグを隠蔽してデバッグ不可能にする」という最大の事故原因になります。</p>
<p>この記事では、<code>On Error Resume Next</code> が持つ危険性と、それを安全に制御するためのペアとなる命令 <code>On Error GoTo 0</code> の関係性、そしてバグを生ませない正しい書き方を解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. On Error Resume Next とは？（危険な「居眠り運転」モード）</h3>
<p><code>On Error Resume Next</code> は、コードの実行中にエラーが発生してもマクロを停止させず、「そのまま無視して次の行の処理に進め」という命令です。</p>
<p>ファイルを開く処理や、特定のオブジェクトが存在するかチェックする処理など、「エラーが起きる可能性を想定して安全にスルーしたい場面」で非常に重宝します。</p>
<p>しかし、一度この命令を実行すると、プロシージャ（SubやFunction）が終わるまでVBA全体が<b>「どんなエラーが起きても一切知らせてくれない居眠り運転モード」</b>になってしまいます。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. なぜ「On Error GoTo 0」がないとヤバいのか？（バグ隠蔽の恐怖）</h3>
<p><code>On Error Resume Next</code> を書いたあと、解除せずに放置すると何が起きるでしょうか？</p>
<p>例えば、後続のコードで変数名のスペルミス（タイポ）をしたり、ゼロ割り計算が発生したりしても、VBAはエラーメッセージを出さずに黙ってその行をスキップします。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 15px;">Sub DangerousExample()<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error Resume Next ' エラー無視モード開始<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 1. 本当にエラーをスルーしたい処理<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim sheet As Worksheet<br />
&nbsp;&nbsp;&nbsp;&nbsp;Set sheet = Worksheets("存在しないシート")<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 2. ここで解除しないまま、別の処理を続けると&hellip;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim total As Long<br />
&nbsp;&nbsp;&nbsp;&nbsp;totall = 100 + 200 ' ★変数名を「totall」とタイポ！本来はエラーだが黙ってスキップされる<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "計算結果: " &amp; total ' 結果は「0」になり、なぜ動かないのか原因不明に！<br />
End Sub</div>
<p>このように、「本当はエラーを出して教えてほしいバグ」まで隠蔽されてしまい、不具合の原因追求（デバッグ）が極めて困難になります。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. バグを出さない！「ピンポイント挟み」の設計パターン</h3>
<p>バグを出さないための鉄則は、<b>「エラーを無視させたい最小限の範囲（1〜2行）だけを挟み込み、すぐに解除する」</b>ことです。ここで登場するのが <code>On Error GoTo 0</code> です。</p>
<p><code>On Error GoTo 0</code> は、VBAのエラー無視モードを強制終了し、「通常の安全状態（エラーが起きたら黄色い画面で知らせる状態）」にリセットする命令です。</p>
<p><b>【正しいコードの書き方例】</b></p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Sub SafeExample()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim lockPath As String<br />
&nbsp;&nbsp;&nbsp;&nbsp;lockPath = Environ("TEMP") &amp; "\MyApp_Running.lock"<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim fileNo As Integer<br />
&nbsp;&nbsp;&nbsp;&nbsp;fileNo = FreeFile<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ==========================================<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 1. エラー無視モードを開始（ピンポイント）<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error Resume Next<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 2. エラーが起きる可能性のある処理だけを実行<br />
&nbsp;&nbsp;&nbsp;&nbsp;Open lockPath For Binary Access Read Write Lock Read Write As #fileNo<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 3. エラーが起きたかどうか判定（Err.Number をチェック）<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim errCode As Long<br />
&nbsp;&nbsp;&nbsp;&nbsp;errCode = Err.Number<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 4. すぐにエラー無視モードを解除！通常状態に戻す<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error GoTo 0<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ==========================================<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 5. 判定結果に応じて処理を分ける<br />
&nbsp;&nbsp;&nbsp;&nbsp;If errCode &lt;&gt; 0 Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "アプリはすでに起動しています。", vbExclamation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exit Sub<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ここから下は通常の安全状態なので、タイポや計算ミスがあればVBAがしっかり教えてくれる！<br />
End Sub</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. まとめ</h3>
<ul>
<li><code>On Error Resume Next</code> は、エラーを無かったことにする魔法ではなく「一時的な判定用ツール」。</li>
<li>解除せずに放置すると、後続コードのバグやタイポが隠蔽され、デバッグ地獄に陥る。</li>
<li>危険な処理を通過させたら、直後に必ず <code>On Error GoTo 0</code> を書いて通常状態に戻す。</li>
</ul>
<p><b>「On Error Resume Next を書いたら、セットで On Error GoTo 0 を書く」</b>。この基本を守るだけで、VBAコードの安全性と信頼性は劇的に向上します！<br />
<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBA知識</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/46/</link>
    <pubDate>Sun, 04 Oct 2026 12:37:19 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/46</guid>
  </item>
    <item>
    <title>【Excel VBA】アプリの本体とデータを分けるべき？VBA開発で知っておくべき「データ分離」の設計作法</title>
    <description>
    <![CDATA[<p>Excel VBAでツールやアプリを開発するとき、1つの <code>.xlsm</code> ファイルの中にユーザーフォームも、VBAコードも、蓄積するデータも全部詰め込んでいませんか？</p>
<p>開発初期や自分1人で使う分にはこれで問題なく動きますが、いざ運用を開始してしばらく経つと、ある「絶望的な問題」にぶち当たることになります。</p>
<p>この記事では、VBAで本格的なアプリを作る際に、なぜ「プログラム本体（画面・処理）」と「データ」を分離するべきなのか、その理由と実務で使える構成パターンを解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. 「一体型（1ファイル）」が抱える運用上の罠</h3>
<p>1つのExcelファイルにすべてを詰め込む「一体型」で開発・運用を続けると、次のようなトラブルが発生します。</p>
<ul>
<li><b>バージョンアップ時の「データ移行地獄」</b>：機能追加やバグ修正をした新しいファイル（v1.1.xlsm）を配布する際、ユーザーが旧ファイル（v1.0.xlsm）に入力した大量のデータを新しいファイルへ手動で移し替えなければならなくなります。</li>
<li><b>ファイル破損によるデータ全喪失リスク</b>：Excelマクロは、長年運用してデータ量が肥大化したり処理を重ねたりすると、ファイル自体が破損して開かなくなるリスクが少なからずあります。一体型だと、プログラムだけでなく貴重な蓄積データまで一緒に消えてしまいます。</li>
<li><b>複数人で使えない</b>：1つのファイルを誰かが開いていると「読み取り専用」になり、他の人がデータを更新できなくなります。</li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. プログラムとデータを分離する（データ分離）のメリット</h3>
<p>プログラム（UserFormや標準モジュール）を置く「Appファイル」と、蓄積データを保存する「Dataファイル」を明確に分けることで、これらの問題が一気に解決します。</p>
<ul>
<li><b>アップデートが「ファイルの差し替え」だけで完結する</b><br />
データ側は一切触らず、プログラム側のファイル（App.xlsm）だけを最新版に差し替えれば、ユーザーのデータを1件も移行させることなく一瞬でバージョンアップが完了します。</li>
<li><b>データ保護と堅牢性の向上</b><br />
万が一VBAの処理中にクラッシュしたりプログラムファイルが壊れたりしても、データファイルは別にあるため安全です。バックアップや復旧も容易になります。</li>
<li><b>将来的な機能拡張（DB化・複数人利用）がスムーズ</b><br />
データアクセス部分が独立しているため、将来的にデータ保存先をExcelからAccess（データベース）やクラウドへ移行したくなった場合でも、プログラム側の最小限の修正で対応できます。</li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. 実務で使われる「データ分離」の3つの構成パターン</h3>
<p>分離型アプリを作る際、実務では用途や規模に合わせて主に以下の3パターンが使われます。</p>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;">
<thead>
<tr style="background-color: #e3f2fd; border-bottom: 2px solid #90caf9;"><th style="padding: 10px; text-align: left; width: 25%;">構成</th><th style="padding: 10px; text-align: left; width: 45%;">概要</th><th style="padding: 10px; text-align: left; width: 30%;">おすすめの用途</th></tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #e0e0e0;">
<td style="padding: 10px;"><b>① App.xlsm ＋ Data.xlsx</b></td>
<td style="padding: 10px;">データをマクロなしの普通のExcelブック（.xlsx）に保存する。VBAから画面非表示で裏で開いて読み書きする。</td>
<td style="padding: 10px;">最も手軽。集計ツールや数千件程度の小〜中規模データ管理。</td>
</tr>
<tr style="border-bottom: 1px solid #e0e0e0; background-color: #f9f9f9;">
<td style="padding: 10px;"><b>② App.xlsm ＋ Access (.accdb)</b></td>
<td style="padding: 10px;">データ管理にMicrosoft Accessデータベースを使用し、ADO（SQL）経由で高速に読み書きする。</td>
<td style="padding: 10px;">本格アプリの王道。大量データ（数万件〜）の高速処理や複数人同時利用。</td>
</tr>
<tr style="border-bottom: 1px solid #e0e0e0;">
<td style="padding: 10px;"><b>③ App.xlsm ＋ CSV / Config</b></td>
<td style="padding: 10px;">設定情報やログ出力、マスターデータをテキスト形式（CSVやJSON）で保持する。</td>
<td style="padding: 10px;">アプリの初期設定値や動作ログの保存。</td>
</tr>
</tbody>
</table>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. まとめ</h3>
<ul>
<li>「使い捨ての簡易ツール」や「1回きりのデータ整形」なら、一体型（1ファイル）でも問題ない。</li>
<li>「日々データを蓄積する」「機能を追加・修正していく」「他人に配って使わせる」アプリなら、最初から「プログラム」と「データ」を分離する設計が鉄則。</li>
</ul>
<p>VBA開発で「保守性の高いコード」を書く第一歩は、プログラミング言語の書き方だけでなく、こうしたファイル構成（アーキテクチャ）の設計から始まります。長期間安心して使えるVBAアプリを目指して、ぜひデータ分離設計を取り入れてみてください！<br />
<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBA知識</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/45/</link>
    <pubDate>Sun, 04 Oct 2026 12:32:49 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/45</guid>
  </item>
    <item>
    <title>【Excel VBA】UserFormのボタン連打防止とモジュール分離！イベント処理の設計と書き方の注意点</title>
    <description>
    <![CDATA[<p>Excel VBAで開発をしていると、ユーザーインターフェース（入力画面）を作成するために「UserFormモジュール」にコードを書く機会があります。例えば、次のようなボタンのクリックイベントプロシージャを見たことがあるかもしれません。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Private Sub BtnStart_Click()<br />
&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "処理を開始します"<br />
End Sub</div>
<p>このコード自体は「ボタンが押されたときにメッセージを表示する」というシンプルなものですが、ここにデータ処理や集計などのロジックまで何でもかんでも書き込んでしまうのは、保守性や安全性の観点から大きなNGです。</p>
<p>この記事では、UserFormモジュールとはそもそも何者なのか、そして「なぜビジネスロジックをここに書いてはいけないのか（連打防止とモジュール分離の設計）」を解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. UserFormモジュールとは？（標準モジュールとの違い）</h3>
<p>VBAのプロジェクトエクスプローラを開くと、通常の「標準モジュール（Module1など）」のほかに、フォームに対応した「UserFormモジュール（UserForm1など）」が存在します。</p>
<ul>
<li><b>標準モジュール</b>：どこからでも呼び出せる汎用的なマクロや関数（プロシージャ）を置く場所。</li>
<li><b>UserFormモジュール</b>：画面上のコントロール（ボタンやテキストボックス等）のイベント（ボタンが押された、入力値が変わったなど）を受け取り、UIを描画・操作する特別な場所。</li>
</ul>
<p>冒頭の <code>BtnStart_Click</code> も、フォーム上のボタンが押された瞬間に自動で発火するイベントプロシージャの一つです。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. 「ここに書きすぎてはダメ！」な理由とビジネスロジックの分離</h3>
<p>イベントを簡単に拾えるからといって、UserFormモジュールの中にデータの加工、ファイルの読み書き、複雑な計算といったビジネスロジック（処理の本質）を直接書き込んでしまうのはNGです。</p>
<p>理由は主に以下の通りです。</p>
<ul>
<li><b>コードが肥大化・複雑化する</b>：1つのフォームモジュールに何百行ものデータ処理が詰まり、「あの処理はどこに書いてあったっけ？」と迷子になります。</li>
<li><b>再利用性が極めて低い</b>：フォームモジュール内のコードは原則そのフォームに紐づいているため、ショートカットキーや他のシートから同じ処理を実行したくなったときに流用できません。</li>
<li><b>二重実行（連打）対策が漏れやすい</b>：画面のボタンを無効化するだけでは、キーボードショートカットや外部からの直接呼び出しによる割り込み実行を防ぎきれません。</li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. 「画面制御と処理」を分ける設計思想</h3>
<p>保守性と安全性の高いプログラミングを目指すなら、UserFormモジュールは画面操作（UI制御）に特化させ、実際の処理は標準モジュールへ任せるのがベストプラクティスです。</p>
<p>UserFormの役割は、あくまで**「UI側の連打防止を行い、ユーザーの操作を受け取って、標準モジュール側にあるメイン処理へバトンを渡すこと」**だけに留めましょう。</p>
<p><b>【正しいコードの書き方の例】</b></p>
<p><b>① UserFormモジュール側（受付・UI制御のみ）</b></p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 15px;">Private Sub BtnStart_Click()<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 1. ボタンを押せなくし、ユーザーの連打を防ぐ<br />
&nbsp;&nbsp;&nbsp;&nbsp;Me.BtnStart.Enabled = False<br />
&nbsp;&nbsp;&nbsp;&nbsp;Me.BtnStart.Caption = "処理中..."<br />
&nbsp;&nbsp;&nbsp;&nbsp;DoEvents ' 画面描画を即座に更新<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 2. 受付だけ行い、実際の処理は標準モジュールのプロシージャを呼び出す<br />
&nbsp;&nbsp;&nbsp;&nbsp;Call MainProcess<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 3. 処理完了後、ボタンを元の状態に戻す<br />
&nbsp;&nbsp;&nbsp;&nbsp;Me.BtnStart.Enabled = True<br />
&nbsp;&nbsp;&nbsp;&nbsp;Me.BtnStart.Caption = "実行"<br />
End Sub</div>
<p><b>② 標準モジュール側（ビジネスロジックと二重実行ガードを記述）</b></p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Public Sub MainProcess()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Static isRunning As Boolean<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ショートカットキー等の割り込み実行による重複を防ぐガード<br />
&nbsp;&nbsp;&nbsp;&nbsp;If isRunning Then Exit Sub<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;isRunning = True<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error GoTo CleanUp<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ==========================================<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ここにデータの集計や加工などのビジネスロジックをガッツリ書く<br />
&nbsp;&nbsp;&nbsp;&nbsp;Application.Wait Now + TimeValue("00:00:03")<br />
&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "処理が完了しました！", vbInformation<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ==========================================<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
CleanUp:<br />
&nbsp;&nbsp;&nbsp;&nbsp;isRunning = False<br />
End Sub</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. まとめ</h3>
<ul>
<li>UserFormモジュールは、画面の表示制御や連打防止（<code>Enabled = False</code> など）を行う窓口として割り切って使う。</li>
<li>実際の計算やデータ処理などのビジネスロジックは標準モジュールに分離（カプセル化）する。</li>
<li>標準モジュール側にも実行フラグ（<code>Static</code> 変数）を仕込むことで、UIを経由しない二重起動も完璧に防御できる。</li>
</ul>
<p>「フォームのボタンイベントだから全部ここに書いちゃえ」とコードを詰め込むと、後で機能拡張や不具合対応をするときに苦労することになります。役割をすっきり分けて、美しく堅牢なVBAコードを保ちましょう！<br />
<br />
</p>]]>
    </description>
    <category>VBA知識</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/44/</link>
    <pubDate>Sun, 04 Oct 2026 11:56:11 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/44</guid>
  </item>
    <item>
    <title>【VBA設計】「契約による設計（DbC）」の考え方をVBAのコメントとコード設計に取り入れる方法</title>
    <description>
    <![CDATA[<p>Excel VBAで保守性の高いコードを書くためには、単に処理を動かすだけでなく、<b>「契約による設計（DbC: Design by Contract）」</b>の概念をコメントやエラーハンドリングに取り入れることが非常に効果的です。</p>
<p>この記事では、「戻り値」と「事後条件」の明確な違いから、DbCの考え方を取り入れた推奨コメントテンプレート、さらに事前条件をコードで強制する実践的なテクニックまでを解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. 「戻り値」と「事後条件」の違い</h3>
<p>Excel VBAにおいては、戻り値（出力）と事後条件を以下の理由から明確に区別して考える必要があります。</p>
<ul>
<li><b>戻り値 (Return Value)</b>: プロシージャが計算・処理した結果として呼び出し元へ返す「値」のこと。（Functionプロシージャのみ）</li>
<li><b>事後条件 (Postconditions)</b>: プロシージャが正常終了した時点で、「システム（またはオブジェクト）がどのような状態になっていることを保証するか」という約束です。</li>
</ul>
<div style="background-color: #fff8e1; border-left: 4px solid #ffb300; padding: 12px; margin-bottom: 20px;"><b> なぜ戻り値の説明だけではダメなのか？</b><br />
VBAでは、「セルの値を書き換える」「シートを非表示にする」「ファイルを出力する」といった副作用（状態の変化）を伴う処理が非常に多いからです。<br />
<br />
<b>【例：データをデータベースに保存し、成功フラグを返す関数】</b><br />

<ul>
<li><b>戻り値:</b> 成功なら <code>True</code>、失敗なら <code>False</code></li>
<li><b>事後条件:</b><br />
・(Trueの場合) 指定されたデータがDBに書き込まれていること。<br />
・(Trueの場合) 対象シートの背景色がグレーアウトされていること。</li>
</ul>
特に戻り値を持たない <code>Sub</code> プロシージャ においては、「事後条件＝このプロシージャが終わった時に何が保証されているか」を明記することが、まさに処理の目的そのものになります。</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. 契約による設計を取り入れた推奨フォーマット</h3>
<p>前回のフォーマットの「前提条件・依存関係」と「出力」部分を、DbCの概念である <code>Requires</code>（事前条件） と <code>Ensures</code>（事後条件） に再構築した、より堅牢なコメントテンプレートです。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">' ==============================================================================<br />
' [機能名] プロシージャの概要を1〜2行で記述<br />
'<br />
' [処理内容]<br />
' &nbsp; ・処理のステップやビジネスロジックを簡潔に記述<br />
'<br />
' [引数]<br />
' &nbsp; @param&nbsp; {型} 引数名 &nbsp; &nbsp; &nbsp; : 必須/任意 : 引数の説明<br />
'<br />
' [事前条件 (Requires)]<br />
' &nbsp; ・呼び出し元が満たしておくべき契約（満たない場合の動作は保証しない）<br />
' &nbsp; ・例: ワークブック "Data.xlsx" が開かれていること<br />
' &nbsp; ・例: 引数 `rowIdx` が 1 以上であること<br />
'<br />
' [事後条件 (Ensures)]<br />
' &nbsp; ・プロシージャ終了時に保証されるシステムの状態（副作用を含む）<br />
' &nbsp; ・例: "Result" シートの A1:D10 に集計結果が書き込まれていること<br />
' &nbsp; ・例: 処理対象のファイルが別フォルダに移動されていること<br />
'<br />
' [戻り値] (Functionの場合のみ)<br />
' &nbsp; @return {型} 戻り値の説明（事後条件を満たしたかどうかの結果など）<br />
'<br />
' [例外処理 (Throws)]<br />
' &nbsp; ・事前条件が満たされているにも関わらず発生しうるシステムエラーの挙動<br />
'<br />
' [履歴]<br />
' &nbsp; YYYY/MM/DD&nbsp; [氏名]&nbsp; 新規作成<br />
' ==============================================================================</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. 実践における DbCコメントのメリット</h3>
<p>この書き方をチームで徹底すると、保守や引き継ぎにおいて以下のような絶大なメリットがあります。</p>
<ul>
<li><b>責任の切り分けが明確になる</b>：エラーが起きた際、「事前条件を満たさずに呼び出した側（親）」が悪いのか、「事後条件を満たせなかった側（子）」の実装が悪いのか、責任の所在（契約違反者）が瞬時に判明します。</li>
<li><b>ブラックボックスでも安全に使える</b>：「事前条件」さえ整えて呼び出せば、「事後条件」の状態になることが約束されているため、中の複雑なコード（処理内容）を一切読まなくても、安全にそのプロシージャを再利用できます。</li>
</ul>
<div style="background-color: #e3f2fd; border-left: 4px solid #1976d2; padding: 12px; margin-bottom: 20px;"><b> さらに堅牢にするためのワンポイント（事前条件のコード化）</b><br />
VBAには本格的な Assert 機能がないため、プロシージャの冒頭で「事前条件」をコードとしてチェックする設計にするのが非常にお勧めです。<br />
<br />

<div style="font-family: monospace; background-color: #ffffff; border: 1px solid #bbdefb; padding: 10px; margin-top: 8px; line-height: 1.5;">Sub UpdateData(ByVal targetRow As Long)<br />
&nbsp;&nbsp;&nbsp;&nbsp;' --- 事前条件のチェック（契約違反は即座にはじく） ---<br />
&nbsp;&nbsp;&nbsp;&nbsp;If targetRow &lt; 1 Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Err.Raise Number:=513, Description:="事前条件違反: targetRowは1以上である必要があります。"<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' (以降、メイン処理)<br />
End Sub</div>
</div>
<p>このように、コメントに書いた「事前条件」「事後条件」を意識することで、VBAのコードの品質は他のモダン言語に匹敵するレベルまで引き上げることが可能です。ぜひ毎日の開発に取り入れてみてください！<br />
<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBA知識</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/43/</link>
    <pubDate>Sun, 04 Oct 2026 11:37:15 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/43</guid>
  </item>
    <item>
    <title>【Excel VBA】ローカルファイルの最終更新日時を取得する！FileSystemObjectより手軽なFileDateTimeの使い方</title>
    <description>
    <![CDATA[<p>Excel VBAでマクロを自動化していると、<b>「処理対象のファイルがいつ更新されたものか（タイムスタンプ）」</b>を確認したくなる場面がよくあります。</p>
<p>VBAでファイルの最終更新日時を取得する方法としては <code>FileSystemObject (FSO)</code> を使う方法が有名ですが、実は組み込み関数の <b><code>FileDateTime</code></b> を使うと、よりシンプルで手軽に取得できます。</p>
<p>この記事では、<code>FileDateTime</code> 関数の基本的な使い方から、実務で必須となる「エラー対策」を施した関数、そして実際にローカルファイル（<code>C:\temp</code>）から取得した実行結果までをご紹介します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. 実装コード（関数 ＆ テストコード）</h3>
<p>安全にファイル存在チェックを行い、タイムスタンプを返す汎用関数と、動作確認用のテストプロシージャです。これを<b>標準モジュール</b>に貼り付けて使用します。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">' [機能名] 指定されたファイルの最終更新日時（タイムスタンプ）を取得する<br />
' [引数] filePath : 必須 : 対象ファイルのフルパス（ローカルパス）<br />
' [戻り値] タイムスタンプ（Date型）または Empty（存在しない場合）<br />
Public Function GetFileTimestamp(ByRef filePath As String) As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 事前存在チェック（Dir関数）<br />
&nbsp;&nbsp;&nbsp;&nbsp;If Dir(filePath) = "" Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetFileTimestamp = Empty<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exit Function<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' タイムスタンプの取得（安全対策としてエラーハンドリング）<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error GoTo ErrorHandler<br />
&nbsp;&nbsp;&nbsp;&nbsp;GetFileTimestamp = FileDateTime(filePath)<br />
&nbsp;&nbsp;&nbsp;&nbsp;Exit Function<br />
<br />
ErrorHandler:<br />
&nbsp;&nbsp;&nbsp;&nbsp;GetFileTimestamp = Empty<br />
End Function<br />
<br />
' [機能名] 動作テスト用プロシージャ<br />
Public Sub Test_GetFileTimestamp()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim targetPath As String<br />
&nbsp;&nbsp;&nbsp;&nbsp;targetPath = "C:\temp\test.txt"<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "=== [テスト] ファイルタイムスタンプ取得 ==="<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "対象パス: " &amp; targetPath<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim ts As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;ts = GetFileTimestamp(targetPath)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;If IsEmpty(ts) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "結果: 指定されたファイルが見つからないか、取得できませんでした。"<br />
&nbsp;&nbsp;&nbsp;&nbsp;Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "結果: 成功"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "ファイルのタイムスタンプ: " &amp; Format(ts, "yyyy/mm/dd hh:nn:ss")<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
End Sub</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. イミディエイトウィンドウの実行結果</h3>
<p>実際に <code>C:\temp\test.txt</code> を用意して <code>Test_GetFileTimestamp</code> を実行したところ、次のように正確な更新日時が取得できました。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">=== [テスト] ファイルタイムスタンプ取得 ===<br />
対象パス: C:\temp\test.txt<br />
結果: 成功<br />
ファイルのタイムスタンプ: 2026/10/04 06:28:33</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. このコードのポイントと注意点</h3>
<ul>
<li><b>FileDateTime関数の手軽さ</b>：オブジェクトの生成（<code>CreateObject</code>）が不要なため、VBAの標準機能だけで非常にスッキリ記述できます。</li>
<li><b>必ず存在チェックを入れること</b>：<code>FileDateTime</code> は、指定したファイルが存在しない場合に実行時エラー（強制終了）になります。そのため、事前に <code>Dir</code> 関数で存在確認を行うか、エラーハンドラを用意するのが実務では必須です。</li>
<li><b>ローカルパスが必須</b>：OneDriveのWebURL（<code>https://...</code>）などのクラウドパスは直接指定できません。必ず <code>C:\...</code> のようなローカルの物理パスに落とし込んで使用してください。</li>
</ul>
<p>「古いログファイルを自動で掃除したい」「特定のデータファイルが新しく更新されているかチェックしたい」といった場面で非常に役立ちますので、ぜひ活用してみてください！<br />
<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBAサンプル</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/42/</link>
    <pubDate>Sat, 03 Oct 2026 21:40:06 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/42</guid>
  </item>
    <item>
    <title>【Excel VBA】配列の重複削除をスマートに実装する（Scripting.Dictionary活用法とテストコード付き）</title>
    <description>
    <![CDATA[<p>Excel VBAでプログラミングをしていると、<b>「配列の中に含まれている重複した値を削除して、一意な（ユニークな）リストを作りたい」</b>という場面に頻繁に出くわします。</p>
<p>セル範囲（Range）であれば <code>RemoveDuplicates</code> メソッドが使えますが、メモリ上の配列（Array）を処理するには少し工夫が必要です。</p>
<p>この記事では、高速かつシンプルに配列の重複を削除できる <b><code>Scripting.Dictionary</code></b> を使った汎用関数と、その実装コード・実行結果をご紹介します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. サンプルコード（重複削除関数 ＆ テストコード）</h3>
<p>文字列や数値が混在するVariant型の1次元配列を受け取り、重複を排除した新しい配列を返す関数と、動作確認用のテストプロシージャです。これを<b>標準モジュール</b>に貼り付けて使用します。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">' [機能名] 配列内の重複要素を削除し、一意な要素のみの配列を返す<br />
' [引数] targetArray : 重複を削除したい1次元配列（Variant）<br />
' [戻り値] 重複が削除された1次元配列<br />
Public Function RemoveDuplicates(ByRef targetArray As Variant) As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 引数が配列かどうかのチェック<br />
&nbsp;&nbsp;&nbsp;&nbsp;If Not IsArray(targetArray) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Err.Raise 13, , "引数に配列を指定してください。"<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 配列が空の場合のハンドリング<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error Resume Next<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim lB As Long, uB As Long<br />
&nbsp;&nbsp;&nbsp;&nbsp;lB = LBound(targetArray)<br />
&nbsp;&nbsp;&nbsp;&nbsp;uB = UBound(targetArray)<br />
&nbsp;&nbsp;&nbsp;&nbsp;If Err.Number &lt;&gt; 0 Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RemoveDuplicates = Array()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exit Function<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error GoTo 0<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' Dictionaryオブジェクトの生成（重複排除用）<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim dict As Object<br />
&nbsp;&nbsp;&nbsp;&nbsp;Set dict = CreateObject("Scripting.Dictionary")<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim i As Long<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim v As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;For i = lB To uB<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v = targetArray(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;If Not dict.Exists(v) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dict.Add v, True<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;Next i<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 結果の配列を返す（dict.Keysは0ベースの配列を返す）<br />
&nbsp;&nbsp;&nbsp;&nbsp;If dict.Count = 0 Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RemoveDuplicates = Array()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RemoveDuplicates = dict.Keys<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
End Function<br />
<br />
' [機能名] 動作テスト用プロシージャ<br />
Public Sub Test_RemoveDuplicates()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim i As Long<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' --- テスト1: 文字列の重複あり配列 ---<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim strArr As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;strArr = Array("Apple", "Banana", "Apple", "Orange", "Banana", "Grape")<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "=== [テスト1] 文字列の重複削除 ==="<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "-- 削除前（元配列） --"<br />
&nbsp;&nbsp;&nbsp;&nbsp;For i = LBound(strArr) To UBound(strArr)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print " [" &amp; i &amp; "] " &amp; strArr(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;Next i<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim resStr As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;resStr = RemoveDuplicates(strArr)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "-- 削除後（結果配列） --"<br />
&nbsp;&nbsp;&nbsp;&nbsp;For i = LBound(resStr) To UBound(resStr)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print " [" &amp; i &amp; "] " &amp; resStr(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;Next i<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print ""<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' --- テスト2: 数値の重複あり配列 ---<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim numArr As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;numArr = Array(10, 20, 10, 30, 20, 40, 10)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "=== [テスト2] 数値の重複削除 ==="<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "-- 削除前（元配列） --"<br />
&nbsp;&nbsp;&nbsp;&nbsp;For i = LBound(numArr) To UBound(numArr)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print " [" &amp; i &amp; "] " &amp; numArr(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;Next i<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim resNum As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;resNum = RemoveDuplicates(numArr)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "-- 削除後（結果配列） --"<br />
&nbsp;&nbsp;&nbsp;&nbsp;For i = LBound(resNum) To UBound(resNum)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print " [" &amp; i &amp; "] " &amp; resNum(i)<br />
&nbsp;&nbsp;&nbsp;&nbsp;Next i<br />
End Sub</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. イミディエイトウィンドウの実行結果</h3>
<p>上記の <code>Test_RemoveDuplicates</code> を実行すると、イミディエイトウィンドウに次のように出力されます。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">=== [テスト1] 文字列の重複削除 ===<br />
-- 削除前（元配列） --<br />
&nbsp; [0] Apple<br />
&nbsp; [1] Banana<br />
&nbsp; [2] Apple<br />
&nbsp; [3] Orange<br />
&nbsp; [4] Banana<br />
&nbsp; [5] Grape<br />
-- 削除後（結果配列） --<br />
&nbsp; [0] Apple<br />
&nbsp; [1] Banana<br />
&nbsp; [2] Orange<br />
&nbsp; [3] Grape<br />
<br />
=== [テスト2] 数値の重複塩削除 ===<br />
-- 削除前（元配列） --<br />
&nbsp; [0] 10<br />
&nbsp; [1] 20<br />
&nbsp; [2] 10<br />
&nbsp; [3] 30<br />
&nbsp; [4] 20<br />
&nbsp; [5] 40<br />
&nbsp; [6] 10<br />
-- 削除後（結果配列） --<br />
&nbsp; [0] 10<br />
&nbsp; [1] 20<br />
&nbsp; [2] 30<br />
&nbsp; [3] 40</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. このコードのポイント</h3>
<ul>
<li><b>Dictionaryの活用</b>：<code>Scripting.Dictionary</code> の「キー（Key）の重複を許さない」という特性を利用することで、ループで一つずつチェックしながら高速に一意な値を抽出しています。</li>
<li><b><code>dict.Keys</code> の利用</b>：Dictionaryに登録されたキーの一覧は、そのまま <code>dict.Keys</code> プロパティで0ベースの1次元配列として取得できるため、非常にスマートに記述できます。</li>
<li><b>幅広いデータ型に対応</b>：<code>Variant</code> 型で受け取っているため、文字列だけでなく数値配列の重複削除にもそのまま流用可能です。</li>
</ul>
<p>配列のデータを整理・集計したいときは、ぜひこの関数を標準モジュールに組み込んで活用してみてください！<br />
<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBAサンプル</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/41/</link>
    <pubDate>Sat, 03 Oct 2026 21:22:30 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/41</guid>
  </item>
    <item>
    <title>【Excel VBA＆AI】ローカルLLM（LM Studio）をMacに導入！VBAコード自動チェック（RAG）への第一歩</title>
    <description>
    <![CDATA[<p>Excel VBAで開発をしていると、「コーディングのルール（例えば数値変数はLongやDoubleを使う、エラーハンドリングを必ず入れるなど）が、複数人で開発しているうちにバラバラになってしまう&hellip;&hellip;」という悩みに直面したことはないでしょうか。</p>
<p>将来的に、こうした<strong>VBAのコーディングルール違反を自動でチェックしてくれる「ローカルRAG（検索拡張生成）システム」を作りたい！</strong>と考えた筆者は、社外にコードを漏らさず完全オフラインで動かせるローカルLLM環境を構築することにしました。</p>
<p>この記事では、その第一歩として、Mac（Apple Silicon）環境にローカルLLM実行アプリ「LM Studio」を導入し、最適なモデルをセットアップする手順を実際の体験ベースで解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. なぜローカルLLM（LM Studio）を選ぶのか？</h3>
<p>業務で扱うVBAコードや社内独自のコーディングルールは、セキュリティの観点から外部のクラウドAIにそのまま送信するわけにはいきません。そこで注目したのが、手元のPC内で安全にAIを動かせるツール「LM Studio」です。</p>
<ul>
<li><b>完全オフライン・安全</b>：外部サーバーへデータを送信しないため、機密性の高いコードやルールも安心して扱えます。</li>
<li><b>Apple Siliconへの最適化</b>：MacのユニファイドメモリやGPUを活用し、Mシリーズ Mac上で非常に高速な推論が可能です。</li>
<li><b>APIサーバー化が容易</b>：ワンクリックで「OpenAI互換のAPIエンドポイント」を起動できるため、将来的にPythonと連携したRAGシステムに組み込みやすいのが最大のメリットです。</li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. Macへのインストール手順</h3>
<p>まずはLM StudioをMacにインストールします。対応OS（macOS 11.0以降のApple Silicon搭載機）を確認の上、以下のステップを進めます。</p>
<ul>
<li><b>手順1: ダウンロード</b><br />
公式サイト（<code>lmstudio.ai</code>）にアクセスし、「Download Mac (Apple Silicon / M-series)」をクリックして <code>.dmg</code> インストーラーを入手します。</li>
<li><b>手順2: アプリケーションの配置</b><br />
ダウンロードしたファイルを開き、<code>LM Studio</code> アイコンを <code>Applications（アプリケーション）</code> フォルダへドラッグ＆ドロップして起動します。（※セキュリティ警告が出た場合は右クリックして「開く」を選択）</li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. 最適なモデルの選定とセットアップ</h3>
<p>プログラムのコード解釈や日本語の指示出しに非常に強いオープンソースモデル**「Qwen2.5-7B-Instruct-1M」**をダウンロードしてセットアップします。</p>
<ul>
<li><b>モデルの検索とダウンロード</b><br />
アプリ起動後、左側メニュー等の検索画面から <code>Qwen2.5-7B-Instruct-1M</code> を検索し、ファイル一覧の中から <code>qwen2.5-7b-instruct-1m-q4_0</code>（または <code>Q4_K_M</code> などの量子化バージョン）を選んで「Download」を押します。</li>
<li><b>モデルのロードと動作テスト</b><br />
ダウンロード完了後、モデルを選択して読み込ませ、チャット画面で動作テストを行います。</li>
</ul>
<p>実際にチャットで質問テストを行った際のやり取りがこちらです。</p>
<p><b>【実際のチャットテスト例】</b></p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 15px;">[入力]<br />
&nbsp;&nbsp;&nbsp;&nbsp;あなたのモデルの名前はなんですか<br />
<br />
[出力]<br />
&nbsp;&nbsp;&nbsp;&nbsp;私はLM Studio Bionicのエージェントです。私のモデルの名称としては「Bionic」または「LM Studio Bionic 内のエージェント」と呼ばれます。</div>
<p>ここで<b>「あれ、Qwenって答えないな？」</b>と少し驚くかもしれませんが、これはチャット画面上でアプリ側が裏で「あなたはLM Studio内で動くアシスタントです&hellip;」というシステムプロンプトを自動付加している仕様のためです。今後PythonからAPI経由で直接叩く際は、この挙動は消えてQwen本来の賢い応答になります。</p>
<p>また、ロード直後や最初の1回目は、メモリ展開やウォームアップ（最初の1文字が出るまでの待機時間）の計算が入るため、数秒〜十数秒ほど時間がかかる点も覚えておくと慌てずに済みます（2回目以降はスムーズに高速出力されます）。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. まとめ</h3>
<ul>
<li>VBAのコーディングルールチェック用RAGを見据え、安全かつ強力なローカルLLM環境（LM Studio ＋ Qwen2.5-7B）をMac上に構築した。</li>
<li>アプリの「Discover」から <code>Qwen2.5-7B-Instruct-1M</code>（<code>qwen2.5-7b-instruct-1m-q4_0</code>）をダウンロードしてロードするだけで、手元にAI環境が整う。</li>
<li>チャット画面特有のシステムプロンプトや初回ロード時の挙動にさえ慣れてしまえば、次はPythonと連携させたAPI自動化へスムーズに進むことができる。</li>
</ul>
<p>次回は、このローカルLLMサーバーに対してPythonからリクエストを送り、VBAのルールチェックを自動化する実験（RAGの土台作り）に挑戦します。お楽しみに！<br />
<br />
<br />
</p>]]>
    </description>
    <category>その他</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/40/</link>
    <pubDate>Sun, 27 Sep 2026 10:06:59 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/40</guid>
  </item>
    <item>
    <title>【Excel VBA】シートモジュール（Worksheet）は「View」に徹するべき！イベント処理の設計と書き方の注意点</title>
    <description>
    <![CDATA[<p>Excel VBAで開発をしていると、シートごとに用意されている「Worksheetモジュール」にコードを書く機会があります。例えば、次のようなイベントプロシージャを見たことがあるかもしれません。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Private Sub Worksheet_Activate()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "Sheet1 Active!!!"<br />
End Sub</div>
<p>このコード自体は「シートがアクティブになったときにイミディエイトウィンドウに文字を出力する」という非常にシンプルなものですが、<b>ここに何でもかんでも書き込んでしまうのは、保守性の観点から大きなNG</b>です。</p>
<p>この記事では、Worksheetモジュールとはそもそも何者なのか、そして「なぜビジネスロジックをここに書いてはいけないのか（Viewとしての役割分担）」を解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. Worksheetモジュールとは？（標準モジュールとの違い）</h3>
<p>VBAのプロジェクトエクスプローラを開くと、通常の「標準モジュール（Module1など）」のほかに、各シート（<code>Sheet1 (Sheet1)</code> など）に対応した「シートモジュール（Worksheetモジュール）」が存在します。</p>
<ul>
<li><b>標準モジュール</b>：どこからでも呼び出せる汎用的なマクロや関数（プロシージャ）を置く場所。</li>
<li><b>Worksheetモジュール</b>：そのシート固有のイベント（セルが変更された、選択範囲が変わった、シートがアクティブになったなど）をフックして、自動実行させるコードを置く特別な場所。</li>
</ul>
<p>冒頭の <code>Worksheet_Activate</code> も、シートが選択された瞬間に自動で発火する<b>イベントプロシージャ</b>の一つです。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. 「ここに書きすぎてはダメ！」な理由とビジネスロジックの分離</h3>
<p>イベントを簡単に拾えるからといって、Worksheetモジュールの中にシートの判定処理、データの集計、データベースや外部ファイルとの連携といった<b>複雑なビジネスロジック（処理の本質）を直接書き込んでしまうのはNG</b>です。</p>
<p>理由は主に以下の通りです。</p>
<ul>
<li><b>コードが迷子になる</b>：「あの処理はどこに書いてあったっけ？」となったとき、標準モジュールを探せばいいのか、特定のシートモジュールを探せばいいのか分からなくなります。</li>
<li><b>再利用性が低い</b>：シートモジュール内のコードは、基本的に「そのシート」に紐づいているため、他のシートや別のブックから流用することが極めて困難になります。</li>
<li><b>デバッグや保守がしんどくなる</b>：イベントドリブンで勝手に発火するため、意図しないタイミングでロジックが走り、不具合の原因追跡（デバッグ）が難しくなります。</li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. 「ここはView（表示・受付）だけよ」という設計思想</h3>
<p>ソフトウェア設計の世界（MVCモデルなど）に倣うなら、Excel VBAのシートモジュールは<b>「View（UI・画面の窓口）」</b>として扱うのがベストプラクティスです。</p>
<p>シートモジュールの役割は、あくまで**「ユーザーの操作（イベント）を受け付け、標準モジュール側にあるメインの処理へバトンを渡すこと」**だけに留めましょう。</p>
<p><b>【正しいコードの書き方の例】</b></p>
<p><b>① シートモジュール側（Viewとしての受付のみ）</b></p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 15px;">Private Sub Worksheet_Activate()<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 受付だけ行い、実際の処理は標準モジュールのプロシージャを呼び出す<br />
&nbsp;&nbsp;&nbsp;&nbsp;Call MainLogic.InitializeSheetView(Me)<br />
End Sub</div>
<p><b>② 標準モジュール側（ビジネスロジックを記述）</b></p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Public Sub InitializeSheetView(targetSheet As Worksheet)<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ここにデータの取得や集計などのビジネスロジックをガッツリ書く<br />
&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print targetSheet.Name &amp; " の初期化処理を実行中..."<br />
End Sub</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. まとめ</h3>
<ul>
<li>Worksheetモジュールは、シート独自のイベント（<code>Worksheet_Activate</code> など）を受け取るための<b>「View（窓口）」</b>として割り切って使う。</li>
<li>実際の計算やデータ処理などの<b>ビジネスロジックは標準モジュールに分離（カプセル化）する</b>。</li>
<li>シートモジュールから標準モジュールを呼ぶ際は、<code>Me</code> キーワードなどを渡すことで柔軟に連携できる。</li>
</ul>
<p>「シートのイベントだから全部ここに書いちゃえ」とコードを詰め込むと、後でメンテナンスに苦労することになります。役割をすっきり分けて、きれいなVBAコードを保ちましょう！<br />
<br />
</p>]]>
    </description>
    <category>VBA知識</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/39/</link>
    <pubDate>Sun, 27 Sep 2026 04:32:12 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/39</guid>
  </item>
    <item>
    <title>【VBA】「1.1 - 1.0 = 0.1」がFalseに！？浮動小数点数の罠とCurrency型の正しい使い方</title>
    <description>
    <![CDATA[<p>VBAで小数の計算を行ったとき、<b>「<code>1.1 - 1.0 = 0.1</code> なのに <code>False</code> になる」</b>という不思議な現象に出会ったことはありませんか？一見すると絶対に一致するはずのシンプルな引き算ですが、数値の末尾に <code>@</code> をつけると <code>True</code> になります。</p>
<p>この記事では、イミディエイトウィンドウの実行結果をもとに、VBAにおける「浮動小数点数の誤差のなぞ」と、それを解決する「通貨型（Currency）」の秘密、そして実務における使い分けの基準を分かりやすく解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. イミディエイトウィンドウの実験結果</h3>
<p>まずは、VBAのイミディエイトウィンドウで次のような引き算の評価式を実行した結果を見てみましょう。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">? 1.1 - 1.0 = 0.1<br />
False<br />
<br />
? 1.1@ - 1.0@ = 0.1@<br />
True</div>
<p>なぜ <code>1.1 - 1.0</code> の結果が <code>0.1</code> と一致しないのでしょうか？その理由は、コンピュータが小数を扱うときに使用する<b>「浮動小数点数（Double型など）」の仕組み</b>にあります。</p>
<p>コンピュータは内部で数値をすべて「2進数」で表現しています。「1.1」や「1.0」といった小数は、2進数に変換すると無限小数になってしまい、正確に表現できません。その結果、ごくわずかな丸め誤差（端数）が発生し、<code>1.1 - 1.0</code> の計算結果は厳密には <code>0.09999999999999988</code> のような値になってしまいます。そのため、<code>0.1</code> と比較したときに一致せず <code>False</code> になってしまいます。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. 通貨型（Currency）の「@」が持つ秘密</h3>
<p>では、2つ目の式にある <code>1.1@ - 1.0@ = 0.1@</code> が <code>True</code> になるのはなぜでしょうか？</p>
<p>ここで使われている <code>@</code> は、VBAで<b>「通貨型（Currency型）」</b>の数値リテラルであることを明示する型文字です。通貨型には次のような強力な特徴があります。</p>
<ul>
<li><b>固定小数点数での演算</b>：浮動小数点数のような「2進数による丸め誤差」が発生しません。</li>
<li><b>小数点以下4桁までの精度</b>：内部的には整数として扱われ（10,000倍した整数で計算）、小数点以下4桁までの正確な計算を保証します。</li>
<li><b>金額や端数計算に最適</b>：消費税の計算や、わずかなズレも許されない厳密な引き算・足し算のために用意されています。</li>
</ul>
<p>数値の末尾に <code>@</code> をつけることで、VBAはこれをDouble型ではなくCurrency型として正確に計算するため、誤差が生じずピッタリ一致して <code>True</code> になります。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. VBAコードでの実装例</h3>
<p>実際のVBAコードで、通貨型と通常の浮動小数点数（Double型）の挙動の違いを確認してみましょう。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Sub TestCurrencySubtraction()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim a As Currency, b As Currency, c As Currency<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim d As Double, e As Double, f As Double<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 通貨型（Currency型）を使った計算（正確）<br />
&nbsp;&nbsp;&nbsp;&nbsp;a = 1.1@<br />
&nbsp;&nbsp;&nbsp;&nbsp;b = 1.0@<br />
&nbsp;&nbsp;&nbsp;&nbsp;c = 0.1@<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;If c = (a - b) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "Currency型: 一致しました（True）"<br />
&nbsp;&nbsp;&nbsp;&nbsp;Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "Currency型: 一致しません（False）"<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 通常の浮動小数点数（Double型）を使った計算（誤差が出る）<br />
&nbsp;&nbsp;&nbsp;&nbsp;d = 1.1<br />
&nbsp;&nbsp;&nbsp;&nbsp;e = 1.0<br />
&nbsp;&nbsp;&nbsp;&nbsp;f = 0.1<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;If f = (d - e) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "Double型: 一致しました（True）"<br />
&nbsp;&nbsp;&nbsp;&nbsp;Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Debug.Print "Double型: 誤差により一致しません（False）"<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
End Sub</div>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. 通貨型（Currency）と 倍精度浮動小数点型（Double）の使い分け</h3>
<p>万能に見える通貨型ですが、どのような場面でもCurrencyを使えばいいというわけではありません。以下の基準を目安に使い分けましょう。</p>
<p><b>【通貨型（Currency）を使うべきケース】</b></p>
<ul>
<li><b>お金や金額・税金の計算をするとき</b>（1円たりともズレが許されない業務システムなど）</li>
<li><b>小数点以下の桁数が固定されており、厳密な一致判定（<code>=</code>）を行いたいとき</b></li>
<li>※扱える範囲は「整数部が最大15桁、小数部が4桁まで」となります。</li>
</ul>
<p><b>【倍精度浮動小数点型（Double）を使うべきケース】</b></p>
<ul>
<li><b>科学技術計算や統計・物理演算など、非常に大きな数値や小さな数値を扱うとき</b>（例: <code>1.23e-10</code> のような指数表現が必要な場合）</li>
<li><b>小数点以下が5桁以上ある細かい数値を扱うとき</b>（Currency型では小数点以下5桁目以降が四捨五入されて切り捨てられてしまうため）</li>
<li><b>マクロ内で単純な四則演算や平均値・割合などを出すだけで、厳密なイコール判定をしないとき</b></li>
</ul>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">5. まとめ</h3>
<ul>
<li>通常、VBAの数値リテラルは何も指定しないとDouble型になり、小数の計算などでわずかな<b>丸め誤差</b>が発生する。</li>
<li>金額計算や、厳密な小数の比較が必要な場面では、末尾に <code>@</code> をつけるか、変数に <b>Currency型</b> を明示する。</li>
<li>小数第5位より細かい値や科学的な計算を扱う場合は、従来通り <b>Double型</b> を選ぶ。</li>
</ul>
<p>「なぜか小数の計算結果が微妙にズレてしまう&hellip;」とお悩みのときは、データの性質に合わせて通貨型（Currency）や型文字 <code>@</code> の活用を検討してみてください！<br />
<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBA知識</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/38/</link>
    <pubDate>Sun, 27 Sep 2026 04:15:07 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/38</guid>
  </item>
    <item>
    <title>【VBA】配列から最大値を求める方法：WorksheetFunctionとループ処理の使い分け・エラー対策</title>
    <description>
    <![CDATA[<p>VBAには、配列から最大値を直接求める組み込み関数（標準関数）は存在しません。しかし、Excel VBAであれば、ワークシート関数の <code>Max</code> を呼び出すことで簡単に最大値を取得できます。</p>
<p>この記事では、<code>WorksheetFunction.Max</code> を使う際のデータ型に関する注意点（仕様）と、予期せぬエラーを防ぐためのエラー処理、さらに安全に最大値を求めるループ処理の実装コードを解説します。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">1. WorksheetFunction.Max を使う際の注意点（仕様の解説）</h3>
<p><code>WorksheetFunction.Max</code> は非常に便利ですが、「何を引数に渡すか」によって、数値以外のデータ（文字列や論理値など）が含まれていた場合の挙動が変わります。特に「セル範囲」を渡す場合と、「VBAの配列」を直接渡す場合で違いがあるため注意が必要です。</p>
<p><b>【比較対象となるデータ型の挙動まとめ】</b></p>
<ul>
<li><b>数値</b>：セル範囲＝計算される ／ VBA配列＝計算される</li>
<li><b>日付 (Date)</b>：セル範囲＝計算される（シリアル値） ／ VBA配列＝計算される（シリアル値）</li>
<li><b>論理値 (Boolean)</b>：セル範囲＝無視される ／ VBA配列＝計算される（True=1, False=0）</li>
<li><b>数値化できる文字列 (例: "100")</b>：セル範囲＝無視される ／ VBA配列＝計算される（自動で数値化）</li>
<li><b>数値化できない文字列 (例: "abc")</b>：セル範囲＝無視される ／ VBA配列＝<b>エラーになる（実行時エラー1004等）</b></li>
</ul>
<p>このように、VBAの配列を直接 <code>WorksheetFunction.Max</code> に渡すと、数値に変換できない文字列が混ざっていた瞬間にエラーで処理が止まってしまいます。そのため、実務で使用する際はエラー処理（エラートラップ）が必須となります。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">2. 実践コード①：WorksheetFunctionを使ったコード（エラー処理付き）</h3>
<p>配列内に計算不ような文字列（"abc"など）が混ざっていた場合に備え、<code>On Error GoTo</code> ステートメントを使ってエラーを検知するコードです。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Sub GetMaxWithWorksheetFunction()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim numbers As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim maxVal As Double<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' テスト用配列 (わざとエラーになる文字列 "abc" を混ぜています)<br />
&nbsp;&nbsp;&nbsp;&nbsp;numbers = Array(10, 45, 23, "100", "abc", 5)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' --- エラー監視を開始 ---<br />
&nbsp;&nbsp;&nbsp;&nbsp;On Error GoTo ErrorHandler<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' WorksheetFunction.Maxで最大値を取得<br />
&nbsp;&nbsp;&nbsp;&nbsp;' ("100"は数値化されますが、"abc"があるためここでエラーが発生します)<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxVal = WorksheetFunction.Max(numbers)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 正常に取得できた場合<br />
&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "最大値は: " &amp; maxVal<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' エラー処理ブロックに行かないように終了<br />
&nbsp;&nbsp;&nbsp;&nbsp;Exit Sub<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
ErrorHandler:<br />
&nbsp;&nbsp;&nbsp;&nbsp;' エラーが発生した場合の処理<br />
&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "最大値の取得に失敗しました。" &amp; vbCrLf &amp; _<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"配列に数値として評価できないデータが含まれています。", _<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vbCritical, "エラー"<br />
End Sub</div>
<p><b>【ポイント】</b><br />
コードをシンプルに保ちたい場合におすすめです。ただし、エラーになった場合は「最大値が分からない」状態で処理が終わってしまうという弱点があります。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">3. 実践コード②：ループ処理を使ったコード（安全・汎用型）</h3>
<p>配列内に文字列が混ざっていてもエラーで止めず、「数値として扱えるものだけを拾って最大値を見つける」という柔軟な処理をしたい場合は、ループ処理（<code>For ～ Next</code>）と <code>IsNumeric</code> 関数を組み合わせるのがベストです。Excel以外の環境（AccessやWordのVBA）でも動作します。</p>
<div style="font-family: monospace; border: 1px solid #bbdefb; border-left: 4px solid #9ccc65; padding: 15px; line-height: 1.6; background-color: #f1f8e9; margin-bottom: 25px;">Sub GetMaxWithLoopSafe()<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim numbers As Variant<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim maxVal As Double<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim i As Long<br />
&nbsp;&nbsp;&nbsp;&nbsp;Dim isFirstNumeric As Boolean<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' テスト用配列 (文字列や空白が混ざっているケース)<br />
&nbsp;&nbsp;&nbsp;&nbsp;numbers = Array("テスト", 10, 45, "abc", 89, Empty, 5)<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 配列が初期化されているか（空でないか）の簡易チェック<br />
&nbsp;&nbsp;&nbsp;&nbsp;If IsEmpty(numbers) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "配列が空です。", vbExclamation<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Exit Sub<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;isFirstNumeric = False<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 配列の最初から最後までループ<br />
&nbsp;&nbsp;&nbsp;&nbsp;For i = LBound(numbers) To UBound(numbers)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' 要素が数値として評価できる場合のみ処理 (IsNumeric)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;If IsNumeric(numbers(i)) And Not IsEmpty(numbers(i)) Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;If Not isFirstNumeric Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' 最初の有効な数値を見つけた時、それを暫定の最大値とする<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxVal = CDbl(numbers(i))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isFirstNumeric = True<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' 2つ目以降の数値と比較し、大きければ上書きする<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;If CDbl(numbers(i)) &gt; maxVal Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxVal = CDbl(numbers(i))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;Next i<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;' 結果の判定<br />
&nbsp;&nbsp;&nbsp;&nbsp;If isFirstNumeric Then<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "最大値は: " &amp; maxVal ' 結果: 89 ("テスト"や"abc"は無視される)<br />
&nbsp;&nbsp;&nbsp;&nbsp;Else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MsgBox "配列内に有効な数値が見つかりませんでした。", vbExclamation<br />
&nbsp;&nbsp;&nbsp;&nbsp;End If<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
End Sub</div>
<p><b>【ポイント】</b><br />
<code>IsNumeric</code> 関数を使うことで、実行時エラーを未然に防ぎながら安全に比較を行います。業務システムなど、予期せぬデータが混入する可能性がある配列を処理する場合に非常に強力です。</p>
<h3 style="color: #1976d2; border-bottom: 2px solid #90caf9; padding-bottom: 4px;">4. まとめ</h3>
<ul>
<li><b>手軽に書きたい・配列に数値しか入らないと確信できる場合</b><br />
&rArr; <code>WorksheetFunction.Max</code> ＋ 念のためのエラー処理（<code>On Error</code>）</li>
<li><b>文字列などが混ざる可能性があり、安全に数値だけを比較したい場合</b><br />
&rArr; ループ処理 ＋ <code>IsNumeric</code> 関数での判定</li>
</ul>
<p>用途とデータの性質に合わせて使い分けてみてください！<br />
<br />
<br />
</p>]]>
    </description>
    <category>VBAサンプル</category>
    <link>http://vbafrom20260822.blog.shinobi.jp/Entry/37/</link>
    <pubDate>Sat, 26 Sep 2026 10:31:21 GMT</pubDate>
    <guid isPermaLink="false">vbafrom20260822.blog.shinobi.jp://entry/37</guid>
  </item>

    </channel>
</rss>