【AI × Excel VBA】複数の画像ファイルを一括リサイズ 【改訂版 / v1.1.0】

アフィリエイト広告を利用しています。
Information

皆さん、こんにちは。ご覧いただきありがとうございます。

“日々の業務にちょうどいい自動化を”──

業務改善アプリケーションの作成を手がける「ソフトデザイン工房」 です。

皆さんは、画像ファイルのサイズを小さくしたいときはないでしょうか。

たとえば、Word や PowerPoint に挿入する写真の容量が大きすぎて、ファイルが重くなったり、動作が遅くなったりする場面です。

複数の画像ファイルをまとめて処理したいとき、専用アプリを使う方法もありますが、実務では次のような不便さを感じることがあります。

  • インストールが必要 ※プリインストールされているものを除く
    追加のアプリを導入しなければならず、利用するPCによってはすぐに使えないことがあります。
  • 操作に慣れが必要
    画像編集ソフトは機能が多い反面、画面や手順が複雑で、毎回同じ操作を行うだけでも手間になりがちです。
  • 一括処理や細かな調整がしにくい
    ソフトによっては複数ファイルの一括リサイズに弱く、ファイル名の付け方や保存ルールを思うように変えられない場合があります。
  • コストがかかることがある
    有料ソフトの導入が必要になることもあり、ちょっとした業務改善のためには負担が大きいことがあります。
ポテ

プリインストール
PCが出荷される前に、あらかじめアプリがインストールされていることです。

その点、Excel VBA で画像リサイズ用のアプリケーションを作成すると、次のようなメリットがあります。

  • Excel があれば追加インストールなしで使える
    普段の業務で使っている Excel をそのまま活用できるため、新しい専用ソフトを用意する必要がありません。
  • 必要な操作だけに絞った画面や処理にできる
    自分で作るマクロなので、実務に必要な操作だけを残し、迷いにくい形に整えることができます。
  • 業務に合わせて自由にカスタマイズできる
    複数画像の一括処理、リサイズ後の命名ルール、対象拡張子の追加、保存先の変更なども柔軟に調整できます。
  • 継続的に改善しやすい
    いったん作って終わりではなく、使いながら少しずつ改良して、自分や職場に合った形へ育てていけます。

今回の記事では、以前公開した「Excel VBAで複数の画像ファイルを一括リサイズする方法」をもとに、改訂版として内容を整理し直しました。

従来の一括リサイズ機能はそのままに、コードの役割分担を見直し、処理の流れや依存関係を追いやすい構成へ改善しています。

単に動くコードとしてではなく、再利用しやすく、あとから保守しやすい VBA アプリケーションとしています。

この記事が、あなたのVBAアプリケーションの価値をさらに高める一助となれば幸いです。

VBAを活用して、自分自身や身近なコミュニティに合ったアプリケーションを作成し、仕事量は半分に、成果は2倍に──そんな未来を目指すあなたを応援しています。

Contents
  1. 概要
  2. 全体像
  3. 構成
  4. ライブラリの参照設定
  5. UI設計 ── シート設計
  6. モジュールごとの役割
  7. 処理の流れ
  8. システムフロー図
  9. システム全体の依存関係
  10. Pres_ImageResizeLauncherモジュールの解説
  11. App_ImageResizeRunnerモジュールの解説
  12. Dom_ResizeDimensionCalculatorモジュールの解説
  13. Inf_ImageFileAccessorモジュールの解説
  14. Util_ImagePathHelperモジュールの解説
  15. 来歴
  16. VBAスキルアップの参考情報
  17. 【初心者歓迎】無料相談受付中 
  18. おわりに

概要

このアプリケーションは、指定フォルダ内の画像ファイルを一括でリサイズする Excel VBA アプリケーションです。

ボタンを押すと、入力フォルダ内の PNG・JPG・JPEG 形式の画像を自動で読み取り、シートに設定した最大幅・最大高さの範囲に収まるようにリサイズして、出力フォルダへ保存します。

縦横比は維持されるため、画像が歪む心配はありません。

同名ファイルが出力先に既に存在する場合は上書きせずにスキップするため、誤った上書きを防ぐ設計になっています。

処理の結果はメッセージボックスで通知されます。対象件数・保存件数・スキップ件数がひとめでわかるため、実行後の確認が容易です。

全体像

このアプリケーションは 5 つの .bas モジュールで構成されており、それぞれが明確な責務を持ちます。

処理の起点はボタン操作で、Pres_ImageResizeLauncher がその入口を担います。

リサイズのユースケース全体は App_ImageResizeRunner が制御し、サイズ計算のロジックは Dom_ResizeDimensionCalculator、ファイルパスの組み立ては Util_ImagePathHelper、ファイルや設定の読み書きは Inf_ImageFileAccessor がそれぞれ担当します。

各モジュールは役割ごとに分離されており、「どこに何が書いてあるか」が判断しやすい構成になっています。

構成

このアプリケーションを構成するモジュールは、次のとおりです。

モジュール名役割
Pres_ImageResizeLauncherPresentationボタン操作の受け口 / 処理の起動と結果表示を担う
App_ImageResizeRunnerApplicationリサイズ処理のユースケース全体を制御する
Dom_ResizeDimensionCalculatorDomain縦横比を維持したリサイズ後サイズを計算する
Util_ImagePathHelperUtilityファイル名・拡張子・出力パスの組み立てを担う
Inf_ImageFileAccessorInfrastructure設定読み取り・画像の読み書き・ファイル存在確認を担う

ライブラリの参照設定

ポテ

ここは最初に済ませておきましょう。

このアプリケーションでは、実行前に使用するライブラリをあらかじめ参照設定しておく必要があります。

今回のコードでは、主にファイルやフォルダを扱うための Microsoft Scripting Runtime と、画像の読み込みやリサイズ処理に使用する Microsoft Windows Image Acquisition Library v2.0 を利用しています。

これらの参照設定が不足していると、コンパイルエラーや実行時エラーの原因になります。

参照設定は、VBE のメニューから ツール → 参照設定 を開き、対象のライブラリにチェックを入れて行います。

UI設計 ── シート設計

このアプリケーションのシート構成は、以下のとおりです。

実際の操作は、操作画面 シートに記載された手順に従って行います。

操作画面のシートには、今回作成するマクロを登録します。右クリックメニューの、「マクロの登録」から登録できます。

ポテ

UIをきれいに作成しておくことが、VBAアプリケーションを長持ちさせるコツです。

モジュールごとの役割

Pres_ImageResizeLauncher

ボタン操作から処理を起動するモジュールです。

RunImageResize が公開プロシージャーとして外部からの呼び出しを受け取り、リサイズ処理本体を App_ImageResizeRunner へ委譲します。

処理後は実行結果に応じてメッセージボックスを表示し、ユーザーへ結果を伝えます。業務ロジックや設定は持ちません。

App_ImageResizeRunner

リサイズ処理のユースケース全体を制御するモジュールです。

ExecuteImageResize が処理フロー全体を担い、設定取得・対象ファイルの列挙・画像ごとのリサイズという3段階を順に実行します。

ProcessSingleImageFile は1件の画像に対するサイズ取得・判定・計算・保存の一連の流れを処理します。

実際の計算やファイル操作は下位モジュールへ委譲しており、このモジュールは「何をどの順に呼ぶか」の制御に専念しています。

また、ResizeSettings(リサイズ設定を保持する構造体)と ResizeExecutionResult(実行結果を保持する構造体)の2つの Type 宣言もこのモジュールに置かれています。

Dom_ResizeDimensionCalculator

縦横比を維持したリサイズ後サイズを計算するモジュールです。

CalculateResizedDimensions が最終的なリサイズ後サイズを返します。

IsUpscalingRequired は、指定サイズへ収める際に拡大が必要かどうかを判定します。

どちらも内部で GetResizeRatio(比率算出)を呼び出しています。

Excelシートの設定値や画像ファイルには直接触れず、サイズ計算の純粋なロジックのみを持ちます。

Util_ImagePathHelper

ファイル名とパスの操作を担うユーティリティモジュールです。

IsTargetImageFileName は拡張子を見てリサイズ対象かどうかを判定します。

BuildResizedFilePath は入力パスと出力フォルダパスから保存先パスを組み立てます。

ファイルの内容や存在確認には関与せず、文字列としてのパス操作に特化しています。

Inf_ImageFileAccessor

外部リソースとのやりとりを担うインフラモジュールです。

Excelシートから最大幅・最大高さを読み取り(TryBuildResizeSettings)、フォルダ内の画像ファイルを列挙し(FindTargetImageFilePaths)、WIA(Windows Image Acquisition Library)を使って画像を読み込み・リサイズ・保存します(TryLoadImageSizeSaveResizedImageFile)。

ファイルやフォルダの存在確認(ImageFileExistsFolderExists)もここに集約されています。

処理の流れ

処理は次の順番で進みます。

  1. ユーザーがボタンを押す
  2. Pres_ImageResizeLauncherRunImageResize が呼ばれる
  3. App_ImageResizeRunnerExecuteImageResize が設定を読み取り、入力フォルダ内の画像ファイルを列挙する
  4. 画像1件ごとに ProcessSingleImageFile が呼ばれ、サイズ取得・拡大判定・リサイズ計算・出力先決定・保存を実行する
  5. すべての処理が終わったあと、Pres_ImageResizeLauncherShowResizeExecutionResult がメッセージボックスで結果を表示する

エラーが発生した場合は、その時点でループを抜けてエラーメッセージを表示します。

対象ファイルが 0 件の場合は、早期リターンして「ファイルが見つからなかった」旨を通知します。

システムフロー図

このアプリケーションのシステムフロー図は、次のとおりです。

システムフロー図

システム全体の依存関係

依存関係ツリー(Dependency Tree)

このアプリケーション全体の呼び出し構造は、次のとおりです。

[Pres_ImageResizeLauncher]
└─ RunImageResize : 実行入口
   ├─ [App_ImageResizeRunner]
   │  └─ ExecuteImageResize : 全体制御
   │     ├─ [Inf_ImageFileAccessor]
   │     │  ├─ TryBuildResizeSettings : 設定取得
   │     │  │  ├─ TryReadPositiveLongFromCell : セル読取
   │     │  │  └─ FolderExists : フォルダ確認
   │     │  └─ FindTargetImageFilePaths : 対象列挙
   │     │
   │     ├─ [Util_ImagePathHelper]
   │     │  ├─ IsTargetImageFileName : 拡張子判定
   │     │  └─ GetLowerCaseExtension : 拡張子整形
   │     │
   │     └─ ProcessSingleImageFile : 単一画像処理
   │        ├─ [Inf_ImageFileAccessor]
   │        │  ├─ TryLoadImageSize : サイズ取得
   │        │  ├─ ImageFileExists : 既存確認
   │        │  └─ SaveResizedImageFile : 保存実行
   │        │
   │        ├─ [Dom_ResizeDimensionCalculator]
   │        │  ├─ IsUpscalingRequired : 拡大判定
   │        │  │  └─ GetResizeRatio : 比率算出
   │        │  └─ CalculateResizedDimensions : サイズ計算
   │        │     └─ GetResizeRatio : 比率算出
   │        │
   │        └─ [Util_ImagePathHelper]
   │           └─ BuildResizedFilePath : 保存先生成
   │
   └─ ShowResizeExecutionResult : 結果表示
      └─ BuildResizeCompletionMessage : 文言作成

ツリーの起点は Pres_ImageResizeLauncherRunImageResize です。

ここから App_ImageResizeRunner へ処理が委譲され、その下に設定・ファイル操作・サイズ計算・パス組み立ての各モジュールが呼ばれる構造になっています。

実務で見るときのポイント

  • 処理フローを追いたいときは、App_ImageResizeRunnerExecuteImageResizeProcessSingleImageFile を中心に読むと全体の流れが把握しやすくなります。
  • サイズ計算のロジックを確認したいときは、Dom_ResizeDimensionCalculator を見てください。縦横比の維持と拡大抑止の判定がここにあります。
  • 設定値の読み取り先や保存フォルダの変更は、Inf_ImageFileAccessorTryBuildResizeSettings 内の定数を確認してください。
  • 対象拡張子の追加・変更は、Util_ImagePathHelperIsTargetImageFileName に手を入れます。

Pres_ImageResizeLauncherモジュールの解説

全体像

Pres_ImageResizeLauncher は、ボタン操作から画像リサイズ処理を起動し、実行結果をユーザーへ伝える Presentation 層のモジュールです。

このモジュールは3つのプロシージャーを持ちます。

RunImageResize が外部からの呼び出しを受け取る唯一の公開プロシージャーで、処理の起動と結果表示という2つの役割をつなぎます。

ShowResizeExecutionResult は実行結果に応じてどのメッセージを表示するかを判断し、BuildResizeCompletionMessage は完了メッセージの文言を組み立てます。

業務ロジックやファイル操作はすべて下位モジュールへ委譲しており、このモジュールは「何を起動し、何を表示するか」という画面側の制御だけを担っています。

含まれるプロシージャー

このモジュールに含まれるプロシージャーは、次のとおりです。

  • RunImageResize(Public Sub)— ボタン操作の受け口。処理の起動と結果表示をつなぐ
  • ShowResizeExecutionResult(Private Sub)— 実行結果に応じたメッセージを表示する
  • BuildResizeCompletionMessage(Private Function)— 完了メッセージの文言を組み立てる

RunImageResize だけが外部から呼び出せる公開プロシージャーで、ShowResizeExecutionResultBuildResizeCompletionMessagePrivate のため、このモジュール内からのみ使われます。

読む順番としては RunImageResize から始めて、ShowResizeExecutionResultBuildResizeCompletionMessage の順に追うと流れを把握しやすくなります。

RunImageResizeプロシージャーの解説

全体像

RunImageResize は、ボタン操作からリサイズ処理を起動し、結果を表示するプロシージャーです。

処理は2段階で構成されます。

まず ExecuteImageResize でリサイズ処理本体を実行し、次に ShowResizeExecutionResult で結果をメッセージボックスに表示します。

このプロシージャー自身はリサイズ処理の内容を一切持たず、「起動して、表示する」という2ステップの橋渡しに専念しています。

コード全文

コード全文は、次のとおりです。

Public Sub RunImageResize()
    ' Description
    '   ボタン操作から画像リサイズ処理を起動し、
    '   実行結果に応じたメッセージをユーザーへ表示する。
    '
    ' Arguments
    '   (None)
    '
    ' Returns
    '   なし
    '
    ' Dependency Tree
    '   RunImageResize
    '     ├─ ExecuteImageResize (App_ImageResizeRunner)
    '     └─ ShowResizeExecutionResult (Pres_ImageResizeLauncher)

    ' リサイズ処理の実行
    '--------------------------------------------------------------------------
    ' 実行結果格納用の構造体を初期化
    Dim execution_result As ResizeExecutionResult

    ' アプリケーション層でリサイズ処理全体を実行
    Call ExecuteImageResize(execution_result)

    ' 実行結果の表示
    '--------------------------------------------------------------------------
    ' 実行結果に応じたメッセージをユーザーへ表示
    Call ShowResizeExecutionResult(execution_result)

End Sub

ステップごとの解説

リサイズ処理を実行する
    ' リサイズ処理の実行
    '--------------------------------------------------------------------------
    ' 実行結果格納用の構造体を初期化
    Dim execution_result As ResizeExecutionResult

    ' アプリケーション層でリサイズ処理全体を実行
    Call ExecuteImageResize(execution_result)

ここでは、リサイズ処理の結果を受け取るための ResizeExecutionResult 型変数を宣言し、ExecuteImageResize を呼び出して処理全体を実行しています。

ResizeExecutionResultApp_ImageResizeRunner で定義されている構造体で、対象件数・保存件数・スキップ件数・エラーメッセージを保持します。

execution_resultByRef で渡されるため、ExecuteImageResize の中で更新された内容がここに反映されます。

このステップはリサイズ処理のすべてを下位モジュールへ委ねており、このプロシージャーは「処理を依頼する」だけの役割です。

実行結果をメッセージで表示する
    ' 実行結果の表示
    '--------------------------------------------------------------------------
    ' 実行結果に応じたメッセージをユーザーへ表示
    Call ShowResizeExecutionResult(execution_result)

ここでは、リサイズ処理後の実行結果をユーザーへ通知しています。

ShowResizeExecutionResultexecution_result を渡し、エラー・0件・正常完了の各状態に応じたメッセージボックスを表示します。

このステップにより、RunImageResize の処理が完結します。

ユーザーはメッセージの内容で「何件処理されたか」「エラーがあったか」を把握できます。

補足・注意点

  • ExecuteImageResize が失敗してエラーメッセージが入った場合でも、ShowResizeExecutionResult は必ず呼ばれます。エラーの有無はメッセージ表示の分岐で判断される設計です。
  • このプロシージャーにはエラーハンドリングがありません。予期しない VBA ランタイムエラーが発生した場合は、標準のエラーダイアログが表示されます。

ShowResizeExecutionResultプロシージャーの解説

全体像

ShowResizeExecutionResult は、ResizeExecutionResult の内容に応じて、ユーザーへ適切なメッセージボックスを表示するプロシージャーです。

表示パターンは3種類あります。

エラーメッセージが入っている場合は警告メッセージを表示して終了します。

対象画像が 0 件の場合は「ファイルが見つからなかった」旨を通知します。

正常に完了した場合は BuildResizeCompletionMessage で組み立てた完了メッセージを表示します。

コード全文

コード全文は、次のとおりです。

Private Sub ShowResizeExecutionResult(ByRef execution_result As ResizeExecutionResult)
    ' Description
    '   画像リサイズ処理の実行結果に応じて、
    '   ユーザーへ適切なメッセージを表示する。
    '
    ' Arguments
    '   execution_result : 画像リサイズ処理の実行結果
    '
    ' Returns
    '   なし
    '
    ' Dependency Tree
    '   ShowResizeExecutionResult
    '     └─ BuildResizeCompletionMessage (Pres_ImageResizeLauncher)

    ' 異常終了時の表示
    '--------------------------------------------------------------------------
    ' エラーメッセージが返ってきている場合は警告表示して終了
    If Len(Trim$(execution_result.error_message)) > 0 Then
        MsgBox execution_result.error_message, vbExclamation
        Exit Sub
    End If

    ' 対象画像なし時の表示
    '--------------------------------------------------------------------------
    ' 対象画像が 0 件なら正常完了メッセージは出さずに終了
    If execution_result.target_file_count = 0 Then
        MsgBox "フォルダに画像ファイルが見つかりませんでした。処理を中断します。", vbExclamation
        Exit Sub
    End If

    ' 正常終了時の表示
    '--------------------------------------------------------------------------
    ' 実行結果を含む完了メッセージを表示
    MsgBox BuildResizeCompletionMessage(execution_result), vbInformation

End Sub

ステップごとの解説

エラーが発生していた場合に警告を表示する
    ' 異常終了時の表示
    '--------------------------------------------------------------------------
    ' エラーメッセージが返ってきている場合は警告表示して終了
    If Len(Trim$(execution_result.error_message)) > 0 Then
        MsgBox execution_result.error_message, vbExclamation
        Exit Sub
    End If

ここでは、error_message に文字列が入っているかどうかを確認しています。

文字列がある場合は処理が途中で異常終了しており、その内容を vbExclamation(警告アイコン)付きのメッセージボックスでそのまま表示します。

Trim$ で前後の空白を除いてから長さを確認することで、空白のみの文字列をエラーなしと正しく扱えます。

表示後は Exit Sub で終了するため、後続の正常完了表示へは進みません。このステップが最優先で評価される設計のため、エラーが起きていれば必ずここで処理が止まります。

対象ファイルが 0 件の場合に通知する
    ' 対象画像なし時の表示
    '--------------------------------------------------------------------------
    ' 対象画像が 0 件なら正常完了メッセージは出さずに終了
    If execution_result.target_file_count = 0 Then
        MsgBox "フォルダに画像ファイルが見つかりませんでした。処理を中断します。", vbExclamation
        Exit Sub
    End If

ここでは、入力フォルダに対象画像が1件も見つからなかった場合に通知しています。

エラーではないものの、正常に処理が完了した状態でもないため、vbExclamation で「0 件だった」ことをユーザーへ伝えます。

ExecuteImageResize の中でも target_file_count = 0 の場合は早期リターンされますが、その後ここでも同じ件数を確認しています。

このステップで通知することで、ユーザーはフォルダに画像がなかったことを認識できます。

正常完了のメッセージを表示する
    ' 正常終了時の表示
    '--------------------------------------------------------------------------
    ' 実行結果を含む完了メッセージを表示
    MsgBox BuildResizeCompletionMessage(execution_result), vbInformation

ここでは、エラーも 0 件もなかった場合に正常完了を通知しています。

BuildResizeCompletionMessage で組み立てた文言を vbInformation(情報アイコン)付きのメッセージボックスで表示します。

このステップに到達するのは、処理が正常に完了した場合のみです。

前の2ステップがガード条件として機能しており、残った状態が「成功」であるという構造になっています。

補足・注意点

  • MsgBox の第2引数にアイコン種別を指定しています。エラーや 0 件は vbExclamation(⚠ アイコン)、正常完了は vbInformation(ℹ アイコン)です。ユーザーが視覚的に結果を区別しやすい設計になっています。

BuildResizeCompletionMessageプロシージャーの解説

全体像

BuildResizeCompletionMessage は、ResizeExecutionResult の内容から完了メッセージの文言を組み立てて返す関数です。

基本の完了文に続けて、対象件数・保存件数を追記します。スキップが発生していた場合のみ、スキップ件数の補足も追加します。

文言の組み立てだけに専念しており、メッセージボックスの表示は呼び出し元の ShowResizeExecutionResult が担います。

コード全文

コード全文は、次のとおりです。

Private Function BuildResizeCompletionMessage( _
    ByRef execution_result As ResizeExecutionResult _
) As String

    ' Description
    '   実行結果から、完了を伝えるメッセージを組み立てて返す。
    '
    ' Arguments
    '   execution_result : 画像リサイズ処理の実行結果
    '
    ' Returns
    '   String
    '     実行結果を表すメッセージ
    '
    ' Dependency Tree
    '   BuildResizeCompletionMessage
    '     (none)

    ' 完了メッセージの組み立て
    '--------------------------------------------------------------------------
    ' まずは基本となる完了メッセージを設定
    Dim completion_message As String
    completion_message = "リサイズが完了しました。"
    completion_message = completion_message & vbCrLf & vbCrLf

    ' 対象件数と保存件数の追記
    completion_message = completion_message & "対象画像数: " & execution_result.target_file_count & " 件"
    completion_message = completion_message & vbCrLf
    completion_message = completion_message & "保存件数: " & execution_result.resized_file_count & " 件"

    ' 同名ファイルによるスキップが発生した場合は補足
    If execution_result.skipped_existing_count > 0 Then
        completion_message = completion_message & vbCrLf
        completion_message = completion_message & _
            "同名ファイルがすでに存在したため、" & execution_result.skipped_existing_count & " 件をスキップしました。"
    End If

    BuildResizeCompletionMessage = completion_message

End Function

ステップごとの解説

基本の完了文を設定する
    ' 完了メッセージの組み立て
    '--------------------------------------------------------------------------
    ' まずは基本となる完了メッセージを設定
    Dim completion_message As String
    completion_message = "リサイズが完了しました。"
    completion_message = completion_message & vbCrLf & vbCrLf

ここでは、メッセージの土台となる完了文を設定しています。

vbCrLf & vbCrLf で2行分の改行を入れることで、後続の件数情報との間に視覚的な余白を設けています。

この変数 completion_message に以降のステップで情報を追記していくことで、1つのメッセージ文字列を段階的に組み立てていきます。

対象件数と保存件数を追記する
    ' 対象件数と保存件数の追記
    completion_message = completion_message & "対象画像数: " & execution_result.target_file_count & " 件"
    completion_message = completion_message & vbCrLf
    completion_message = completion_message & "保存件数: " & execution_result.resized_file_count & " 件"

ここでは、入力フォルダで見つかった画像の総数と、実際に保存できた件数を追記しています。

2つの数値が異なる場合(スキップが発生した場合など)にユーザーが気づける情報です。

改行は1行分(vbCrLf)で区切っており、対象件数と保存件数が並んで表示されます。

スキップが発生していた場合に補足を追加する
    ' 同名ファイルによるスキップが発生した場合は補足
    If execution_result.skipped_existing_count > 0 Then
        completion_message = completion_message & vbCrLf
        completion_message = completion_message & _
            "同名ファイルがすでに存在したため、" & execution_result.skipped_existing_count & " 件をスキップしました。"
    End If

    BuildResizeCompletionMessage = completion_message

ここでは、スキップ件数が1件以上あった場合のみ、その旨の補足文を追加しています。

スキップが0件のときはこの行は追記されないため、不要な情報がメッセージに混じることはありません。

最後に BuildResizeCompletionMessage = completion_message で組み立てた文字列を戻り値として返します。

このステップで関数の処理が完結し、文言が ShowResizeExecutionResult のメッセージボックスへ渡されます。

補足・注意点

  • このプロシージャーは文言の組み立てだけを担っており、MsgBox の呼び出しは行いません。表示方法の変更(たとえば GUI フォームへの切り替えなど)が必要になった場合、このプロシージャーは変更不要です。
  • スキップ件数の補足が追加されるのは skipped_existing_count > 0 のときのみです。0 件の場合はメッセージが余計に長くならないよう省略されています。

App_ImageResizeRunnerモジュールの解説

全体像

App_ImageResizeRunner は、画像リサイズ処理のユースケース全体を制御する Application 層のモジュールです。

このモジュールは2つの公開・非公開プロシージャーに加え、2つの Type 宣言を持ちます。

ExecuteImageResize がリサイズ処理全体のフロー制御を担い、ProcessSingleImageFile が1件の画像に対するサイズ取得・判定・計算・保存の一連の処理を担います。

どちらも自身では計算やファイル操作は行わず、実処理はすべて下位モジュールへ委譲しています。

Type 宣言の ResizeSettingsResizeExecutionResult はこのモジュールで定義されており、プロシージャー間で設定値や実行結果を受け渡す器として使われます。

このモジュールを読むときは「何をどの順番で呼ぶか」という制御の流れに着目すると、すっきり読めます。

各呼び出し先の処理内容は、Dependency Tree に記載されたモジュールの解説書を参照してください。

含まれる型定義とプロシージャー

このモジュールに含まれる主な型定義とプロシージャーは、次のとおりです。

型定義(Type)

  • ResizeSettings — リサイズ処理に必要な設定値を保持する構造体
  • ResizeExecutionResult — リサイズ処理の実行結果を保持する構造体

プロシージャー

  • ExecuteImageResize(Public Sub)— リサイズ処理のユースケース全体を制御する
  • ProcessSingleImageFile(Private Sub)— 1件の画像に対するリサイズ処理を実行する

ExecuteImageResize が上位フローから呼ばれ、内部で ProcessSingleImageFile を画像1件ごとに呼ぶ構成です。

ProcessSingleImageFilePrivate のため、このモジュール内からのみ呼ばれます。

型定義の解説

ResizeSettings

ResizeSettings は、リサイズ処理で使用する設定値を一括して保持する構造体です。

Public Type ResizeSettings
    ' 入力画像格納フォルダパス
    source_folder_path As String

    ' リサイズ後画像の出力先フォルダパス
    output_folder_path As String

    ' リサイズ後の最大幅
    max_width As Long

    ' リサイズ後の最大高さ
    max_height As Long
End Type

source_folder_pathoutput_folder_path はフォルダのパスを、max_widthmax_height はリサイズ後の上限サイズを保持します。

これらの値は Inf_ImageFileAccessorTryBuildResizeSettings で組み立てられ、ExecuteImageResize を通じて各処理に受け渡されます。

ResizeExecutionResult

ResizeExecutionResult は、リサイズ処理の実行結果をまとめて保持する構造体です。

Public Type ResizeExecutionResult
    ' 対象画像ファイル数
    target_file_count As Long

    ' 実際に保存できた画像ファイル数
    resized_file_count As Long

    ' 同名ファイルの存在による保存スキップ回数
    skipped_existing_count As Long

    ' 異常終了時のエラーメッセージ
    error_message As String
End Type

target_file_count は入力フォルダで見つかった画像の総数、resized_file_count は実際に保存した件数、skipped_existing_count は出力先に同名ファイルが存在してスキップした件数です。

error_message に文字列が入っている場合は、処理が異常終了したことを意味します。

この構造体は Pres_ImageResizeLauncher まで渡され、最終的なメッセージ表示に使われます。

ExecuteImageResizeプロシージャーの解説

全体像

ExecuteImageResize は、画像リサイズ処理のユースケース全体を制御するプロシージャーです。

処理の流れは大きく2段階に分かれます。

まず設定と対象ファイルを取得し、次に画像1件ごとにリサイズを実行します。

各段階でエラーや件数が 0 件の場合は早期リターンする設計になっており、不正な状態のまま処理を進めないように制御しています。

引数 execution_resultByRef で受け取っており、処理の途中で件数やエラーメッセージを書き込みながら更新していきます。

呼び出し元の Pres_ImageResizeLauncher はこの結果をもとにメッセージを表示します。

コード全文

コード全文は、次のとおりです。

Public Sub ExecuteImageResize(ByRef execution_result As ResizeExecutionResult)
    ' Description
    '   画像リサイズ処理のユースケース全体を制御する。
    '   設定取得、対象ファイル列挙、画像ごとのリサイズ保存処理を順に実行する。
    '
    ' Arguments
    '   execution_result : 画像リサイズ処理の実行結果
    '
    ' Returns
    '   なし
    '
    ' Dependency Tree
    '   ExecuteImageResize
    '     ├─ TryBuildResizeSettings (Inf_ImageFileAccessor)
    '     ├─ FindTargetImageFilePaths (Inf_ImageFileAccessor)
    '     └─ ProcessSingleImageFile (App_ImageResizeRunner)

    ' 設定と対象ファイルの取得
    '--------------------------------------------------------------------------
    ' 実行時に利用する設定を取得
    Dim resize_settings As ResizeSettings
    If Not TryBuildResizeSettings(resize_settings, execution_result.error_message) Then Exit Sub

    ' 入力フォルダ配下の画像ファイルパス一覧を取得
    Dim source_file_path_coll As Collection
    Set source_file_path_coll = FindTargetImageFilePaths(resize_settings.source_folder_path)
    If source_file_path_coll Is Nothing Then Exit Sub

    ' 対象画像が0件なら結果へ反映
    execution_result.target_file_count = source_file_path_coll.Count
    If execution_result.target_file_count = 0 Then Exit Sub

    ' 画像ごとのリサイズ処理
    '--------------------------------------------------------------------------
    ' コレクションに格納した各画像パスを順に処理
    Dim source_file_path As Variant
    For Each source_file_path In source_file_path_coll
        ' 単一画像のリサイズ保存処理を実行
        Call ProcessSingleImageFile( _
            CStr(source_file_path), _
            resize_settings, _
            execution_result _
        )

        ' 途中で異常が発生した場合は後続処理を中止する
        If Len(Trim$(execution_result.error_message)) > 0 Then Exit Sub
    Next source_file_path

End Sub

ステップごとの解説

実行設定を組み立てる
    ' 設定と対象ファイルの取得
    '--------------------------------------------------------------------------
    ' 実行時に利用する設定を取得
    Dim resize_settings As ResizeSettings
    If Not TryBuildResizeSettings(resize_settings, execution_result.error_message) Then Exit Sub

ここでは、リサイズ処理で使用する設定値を ResizeSettings 型の変数 resize_settings に組み立てています。

TryBuildResizeSettingsInf_ImageFileAccessor モジュールの関数で、シートから最大幅・最大高さを読み取り、入出力フォルダのパスを確認します。

戻り値が False(設定取得失敗)の場合は Exit Sub で即座に処理を抜けます。失敗理由は execution_result.error_message に書き込まれており、呼び出し元でそのまま表示に使われます。

このステップが後続のすべての処理の前提となっており、設定が正しく取れた状態からのみ次へ進む設計になっています。

対象ファイルを列挙する
    ' 入力フォルダ配下の画像ファイルパス一覧を取得
    Dim source_file_path_coll As Collection
    Set source_file_path_coll = FindTargetImageFilePaths(resize_settings.source_folder_path)
    If source_file_path_coll Is Nothing Then Exit Sub

    ' 対象画像が0件なら結果へ反映
    execution_result.target_file_count = source_file_path_coll.Count
    If execution_result.target_file_count = 0 Then Exit Sub

ここでは、入力フォルダ内の PNG・JPG・JPEG ファイルのパス一覧を Collection として取得しています。

FindTargetImageFilePathsInf_ImageFileAccessor モジュールの関数です。

Nothing が返ってきた場合(フォルダの取得に失敗した場合など)は即座に処理を抜けます。

続けて件数を target_file_count に記録し、0 件の場合もここで処理を終了します。

件数が 0 のままループへ進んでしまうと、後続の処理が意味を持たないため、この段階で早期リターンしています。

画像1件ごとにリサイズ処理を実行する
    ' 画像ごとのリサイズ処理
    '--------------------------------------------------------------------------
    ' コレクションに格納した各画像パスを順に処理
    Dim source_file_path As Variant
    For Each source_file_path In source_file_path_coll
        ' 単一画像のリサイズ保存処理を実行
        Call ProcessSingleImageFile( _
            CStr(source_file_path), _
            resize_settings, _
            execution_result _
        )

        ' 途中で異常が発生した場合は後続処理を中止する
        If Len(Trim$(execution_result.error_message)) > 0 Then Exit Sub
    Next source_file_path

ここでは、前のステップで列挙した画像パス一覧を1件ずつ処理しています。

For Each でコレクションを順に取り出し、ProcessSingleImageFile へ渡します。

Collection に格納されている値は Variant 型のため、CStr で文字列に変換してから渡しています。

各画像の処理後に error_message をチェックしており、異常が検出された時点でループを抜けて処理を終了します。

1件のエラーで残りの画像処理をすべて中断する設計のため、エラーの原因を特定しやすくなっています。

このステップで全画像の処理が完了すると、execution_result に最終的な件数が記録された状態で呼び出し元へ制御が戻ります。

補足・注意点

  • source_file_path_coll のループ変数を Variant で受けているのは、CollectionFor Each では Variant が必要なためです。String で受けようとするとエラーになります。
  • error_message に値があるかどうかの確認に Len(Trim$(...)) > 0 を使っています。空白のみの文字列をエラーなしと見なすためです。
  • このプロシージャー自体にはエラーハンドリング(On Error)がありません。予期しないランタイムエラーが発生した場合は VBA の標準エラーダイアログが表示されます。

ProcessSingleImageFileプロシージャーの解説

全体像

ProcessSingleImageFile は、1枚の画像ファイルに対して、サイズ取得・拡大判定・リサイズ計算・出力先決定・保存という一連の処理を順に実行し、実行結果を更新するプロシージャーです。

処理の流れは4段階に分かれます。

まず元画像のサイズを取得し、次に拡大が不要かどうかを判定します。

続いてリサイズ後のサイズを計算し、最後に出力先パスを決めて保存します。

いずれかのステップで問題が生じた場合は、execution_result.error_message にメッセージを書き込んで処理を中断します。

Private なプロシージャーのため、このモジュール外からは直接呼べません。ExecuteImageResize からのみ呼ばれます。

コード全文

コード全文は、次のとおりです。

Private Sub ProcessSingleImageFile( _
    ByVal source_file_path As String, _
    ByRef resize_settings As ResizeSettings, _
    ByRef execution_result As ResizeExecutionResult _
)
    ' Description
    '   単一画像ファイルに対して、サイズ取得、出力先決定、
    '   リサイズ保存までを実行し、実行結果を更新する。
    '
    ' Arguments
    '   source_file_path  : 入力画像ファイルパス
    '   resize_settings   : リサイズ設定
    '   execution_result  : 画像リサイズ処理の実行結果
    '
    ' Returns
    '   なし
    '
    ' Dependency Tree
    '   ProcessSingleImageFile
    '     ├─ TryLoadImageSize (Inf_ImageFileAccessor)
    '     ├─ IsUpscalingRequired (Dom_ResizeDimensionCalculator)
    '     ├─ CalculateResizedDimensions (Dom_ResizeDimensionCalculator)
    '     ├─ BuildResizedFilePath (Util_ImagePathHelper)
    '     ├─ ImageFileExists (Inf_ImageFileAccessor)
    '     └─ SaveResizedImageFile (Inf_ImageFileAccessor)

    ' 元画像サイズの取得
    '--------------------------------------------------------------------------
    ' 入力画像の幅と高さを取得
    Dim source_width As Long
    Dim source_height As Long
    If Not TryLoadImageSize( _
        source_file_path, _
        source_width, _
        source_height, _
        execution_result.error_message _
    ) Then
        Exit Sub
    End If

    ' リサイズ方向の妥当性確認
    '--------------------------------------------------------------------------
    ' 拡大が必要になる画像は対象外として処理を中断
    If IsUpscalingRequired( _
        source_width, _
        source_height, _
        resize_settings.max_width, _
        resize_settings.max_height _
    ) Then
        execution_result.error_message = _
            "指定サイズでは拡大が必要になる画像が含まれているため、処理を中断しました。" & vbCrLf & _
            "対象ファイル: " & source_file_path
        Exit Sub
    End If

    ' リサイズ後サイズの算出
    '--------------------------------------------------------------------------
    ' 最大幅・最大高さに収まるリサイズ後サイズを算出
    Dim resized_width As Long
    Dim resized_height As Long
    Call CalculateResizedDimensions( _
        source_width, _
        source_height, _
        resize_settings.max_width, _
        resize_settings.max_height, _
        resized_width, _
        resized_height _
    )

    ' 出力先決定と保存
    '--------------------------------------------------------------------------
    ' 出力フォルダ配下の保存先パスを組み立てる
    Dim output_file_path As String
    output_file_path = BuildResizedFilePath( _
        source_file_path, _
        resize_settings.output_folder_path _
    )

    ' 同名ファイルがすでに存在する場合は上書きせずにスキップ
    If ImageFileExists(output_file_path) Then
        execution_result.skipped_existing_count = execution_result.skipped_existing_count + 1
        Exit Sub
    End If

    ' 画像保存に失敗した場合はエラー内容を結果へ返す
    If Not SaveResizedImageFile( _
        source_file_path, _
        output_file_path, _
        resized_width, _
        resized_height, _
        execution_result.error_message _
    ) Then
        Exit Sub
    End If

    ' 保存が成功したら結果へ加算
    execution_result.resized_file_count = execution_result.resized_file_count + 1

End Sub

ステップごとの解説

元画像のサイズを取得する
    ' 元画像サイズの取得
    '--------------------------------------------------------------------------
    ' 入力画像の幅と高さを取得
    Dim source_width As Long
    Dim source_height As Long
    If Not TryLoadImageSize( _
        source_file_path, _
        source_width, _
        source_height, _
        execution_result.error_message _
    ) Then
        Exit Sub
    End If

ここでは、入力画像の幅と高さを取得しています。

TryLoadImageSizeInf_ImageFileAccessor モジュールの関数で、WIA(Windows Image Acquisition Library)を使って画像を読み込み、サイズを返します。

取得に失敗した場合は error_message にメッセージが入り、False が返ってくるため、即座に Exit Sub で処理を抜けます。

このステップで取得した source_widthsource_height は、続く判定とサイズ計算の基準として使われます。

拡大が必要かどうかを判定する
    ' リサイズ方向の妥当性確認
    '--------------------------------------------------------------------------
    ' 拡大が必要になる画像は対象外として処理を中断
    If IsUpscalingRequired( _
        source_width, _
        source_height, _
        resize_settings.max_width, _
        resize_settings.max_height _
    ) Then
        execution_result.error_message = _
            "指定サイズでは拡大が必要になる画像が含まれているため、処理を中断しました。" & vbCrLf & _
            "対象ファイル: " & source_file_path
        Exit Sub
    End If

ここでは、指定された最大幅・最大高さへ収めようとすると画像が拡大されてしまうかどうかを確認しています。

IsUpscalingRequiredDom_ResizeDimensionCalculator モジュールの関数です。

拡大が必要と判定された場合(元画像が最大サイズより小さく、指定サイズに合わせると大きくなってしまう場合)は、エラーメッセージを設定して処理を中断します。

どの画像が原因かわかるよう、ファイルパスもメッセージに含めています。

このアプリケーションは「縮小専用」の設計であり、拡大は意図しない品質劣化につながるため、明示的に弾く設計になっています。

リサイズ後のサイズを計算する
    ' リサイズ後サイズの算出
    '--------------------------------------------------------------------------
    ' 最大幅・最大高さに収まるリサイズ後サイズを算出
    Dim resized_width As Long
    Dim resized_height As Long
    Call CalculateResizedDimensions( _
        source_width, _
        source_height, _
        resize_settings.max_width, _
        resize_settings.max_height, _
        resized_width, _
        resized_height _
    )

ここでは、縦横比を維持しながら最大幅・最大高さの範囲に収まるリサイズ後サイズを計算しています。

CalculateResizedDimensionsDom_ResizeDimensionCalculator モジュールの関数で、計算結果を ByRef の引数 resized_widthresized_height に書き込んで返します。

元画像がすでに指定サイズ以内の場合は元のサイズがそのまま返ってきます(前のステップで拡大不要な場合のみここに到達するため、縮小または等倍になります)。

ここで計算したサイズは次の保存ステップで使われます。

出力先を決め、保存する
    ' 出力先決定と保存
    '--------------------------------------------------------------------------
    ' 出力フォルダ配下の保存先パスを組み立てる
    Dim output_file_path As String
    output_file_path = BuildResizedFilePath( _
        source_file_path, _
        resize_settings.output_folder_path _
    )

    ' 同名ファイルがすでに存在する場合は上書きせずにスキップ
    If ImageFileExists(output_file_path) Then
        execution_result.skipped_existing_count = execution_result.skipped_existing_count + 1
        Exit Sub
    End If

    ' 画像保存に失敗した場合はエラー内容を結果へ返す
    If Not SaveResizedImageFile( _
        source_file_path, _
        output_file_path, _
        resized_width, _
        resized_height, _
        execution_result.error_message _
    ) Then
        Exit Sub
    End If

    ' 保存が成功したら結果へ加算
    execution_result.resized_file_count = execution_result.resized_file_count + 1

ここでは、出力先のファイルパスを組み立て、画像を保存しています。

BuildResizedFilePath で出力先パスを作り、ImageFileExists で同名ファイルの存在を確認します。

すでに同名ファイルが存在する場合は skipped_existing_count を加算してスキップします。上書きせずスキップとすることで、再実行しても既存の出力を守れる設計になっています。

存在しない場合は SaveResizedImageFile でリサイズ保存を実行します。保存が成功したら resized_file_count を加算し、1枚分の処理が完結します。

この件数の積み上げが最終的なメッセージ表示の根拠になります。

補足・注意点

  • このアプリケーションでは、ユーザーが指定した最大サイズを超える画像だけを縮小します。元画像がすでに最大サイズ以内に収まっている場合は、縮小せずそのままのサイズで保存されます。一方、指定した最大サイズに合わせるために拡大が必要な場合は、エラーとして処理を中断します。
  • 同名ファイルのスキップ判定は出力先パスで行っています。入力ファイル名に _リサイズ済み が付いた名前が出力先に存在するかどうかで判断します。

Dom_ResizeDimensionCalculatorモジュールの解説

全体像

Dom_ResizeDimensionCalculator は、縦横比を維持したリサイズ後サイズを計算する Domain 層のモジュールです。

このモジュールは3つのプロシージャーを持ちます。

CalculateResizedDimensions がリサイズ後の幅と高さを返します。

IsUpscalingRequired は、指定サイズへ収めようとすると拡大になってしまうかどうかを判定します。

どちらも内部で GetResizeRatio(比率算出)を利用しています。

Excelシートやファイルシステムとは一切やりとりせず、数値の計算ロジックだけに専念しています。

そのためテストや動作確認がしやすく、ロジック変更の際の影響範囲も限定的です。

含まれるプロシージャー

このモジュールに含まれるプロシージャーは、次のとおりです。

  • CalculateResizedDimensions(Public Sub)— 縦横比を維持したリサイズ後サイズを計算して返す
  • IsUpscalingRequired(Public Function)— 拡大が必要かどうかを判定して返す
  • GetResizeRatio(Private Function)— リサイズ比率を算出して返す

CalculateResizedDimensionsIsUpscalingRequired はどちらも公開されており、上位の App_ImageResizeRunner から呼ばれます。

GetResizeRatio はこのモジュール内でのみ使われる内部ヘルパーです。

CalculateResizedDimensionsプロシージャーの解説

全体像

CalculateResizedDimensions は、元画像のサイズと最大サイズを受け取り、縦横比を維持したリサイズ後の幅と高さを計算して返すプロシージャーです。

処理は2段階で、まず GetResizeRatio で最大サイズ内に収めるための比率を求め、次にその比率を元画像の幅と高さに掛けて、リサイズ後サイズを算出します。

ここで使っている GetResizeRatio は、サイズ計算専用のものではありません。拡大が必要かどうかの判定でも、同じ比率を利用します。

具体的には、IsUpscalingRequired が GetResizeRatio の結果を見て、その値が 1 を超えるかどうかで拡大の要否を判定します。

一方、CalculateResizedDimensions は、その比率を使ってサイズを計算するだけで、拡大の可否そのものは判断しません。

そのため、このプロシージャーは「拡大判定をしながらサイズも計算する」のではなく、「共通の比率計算結果を使って、サイズ計算だけを担当する」役割になっています。

実際の呼び出し順としては、App_ImageResizeRunner が先に IsUpscalingRequired で拡大の要否を確認し、拡大不要と判断できた場合にだけ CalculateResizedDimensions を呼ぶ設計です。

コード全文

コード全文は、次のとおりです。

Public Sub CalculateResizedDimensions( _
    ByVal source_width As Long, _
    ByVal source_height As Long, _
    ByVal max_width As Long, _
    ByVal max_height As Long, _
    ByRef resized_width As Long, _
    ByRef resized_height As Long _
)
    ' Description
    '   元画像サイズと最大サイズから、
    '   縦横比を維持したリサイズ後サイズを算出する。
    '   元画像が指定サイズ以内の場合は拡大せず、そのままのサイズを返す。
    '
    ' Arguments
    '   source_width    : 元画像の幅
    '   source_height   : 元画像の高さ
    '   max_width       : リサイズ後の最大幅
    '   max_height      : リサイズ後の最大高さ
    '   resized_width   : リサイズ後の幅
    '   resized_height  : リサイズ後の高さ
    '
    ' Returns
    '   なし
    '
    ' Dependency Tree
    '   CalculateResizedDimensions
    '     └─ GetResizeRatio (Dom_ResizeDimensionCalculator)

    ' リサイズ比率の算出
    '--------------------------------------------------------------------------
    ' 最大幅に収まる比率を取得
    Dim resize_ratio As Double
    resize_ratio = GetResizeRatio(source_width, source_height, max_width, max_height)

    ' リサイズ後サイズの設定
    '--------------------------------------------------------------------------
    ' 算出した比率を元画像サイズへ反映
    resized_width = CLng(source_width * resize_ratio)
    resized_height = CLng(source_height * resize_ratio)

End Sub

ステップごとの解説

リサイズ比率を取得する
    ' リサイズ比率の算出
    '--------------------------------------------------------------------------
    ' 最大幅に収まる比率を取得
    Dim resize_ratio As Double
    resize_ratio = GetResizeRatio(source_width, source_height, max_width, max_height)

ここでは、元画像を最大サイズに収めるための比率を GetResizeRatio から取得しています。

比率は Double 型で受け取ります。

GetResizeRatio は幅方向・高さ方向それぞれの比率を計算し、小さいほうを返します。

たとえば幅方向の比率が 0.8、高さ方向が 0.6 なら 0.6 が返ります。

リサイズ後のサイズを確定する
    ' リサイズ後サイズの設定
    '--------------------------------------------------------------------------
    ' 算出した比率を元画像サイズへ反映
    resized_width = CLng(source_width * resize_ratio)
    resized_height = CLng(source_height * resize_ratio)

ここでは、前のステップで取得した比率を元のサイズに掛けて、リサイズ後の幅と高さを確定しています。

CLngDouble の計算結果を Long 型の整数へ変換するための関数で、小数点以下は四捨五入されます。

画像の幅と高さは整数のピクセル値で扱うため、ここでは Long 型へ変換しています。

計算結果は ByRef 引数の resized_widthresized_height に書き込まれ、呼び出し元の App_ImageResizeRunner へ返ります。

このステップでプロシージャーの処理が完結し、リサイズ後サイズが確定します。

補足・注意点

  • CLng による四捨五入の結果、元の縦横比から 1px 程度ずれることがあります。精密なサイズ管理が必要な場合は注意してください。

IsUpscalingRequiredプロシージャーの解説

全体像

IsUpscalingRequired は、指定サイズへ収めようとすると拡大が必要かどうかを判定して返す関数です。

内部で GetResizeRatio を呼んで比率を取得し、その比率が 1 を超えていれば「拡大が必要」と判断します。

比率が 1 を超えるということは、元画像が最大サイズより小さく、指定サイズに合わせると引き伸ばしになることを意味します。

コード全文

コード全文は、次のとおりです。

Public Function IsUpscalingRequired( _
    ByVal source_width As Long, _
    ByVal source_height As Long, _
    ByVal max_width As Long, _
    ByVal max_height As Long _
) As Boolean
    ' Description
    '   指定サイズへ収めるときに、拡大が必要かどうかを返す。
    '   幅比率と高さ比率の両方が 1 を超える場合は拡大が必要と判定する。
    '
    ' Arguments
    '   source_width   : 元画像の幅
    '   source_height  : 元画像の高さ
    '   max_width      : リサイズ後の最大幅
    '   max_height     : リサイズ後の最大高さ
    '
    ' Returns
    '   Boolean
    '     True  : 拡大が必要
    '     False : 拡大は不要
    '
    ' Dependency Tree
    '   IsUpscalingRequired
    '     └─ GetResizeRatio (Dom_ResizeDimensionCalculator)

    ' リサイズ比率の確認
    '--------------------------------------------------------------------------
    ' 比率が 1 を超える場合は拡大が必要とみなす
    IsUpscalingRequired = (GetResizeRatio(source_width, source_height, max_width, max_height) > 1#)

End Function

ステップごとの解説

比率が 1 を超えるかどうかを判定する
    ' リサイズ比率の確認
    '--------------------------------------------------------------------------
    ' 比率が 1 を超える場合は拡大が必要とみなす
    IsUpscalingRequired = (GetResizeRatio(source_width, source_height, max_width, max_height) > 1#)

ここでは、GetResizeRatio が返す比率を 1 と比較しています。

比率が 1 を超えるということは、元画像を最大サイズに合わせると引き伸ばしになることを意味します。逆に 1 以下であれば、縮小またはそのままのサイズで収まります。

1# は倍精度浮動小数点数(Double 型)のリテラルで、Double 型の比率との比較を正確に行うために使われています。

このプロシージャーはこの1行だけで処理が完結しており、判定結果がそのまま関数の戻り値になります。

この判定が True を返した場合、つまり拡大が必要な場合は、App_ImageResizeRunner がエラーとして処理を中断します。

補足・注意点

  • GetResizeRatio は幅と高さの比率のうち小さいほうを返す設計です。
  • このアプリケーションは縮小専用の設計として意図されており、拡大が必要な画像は処理対象外として扱われます。

GetResizeRatioプロシージャーの解説

全体像

GetResizeRatio は、元画像サイズを最大サイズへ収めるための比率を計算して返す関数です。

幅方向と高さ方向それぞれの比率を計算し、小さいほうを返します。

これにより、幅と高さのどちらの辺も最大サイズをはみ出さずに収まるリサイズ比率が得られます。

Private 関数のため、このモジュール内からのみ呼ばれます。

コード全文

コード全文は、次のとおりです。

Private Function GetResizeRatio( _
    ByVal source_width As Long, _
    ByVal source_height As Long, _
    ByVal max_width As Long, _
    ByVal max_height As Long _
) As Double

    ' Description
    '   元画像サイズを最大サイズへ収めるための比率を返す。
    '   拡大判定ではこのままの比率を返し、呼び出し元で判断を分ける。
    '
    ' Arguments
    '   source_width   : 元画像の幅
    '   source_height  : 元画像の高さ
    '   max_width      : リサイズ後の最大幅
    '   max_height     : リサイズ後の最大高さ
    '
    ' Returns
    '   Double
    '     リサイズ比率
    '
    ' Dependency Tree
    '   GetResizeRatio
    '     (none)

    ' 幅と高さの比率を比較
    '--------------------------------------------------------------------------
    ' 幅方向から求まる比率を算出
    Dim width_ratio As Double
    width_ratio = max_width / source_width

    ' 高さ方向から求まる比率を算出
    Dim height_ratio As Double
    height_ratio = max_height / source_height

    ' 小さい比率を採用して最大幅に収める
    If width_ratio <= height_ratio Then
        GetResizeRatio = width_ratio
    Else
        GetResizeRatio = height_ratio
    End If

End Function

ステップごとの解説

幅方向と高さ方向の比率をそれぞれ計算する
    ' 幅と高さの比率を比較
    '--------------------------------------------------------------------------
    ' 幅方向から求まる比率を算出
    Dim width_ratio As Double
    width_ratio = max_width / source_width

    ' 高さ方向から求まる比率を算出
    Dim height_ratio As Double
    height_ratio = max_height / source_height

ここでは、幅と高さそれぞれについて「最大サイズ ÷ 元のサイズ」の比率を計算しています。

たとえば元画像が 1600px、最大幅が 1200px の場合、幅方向の比率は 1200 / 1600 = 0.75 となります。

比率が 1 未満なら縮小、1 を超えるなら拡大を意味します。

この時点では幅と高さそれぞれの比率をただ計算するだけで、どちらを使うかの判断は次のステップで行います。

小さいほうの比率を採用して返す
    ' 小さい比率を採用して最大幅に収める
    If width_ratio <= height_ratio Then
        GetResizeRatio = width_ratio
    Else
        GetResizeRatio = height_ratio
    End If

ここでは、幅方向と高さ方向の比率を比べて、小さいほうを関数の戻り値として返しています。

小さいほうの比率を採用することで、幅にも高さにも収まる縮小率が得られます。

たとえば幅方向が 0.75、高さ方向が 0.5 であれば 0.5 を採用します。

0.5 倍にすれば幅も高さもどちらも最大サイズ以内に収まることが保証されます。

この比率は CalculateResizedDimensions でサイズ計算に、IsUpscalingRequired で拡大判定に使われます。

Inf_ImageFileAccessorモジュールの解説

全体像

Inf_ImageFileAccessor は、外部リソース(Excelシート・フォルダ・画像ファイル)とのやりとりをすべて担う Infrastructure 層のモジュールです。

このモジュールは6つの公開関数と1つのプライベート関数を持ちます。

Excelシートから設定値を読み取り(TryBuildResizeSettings)、フォルダ内の画像を列挙し(FindTargetImageFilePaths)、WIA(Windows Image Acquisition Library)を使って画像の読み込み・リサイズ・保存を行います(TryLoadImageSizeSaveResizedImageFile)。

フォルダやファイルの存在確認(FolderExistsImageFileExists)もここに集約されています。

外部との接点を1つのモジュールに集めることで、設定の読み取り元やファイルの操作方法を変更したいときの修正範囲が明確になっています。

Microsoft Scripting RuntimeMicrosoft Windows Image Acquisition Library v2.0 の2つの参照設定が必要です。

含まれるプロシージャー

このモジュールに含まれるプロシージャーは、次のとおりです。

  • TryBuildResizeSettings(Public Function)— シートから設定値を読み取り ResizeSettings を組み立てる
  • FindTargetImageFilePaths(Public Function)— 入力フォルダ内の対象画像パスを列挙して返す
  • TryLoadImageSize(Public Function)— 画像ファイルの幅と高さを取得する
  • SaveResizedImageFile(Public Function)— 画像をリサイズして指定パスへ保存する
  • ImageFileExists(Public Function)— ファイルが存在するかどうかを返す
  • FolderExists(Public Function)— フォルダが存在するかどうかを返す
  • TryReadPositiveLongFromCell(Private Function)— セルから正の整数値を読み取る

TryBuildResizeSettingsプロシージャーの解説

全体像

TryBuildResizeSettings は、ブックの保存場所とシートの設定セルから、リサイズ処理で使用する設定値を組み立てて返す関数です。

処理は2つのフェーズで構成されます。

まずブックの保存場所をもとに入出力フォルダのパスを決定し、各フォルダの存在を確認します。

次にシートから最大幅・最大高さの数値を読み取ります。

いずれかのチェックで問題が見つかった場合は error_message にメッセージを入れて False を返します。

コード全文

コード全文は、次のとおりです。

Public Function TryBuildResizeSettings( _
    ByRef resize_settings As ResizeSettings, _
    ByRef error_message As String _
) As Boolean

    ' Description
    '   ブックと設定セルから、画像リサイズ処理で使用する設定値を組み立てる。
    '   必要なフォルダや設定値が不正な場合は、エラーメッセージを返す。
    '
    ' Arguments
    '   resize_settings : 画像リサイズ設定
    '   error_message   : エラーメッセージ
    '
    ' Returns
    '   Boolean
    '     True  : 設定取得成功
    '     False : 設定取得失敗
    '
    ' Dependency Tree
    '   TryBuildResizeSettings
    '     ├─ TryReadPositiveLongFromCell (Inf_ImageFileAccessor)
    '     └─ FolderExists (Inf_ImageFileAccessor)

    ' 固定仕様の取得
    '--------------------------------------------------------------------------
    Const SOURCE_FOLDER_NAME   As String = "A_リサイズ前画像ファイル"
    Const OUTPUT_FOLDER_NAME   As String = "B_リサイズ後の画像ファイル"
    Const SETTING_SHEET_INDEX  As Long = 1
    Const MAX_WIDTH_ROW        As Long = 25
    Const MAX_HEIGHT_ROW       As Long = 26
    Const SETTING_VALUE_COLUMN As Long = 5

    ' 保存場所と対象フォルダの確認
    '--------------------------------------------------------------------------
    ' 未保存ブックでは相対フォルダを組み立てられないため先に弾く
    If Len(Trim$(ThisWorkbook.Path)) = 0 Then
        error_message = "ブックの保存場所が取得できませんでした。保存済みのブックで実行してください。"
        Exit Function
    End If

    ' ブック保存場所配下の入力フォルダパスを設定
    resize_settings.source_folder_path = ThisWorkbook.Path & "\" & SOURCE_FOLDER_NAME

    ' 入力フォルダが存在しない場合は処理を中断
    If Not FolderExists(resize_settings.source_folder_path) Then
        error_message = "入力フォルダが見つかりませんでした。フォルダ名を確認してください。" & vbCrLf & _
            resize_settings.source_folder_path
        Exit Function
    End If

    ' ブック保存場所配下の出力フォルダパスを設定
    resize_settings.output_folder_path = ThisWorkbook.Path & "\" & OUTPUT_FOLDER_NAME

    ' 出力フォルダが存在しない場合は処理を中断
    If Not FolderExists(resize_settings.output_folder_path) Then
        error_message = "出力フォルダが見つかりませんでした。フォルダ名を確認してください。" & vbCrLf & _
            resize_settings.output_folder_path
        Exit Function
    End If

    ' 設定値の取得
    '--------------------------------------------------------------------------
    ' シートから最大幅を読み込む
    If Not TryReadPositiveLongFromCell( _
        ThisWorkbook.Worksheets(SETTING_SHEET_INDEX), _
        MAX_WIDTH_ROW, _
        SETTING_VALUE_COLUMN, _
        "最大幅", _
        resize_settings.max_width, _
        error_message _
    ) Then
        Exit Function
    End If

    ' シートから最大高さを読み込む
    If Not TryReadPositiveLongFromCell( _
        ThisWorkbook.Worksheets(SETTING_SHEET_INDEX), _
        MAX_HEIGHT_ROW, _
        SETTING_VALUE_COLUMN, _
        "最大高さ", _
        resize_settings.max_height, _
        error_message _
    ) Then
        Exit Function
    End If

    TryBuildResizeSettings = True

End Function

ステップごとの解説

定数で設定場所を定義する
    ' 固定仕様の取得
    '--------------------------------------------------------------------------
    Const SOURCE_FOLDER_NAME   As String = "A_リサイズ前画像ファイル"
    Const OUTPUT_FOLDER_NAME   As String = "B_リサイズ後の画像ファイル"
    Const SETTING_SHEET_INDEX  As Long = 1
    Const MAX_WIDTH_ROW        As Long = 25
    Const MAX_HEIGHT_ROW       As Long = 26
    Const SETTING_VALUE_COLUMN As Long = 5

ここでは、フォルダ名とシート上の設定値の位置を定数として定義しています。

入力フォルダ名は A_リサイズ前画像ファイル、出力フォルダ名は B_リサイズ後の画像ファイル で、どちらもブックと同じフォルダに配置します。

設定値はブック内の最初のシート(インデックス 1)の25行目と26行目、E列(5列目)に置かれている想定です。

フォルダ名や設定セルの位置を変更したい場合はここの定数を修正します。

ブックの保存場所とフォルダの存在を確認する
    ' 保存場所と対象フォルダの確認
    '--------------------------------------------------------------------------
    ' 未保存ブックでは相対フォルダを組み立てられないため先に弾く
    If Len(Trim$(ThisWorkbook.Path)) = 0 Then
        error_message = "ブックの保存場所が取得できませんでした。保存済みのブックで実行してください。"
        Exit Function
    End If

    ' ブック保存場所配下の入力フォルダパスを設定
    resize_settings.source_folder_path = ThisWorkbook.Path & "\" & SOURCE_FOLDER_NAME

    ' 入力フォルダが存在しない場合は処理を中断
    If Not FolderExists(resize_settings.source_folder_path) Then
        error_message = "入力フォルダが見つかりませんでした。フォルダ名を確認してください。" & vbCrLf & _
            resize_settings.source_folder_path
        Exit Function
    End If

    ' ブック保存場所配下の出力フォルダパスを設定
    resize_settings.output_folder_path = ThisWorkbook.Path & "\" & OUTPUT_FOLDER_NAME

    ' 出力フォルダが存在しない場合は処理を中断
    If Not FolderExists(resize_settings.output_folder_path) Then
        error_message = "出力フォルダが見つかりませんでした。フォルダ名を確認してください。" & vbCrLf & _
            resize_settings.output_folder_path
        Exit Function
    End If

ここでは、ブックの保存場所を基準にして入出力フォルダのパスを組み立て、それぞれの存在を確認しています。

ThisWorkbook.Path はブックが保存されているフォルダのパスを返します。未保存のブックでは空文字になるため、最初にチェックして早期リターンします。

入出力フォルダはブックと同じ場所に固定フォルダ名で配置する設計であり、フォルダが存在しない場合はパス付きのエラーメッセージを設定して処理を中断します。

ポテ

ここは堅牢な設計にしています。

シートから最大幅・最大高さを読み取る
    ' 設定値の取得
    '--------------------------------------------------------------------------
    ' シートから最大幅を読み込む
    If Not TryReadPositiveLongFromCell( _
        ThisWorkbook.Worksheets(SETTING_SHEET_INDEX), _
        MAX_WIDTH_ROW, _
        SETTING_VALUE_COLUMN, _
        "最大幅", _
        resize_settings.max_width, _
        error_message _
    ) Then
        Exit Function
    End If

    ' シートから最大高さを読み込む
    If Not TryReadPositiveLongFromCell( _
        ThisWorkbook.Worksheets(SETTING_SHEET_INDEX), _
        MAX_HEIGHT_ROW, _
        SETTING_VALUE_COLUMN, _
        "最大高さ", _
        resize_settings.max_height, _
        error_message _
    ) Then
        Exit Function
    End If

    TryBuildResizeSettings = True

ここでは、シートの指定セルから最大幅と最大高さを読み取っています。

TryReadPositiveLongFromCell はセルの値が数値かつ正の整数であることを確認してから値を返します。どちらかが不正な場合はエラーメッセージを設定して False を返します。

両方の読み取りが成功した場合のみ、関数の最後で TryBuildResizeSettings = True を設定します。この TrueApp_ImageResizeRunner の処理継続の条件となります。

このステップまで到達した時点で、resize_settings のすべてのフィールドが有効な値で埋まった状態になっています。

補足・注意点

  • 設定シートはインデックス 1(最初のシート)を想定しています。シートの順番が変わると正しくないシートを参照してしまうため注意してください。
  • 最大幅・最大高さのセル位置(25行目、26行目、E列)は定数で管理されています。シートのレイアウトを変更した場合はここを合わせて修正してください。
  • 入出力フォルダのパスはブックの保存場所を基準に決まります。ブックをコピーして別の場所で実行する場合、同じフォルダ構成が必要です。

FindTargetImageFilePathsプロシージャーの解説

全体像

FindTargetImageFilePaths は、指定フォルダ内を走査して、対象拡張子(PNG・JPG・JPEG)の画像ファイルパスを Collection にまとめて返す関数です。

FileSystemObject でフォルダ内のファイルを列挙し、IsTargetImageFileName で拡張子を確認して対象のものだけを集めます。

対象ファイルが0件の場合は空の Collection が返ります。

コード全文

コード全文は、次のとおりです。

Public Function FindTargetImageFilePaths( _
    ByVal source_folder_path As String _
) As Collection

    ' Description
    '   対象フォルダ配下の画像ファイルパス一覧を集めて返す。
    '
    ' Arguments
    '   source_folder_path : 対象フォルダパス
    '
    ' Returns
    '   Collection
    '     対象画像ファイルパス一覧
    '
    ' Dependency Tree
    '   FindTargetImageFilePaths
    '     └─ IsTargetImageFileName (Util_ImagePathHelper)
    '
    ' References
    '   Microsoft Scripting Runtime

    ' フォルダ内ファイルの列挙
    '--------------------------------------------------------------------------
    ' ファイル列挙に使用する FSO を生成
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ' 対象フォルダオブジェクトを取得
    Dim source_folder As Folder
    Set source_folder = fso.GetFolder(source_folder_path)

    ' 対象画像ファイルパスの格納用コレクションを初期化
    Dim image_file_path_coll As Collection
    Set image_file_path_coll = New Collection

    ' フォルダ内の各ファイルを順番に確認
    Dim target_file As File
    For Each target_file In source_folder.Files
        ' 対象拡張子の画像だけを集約
        If IsTargetImageFileName(target_file.Name) Then
            image_file_path_coll.Add target_file.Path
        End If
    Next target_file

    Set FindTargetImageFilePaths = image_file_path_coll

End Function

ステップごとの解説

FSO でフォルダを開き、格納先を準備する
    ' フォルダ内ファイルの列挙
    '--------------------------------------------------------------------------
    ' ファイル列挙に使用する FSO を生成
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ' 対象フォルダオブジェクトを取得
    Dim source_folder As Folder
    Set source_folder = fso.GetFolder(source_folder_path)

    ' 対象画像ファイルパスの格納用コレクションを初期化
    Dim image_file_path_coll As Collection
    Set image_file_path_coll = New Collection

ここでは、FileSystemObject を生成してフォルダを開き、結果を格納するための空の Collection を用意しています。

fso.GetFolder はフォルダパスを受け取って Folder オブジェクトを返します。Folder オブジェクトを持つことで、フォルダ内のファイルを For Each で1件ずつ取り出せるようになります。

Collection は VBA で複数の値をまとめて保持する入れ物です。配列と異なり、件数があらかじめ決まらない場合でも Add で追加できるため、列挙結果の格納に適しています。

フォルダ内のファイルを走査して対象だけを集める
    ' フォルダ内の各ファイルを順番に確認
    Dim target_file As File
    For Each target_file In source_folder.Files
        ' 対象拡張子の画像だけを集約
        If IsTargetImageFileName(target_file.Name) Then
            image_file_path_coll.Add target_file.Path
        End If
    Next target_file

    Set FindTargetImageFilePaths = image_file_path_coll

ここでは、フォルダ内の全ファイルを For Each で1件ずつ取り出し、IsTargetImageFileName で拡張子を確認しています。

対象(PNG・JPG・JPEG)のファイルのみ Collection に追加します。

ループが終わったら Set FindTargetImageFilePaths = image_file_path_coll でコレクションを戻り値として返します。

Set が必要なのはオブジェクト型を返すためです。

このコレクションが App_ImageResizeRunner のループ処理の入力になります。

補足・注意点

  • サブフォルダ内のファイルは対象外です。source_folder.Files はフォルダ直下のファイルのみを返すため、フォルダの中にさらにフォルダがあっても再帰的に走査しません。
  • fso.GetFolder はフォルダが存在しない場合にエラーになります。この関数が呼ばれる前に TryBuildResizeSettings でフォルダの存在確認が済んでいるため、通常は問題ありませんが、直接呼び出す場合は事前確認が必要です。

TryLoadImageSizeプロシージャーの解説

全体像

TryLoadImageSize は、画像ファイルを読み込んで幅と高さを取得して返す関数です。

WIA(Windows Image Acquisition Library)の ImageFile オブジェクトを使ってファイルを読み込み、WidthHeight プロパティからサイズを取得します。

読み込みに失敗した場合は On Error GoTo でエラーを捕捉し、エラーメッセージを返します。

コード全文

コード全文は、次のとおりです。

Public Function TryLoadImageSize( _
    ByVal image_file_path As String, _
    ByRef image_width As Long, _
    ByRef image_height As Long, _
    ByRef error_message As String _
) As Boolean

    ' Description
    '   画像ファイルを読み込み、幅と高さを取得する。
    '
    ' Arguments
    '   image_file_path : 画像ファイルパス
    '   image_width     : 画像幅
    '   image_height    : 画像高さ
    '   error_message   : エラーメッセージ
    '
    ' Returns
    '   Boolean
    '     True  : 読み込み成功
    '     False : 読み込み失敗
    '
    ' Dependency Tree
    '   TryLoadImageSize
    '     (none)
    '
    ' References
    '   Microsoft Windows Image Acquisition Library v2.0

    On Error GoTo LoadError

    ' 画像サイズの取得
    '--------------------------------------------------------------------------
    ' WIA の ImageFile で入力画像を読み込む
    Dim image_object As ImageFile
    Set image_object = New ImageFile
    image_object.LoadFile image_file_path

    ' 読み込んだ画像の幅と高さを返却値へ設定
    image_width = image_object.Width
    image_height = image_object.Height

    TryLoadImageSize = True
    Exit Function

LoadError:
    error_message = "画像サイズの取得に失敗しました。" & vbCrLf & image_file_path
End Function

ステップごとの解説

WIA で画像を読み込んでサイズを取得する
    On Error GoTo LoadError

    ' 画像サイズの取得
    '--------------------------------------------------------------------------
    ' WIA の ImageFile で入力画像を読み込む
    Dim image_object As ImageFile
    Set image_object = New ImageFile
    image_object.LoadFile image_file_path

    ' 読み込んだ画像の幅と高さを返却値へ設定
    image_width = image_object.Width
    image_height = image_object.Height

    TryLoadImageSize = True
    Exit Function

LoadError:
    error_message = "画像サイズの取得に失敗しました。" & vbCrLf & image_file_path

ここでは、WIA の ImageFile オブジェクトで画像ファイルを読み込み、幅と高さを取得しています。

On Error GoTo LoadError でエラーハンドリングを設定しており、LoadFileWidthHeight の取得で問題が起きた場合は LoadError ラベルへジャンプします。

正常に取得できた場合は TryLoadImageSize = True を設定し、Exit Function でエラーラベルをスキップして終了します。

失敗した場合は LoadError: 以降の行でエラーメッセージを書き込み、関数が False(初期値)のまま戻ります。

補足・注意点

  • WIA は Microsoft Windows Image Acquisition Library v2.0 の参照設定が必要です。VBA エディタの「ツール → 参照設定」から追加してください。
  • On Error GoTo はエラーが起きた行から指定ラベルへジャンプする構文です。ここでは読み込み失敗をユーザーへ通知するためのメッセージ設定に使っています。
  • WIA の対応フォーマットに含まれていない形式のファイルを渡した場合も LoadError に飛びます。

SaveResizedImageFileプロシージャーの解説

全体像

SaveResizedImageFile は、画像ファイルを指定サイズへリサイズして出力先パスへ保存する関数です。

WIA の ImageFile で画像を読み込み、ImageProcessScale フィルタでリサイズし、指定パスへ保存します。

保存に失敗した場合は On Error GoTo でエラーを捕捉し、エラーメッセージを返します。

コード全文

コード全文は、次のとおりです。

Public Function SaveResizedImageFile( _
    ByVal source_file_path As String, _
    ByVal output_file_path As String, _
    ByVal resized_width As Long, _
    ByVal resized_height As Long, _
    ByRef error_message As String _
) As Boolean

    ' Description
    '   画像ファイルを読み込み、指定サイズへリサイズして保存する。
    '
    ' Arguments
    '   source_file_path : 入力画像ファイルパス
    '   output_file_path : 出力画像ファイルパス
    '   resized_width    : リサイズ後の幅
    '   resized_height   : リサイズ後の高さ
    '   error_message    : エラーメッセージ
    '
    ' Returns
    '   Boolean
    '     True  : 保存成功
    '     False : 保存失敗
    '
    ' Dependency Tree
    '   SaveResizedImageFile
    '     (none)
    '
    ' References
    '   Microsoft Windows Image Acquisition Library v2.0

    On Error GoTo SaveError

    ' 画像の読み込み
    '--------------------------------------------------------------------------
    ' 入力画像を WIA で読み込む
    Dim image_object As ImageFile
    Set image_object = New ImageFile
    image_object.LoadFile source_file_path

    ' リサイズ処理の適用
    '--------------------------------------------------------------------------
    ' Scale フィルタを追加して最大幅・最大高さを設定
    Dim image_processor As ImageProcess
    Set image_processor = New ImageProcess
    image_processor.Filters.Add image_processor.FilterInfos("Scale").FilterID
    image_processor.Filters(1).Properties("MaximumWidth").Value = resized_width
    image_processor.Filters(1).Properties("MaximumHeight").Value = resized_height

    ' フィルタ適用後の画像へ差し替える
    Set image_object = image_processor.Apply(image_object)

    ' リサイズ後ファイルの保存
    '--------------------------------------------------------------------------
    ' 出力先パスへ画像を保存
    image_object.SaveFile output_file_path

    SaveResizedImageFile = True
    Exit Function

SaveError:
    error_message = "画像ファイルの保存に失敗しました。" & vbCrLf & output_file_path
End Function

ステップごとの解説

入力画像を読み込む
    On Error GoTo SaveError

    ' 画像の読み込み
    '--------------------------------------------------------------------------
    ' 入力画像を WIA で読み込む
    Dim image_object As ImageFile
    Set image_object = New ImageFile
    image_object.LoadFile source_file_path

ここでは、保存処理全体のエラーハンドリングを設定してから、入力画像ファイルを WIA の ImageFile オブジェクトに読み込んでいます。

このステップで読み込んだ image_object に後続のリサイズフィルタを適用します。

Scale フィルタを設定してリサイズを適用する
    ' リサイズ処理の適用
    '--------------------------------------------------------------------------
    ' Scale フィルタを追加して最大幅・最大高さを設定
    Dim image_processor As ImageProcess
    Set image_processor = New ImageProcess
    image_processor.Filters.Add image_processor.FilterInfos("Scale").FilterID
    image_processor.Filters(1).Properties("MaximumWidth").Value = resized_width
    image_processor.Filters(1).Properties("MaximumHeight").Value = resized_height

    ' フィルタ適用後の画像へ差し替える
    Set image_object = image_processor.Apply(image_object)

ここでは、WIA の ImageProcess を使ってリサイズフィルタを設定し、画像に適用しています。

ImageProcess はWIAが提供する画像加工の仕組みで、Filters に処理内容を追加して Apply で実行します。

Scale フィルタは MaximumWidthMaximumHeight を指定することで、その範囲に収まるようにリサイズします。

image_processor.Apply の戻り値が処理後の画像なので、Set image_object = で元の変数に上書きします。

リサイズ後の画像を保存する
    ' リサイズ後ファイルの保存
    '--------------------------------------------------------------------------
    ' 出力先パスへ画像を保存
    image_object.SaveFile output_file_path

    SaveResizedImageFile = True
    Exit Function

SaveError:
    error_message = "画像ファイルの保存に失敗しました。" & vbCrLf & output_file_path

ここでは、リサイズ済みの画像を出力先パスへ保存しています。

SaveFile にパスを渡すと、そのパスへ画像ファイルが書き出されます。

保存が成功したら SaveResizedImageFile = True を設定し、Exit Function でエラーラベルをスキップして終了します。

失敗した場合は SaveError: ラベルでメッセージを設定し、False のまま戻ります。

補足・注意点

  • WIA の Scale フィルタは MaximumWidthMaximumHeight の範囲に収まるようにリサイズします。縦横比は WIA 側で自動的に維持されます。
  • image_processor.Apply に渡した後、元の image_objectSet で上書きすることに注意してください。上書きしないと未処理の元画像が保存されてしまいます。

ImageFileExistsプロシージャーの解説

全体像

ImageFileExists は、指定されたパスにファイルが存在するかどうかを返す関数です。

FileSystemObjectFileExists を呼ぶだけのシンプルな関数です。

コード全文

コード全文は、次のとおりです。

Public Function ImageFileExists(ByVal file_path As String) As Boolean
    ' Description
    '   指定ファイルが存在するかどうかを返す。
    '
    ' Arguments
    '   file_path : 判定対象ファイルパス
    '
    ' Returns
    '   Boolean
    '     True  : 存在する
    '     False : 存在しない
    '
    ' Dependency Tree
    '   ImageFileExists
    '     (none)
    '
    ' References
    '   Microsoft Scripting Runtime

    ' ファイル存在判定
    '--------------------------------------------------------------------------
    ' FileSystemObject でファイル有無を判定
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ImageFileExists = fso.FileExists(file_path)

End Function

ステップごとの解説

FSO でファイルの存在を確認する
    ' ファイル存在判定
    '--------------------------------------------------------------------------
    ' FileSystemObject でファイル有無を判定
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ImageFileExists = fso.FileExists(file_path)

ここでは、FileSystemObjectFileExists メソッドを使って指定パスにファイルが存在するかどうかを確認しています。

True(存在する)または False(存在しない)がそのまま関数の戻り値になります。

この結果が App_ImageResizeRunner でのスキップ判定に使われます。

FolderExistsプロシージャーの解説

全体像

FolderExists は、指定されたパスにフォルダが存在するかどうかを返す関数です。

TryBuildResizeSettings から入出力フォルダの確認に使われます。

コード全文

コード全文は、次のとおりです。

Public Function FolderExists(ByVal folder_path As String) As Boolean
    ' Description
    '   指定フォルダが存在するかどうかを返す。
    '
    ' Arguments
    '   folder_path : 判定対象フォルダパス
    '
    ' Returns
    '   Boolean
    '     True  : 存在する
    '     False : 存在しない
    '
    ' Dependency Tree
    '   FolderExists
    '     (none)
    '
    ' References
    '   Microsoft Scripting Runtime

    ' フォルダ存在判定
    '--------------------------------------------------------------------------
    ' FileSystemObject でフォルダ有無を判定
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    FolderExists = fso.FolderExists(folder_path)

End Function

ステップごとの解説

FSO でフォルダの存在を確認する
    ' フォルダ存在判定
    '--------------------------------------------------------------------------
    ' FileSystemObject でフォルダ有無を判定
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    FolderExists = fso.FolderExists(folder_path)

ここでは、FileSystemObjectFolderExists メソッドを使って指定パスにフォルダが存在するかどうかを確認しています。

True(存在する)または False(存在しない)がそのまま関数の戻り値になります。

TryBuildResizeSettings での入出力フォルダ確認に使われます。

TryReadPositiveLongFromCellプロシージャーの解説

全体像

TryReadPositiveLongFromCell は、指定したシートのセルから正の整数値を読み取る Private 関数です。

セルの値が数値かどうか、0より大きいかどうかを確認し、問題があればエラーメッセージを設定して False を返します。

このモジュール内の TryBuildResizeSettings からのみ呼ばれます。

コード全文

コード全文は、次のとおりです。

Private Function TryReadPositiveLongFromCell( _
    ByVal target_ws As Worksheet, _
    ByVal target_row As Long, _
    ByVal target_col As Long, _
    ByVal item_name As String, _
    ByRef output_value As Long, _
    ByRef error_message As String _
) As Boolean

    ' Description
    '   指定セルから正の整数値を読み込み、設定値として返す。
    '
    ' Arguments
    '   target_ws      : 対象シート
    '   target_row     : 対象行
    '   target_col     : 対象列
    '   item_name      : 項目名
    '   output_value   : 読み込み結果
    '   error_message  : エラーメッセージ
    '
    ' Returns
    '   Boolean
    '     True  : 読み込み成功
    '     False : 読み込み失敗
    '
    ' Dependency Tree
    '   TryReadPositiveLongFromCell
    '     (none)

    ' セル値の妥当性確認
    '--------------------------------------------------------------------------
    ' 設定シートから対象セルの値を取得
    Dim cell_value As Variant
    cell_value = target_ws.Cells(target_row, target_col).Value

    ' 数値でない場合は設定不正として扱う
    If Not IsNumeric(cell_value) Then
        error_message = item_name & " の設定値が数値ではありません。シートの設定値を確認してください。"
        Exit Function
    End If

    ' 正の整数として扱える値なら Long へ変換
    output_value = CLng(cell_value)

    ' 0 以下の値は設定不正として扱う
    If output_value <= 0 Then
        error_message = item_name & " の設定値は 1 以上で入力してください。"
        Exit Function
    End If

    TryReadPositiveLongFromCell = True

End Function

ステップごとの解説

セルの値を取得して数値かどうか確認する
    ' セル値の妥当性確認
    '--------------------------------------------------------------------------
    ' 設定シートから対象セルの値を取得
    Dim cell_value As Variant
    cell_value = target_ws.Cells(target_row, target_col).Value

    ' 数値でない場合は設定不正として扱う
    If Not IsNumeric(cell_value) Then
        error_message = item_name & " の設定値が数値ではありません。シートの設定値を確認してください。"
        Exit Function
    End If

ここでは、対象シートの指定セルから値を Variant 型で取得し、IsNumeric で数値かどうかを確認しています。

セルには文字列や空白が入ることもあるため、Variant で受けてから型確認をする設計です。

数値でない場合は項目名(item_name)を含むエラーメッセージを設定して処理を抜けます。

item_name が「最大幅」「最大高さ」のように渡されるため、どの設定値で問題が起きたかがメッセージからわかります。

正の整数に変換して値を確定する
    ' 正の整数として扱える値なら Long へ変換
    output_value = CLng(cell_value)

    ' 0 以下の値は設定不正として扱う
    If output_value <= 0 Then
        error_message = item_name & " の設定値は 1 以上で入力してください。"
        Exit Function
    End If

    TryReadPositiveLongFromCell = True

ここでは、数値であることが確認できた値を CLngLong 型の整数に変換し、0 以下でないことを確認しています。

0 以下の幅・高さはリサイズ処理で意味を成さないため、ここで弾きます。

両チェックが通れば TryReadPositiveLongFromCell = True を設定して成功を返します。

output_valueByRef のため、呼び出し元の resize_settings.max_widthresize_settings.max_height に直接書き込まれます。

補足・注意点

  • CLng は小数値を四捨五入して整数に変換します。セルに 1200.5 が入っていれば 1201 になります。
  • このプロシージャーは Private のため、同モジュール内の TryBuildResizeSettings からのみ呼ばれます。他のモジュールから直接呼ぶことはできません。

Util_ImagePathHelperモジュールの解説

全体像

Util_ImagePathHelper は、画像ファイルの名前とパスに関する操作を担うユーティリティモジュールです。

このモジュールは3つのプロシージャーを持ちます。

IsTargetImageFileName はファイルが対象の画像拡張子かどうかを判定し、BuildResizedFilePath は入力パスと出力フォルダから保存先パスを組み立てます。

どちらも内部で GetLowerCaseExtension(拡張子の小文字化)を使っています。

ファイルの存在確認や読み書きは行わず、文字列としてのファイル名とパスの操作に特化しています。

Microsoft Scripting RuntimeFileSystemObject を参照設定として使用しています。

含まれるプロシージャー

このモジュールに含まれるプロシージャーは、次のとおりです。

  • IsTargetImageFileName(Public Function)— ファイルが対象拡張子かどうかを判定する
  • BuildResizedFilePath(Public Function)— リサイズ後の出力ファイルパスを組み立てる
  • GetLowerCaseExtension(Private Function)— 拡張子を小文字で返す

IsTargetImageFileNameInf_ImageFileAccessor から、BuildResizedFilePathApp_ImageResizeRunner からそれぞれ呼ばれます。

GetLowerCaseExtension はこのモジュール内でのみ使われる内部ヘルパーです。

IsTargetImageFileNameプロシージャーの解説

全体像

IsTargetImageFileName は、ファイル名の拡張子を見てリサイズ対象の画像ファイルかどうかを判定する関数です。

対象拡張子は pngjpgjpeg の3種類です。

拡張子の比較は小文字化してから行うため、大文字小文字の違いによる見落としが起きない設計になっています。

コード全文

コード全文は、次のとおりです。

Public Function IsTargetImageFileName(ByVal file_name As String) As Boolean
    ' Description
    '   指定ファイルが画像リサイズ対象の拡張子かどうかを返す。
    '
    ' Arguments
    '   file_name : 判定対象ファイル名
    '
    ' Returns
    '   Boolean
    '     True  : 対象画像ファイル
    '     False : 対象外ファイル
    '
    ' Dependency Tree
    '   IsTargetImageFileName
    '     └─ GetLowerCaseExtension (Util_ImagePathHelper)
    '
    ' References
    '   Microsoft Scripting Runtime

    ' 対象拡張子の判定
    '--------------------------------------------------------------------------
    ' 比較前に拡張子を小文字化する関数を適用
    Dim extension_name As String
    extension_name = GetLowerCaseExtension(file_name)

    ' 対象拡張子なら True を返す
    Select Case extension_name
        Case "png", "jpg", "jpeg"
            IsTargetImageFileName = True
    End Select

End Function

ステップごとの解説

拡張子を小文字で取得する
    ' 対象拡張子の判定
    '--------------------------------------------------------------------------
    ' 比較前に拡張子を小文字化する関数を適用
    Dim extension_name As String
    extension_name = GetLowerCaseExtension(file_name)

ここでは、ファイル名から拡張子を取り出して小文字化した文字列を取得しています。

GetLowerCaseExtensionFileSystemObjectGetExtensionName で拡張子を取り出し、LCase$ で小文字に変換して返します。

拡張子を小文字化してから比較することで、PNGPngpng のようなケースの違いを吸収しています。

このステップで取得した拡張子を次の判定で使います。

対象拡張子かどうかを判定する
    ' 対象拡張子なら True を返す
    Select Case extension_name
        Case "png", "jpg", "jpeg"
            IsTargetImageFileName = True
    End Select

ここでは、取得した拡張子が pngjpgjpeg のいずれかに一致するかどうかを Select Case で確認しています。

一致した場合のみ True を関数の戻り値に設定します。

Select Case で複数の値をカンマ区切りで並べられるため、拡張子が増えた場合も Case の行に追記するだけで対応できます。

一致しない場合は IsTargetImageFileName が初期値の False のままになり、関数が False を返します。

この判定結果が Inf_ImageFileAccessorFindTargetImageFilePaths でファイル一覧の絞り込みに使われます。

補足・注意点

  • 拡張子のない無拡張ファイルを渡した場合、GetLowerCaseExtension が空文字を返すため False になります。

BuildResizedFilePathプロシージャーの解説

全体像

BuildResizedFilePath は、入力画像のファイルパスと出力フォルダパスを受け取り、リサイズ後の保存先ファイルパスを組み立てて返す関数です。

出力ファイル名は元のファイル名に _リサイズ済み というサフィックスを付けた形になります。

たとえば入力が photo.jpg であれば、出力は photo_リサイズ済み.jpg となります。

コード全文

コード全文は、次のとおりです。

Public Function BuildResizedFilePath( _
    ByVal source_file_path As String, _
    ByVal output_folder_path As String _
) As String
    ' Description
    '   入力画像ファイルパスと出力フォルダパスから、
    '   リサイズ後の出力ファイルパスを組み立てる。
    '
    ' Arguments
    '   source_file_path   : 入力画像ファイルパス
    '   output_folder_path : 出力フォルダパス
    '
    ' Returns
    '   String
    '     リサイズ後の出力ファイルパス
    '
    ' Dependency Tree
    '   BuildResizedFilePath
    '     (none)
    '
    ' References
    '   Microsoft Scripting Runtime

    ' 出力ファイルパスの組み立て
    '--------------------------------------------------------------------------
    ' パス操作に使用する FSO を生成
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ' 元画像の拡張子なしファイル名を取得
    Dim base_name As String
    base_name = fso.GetBaseName(source_file_path)

    ' 出力時に利用する拡張子を取得
    Dim extension_name As String
    extension_name = fso.GetExtensionName(source_file_path)

    ' 出力フォルダ配下の保存先フルパスのファイル名を返す
    BuildResizedFilePath = output_folder_path & "\" & base_name & "_リサイズ済み." & extension_name

End Function

ステップごとの解説

FSO を準備してファイル名と拡張子を取得する
    ' 出力ファイルパスの組み立て
    '--------------------------------------------------------------------------
    ' パス操作に使用する FSO を生成
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ' 元画像の拡張子なしファイル名を取得
    Dim base_name As String
    base_name = fso.GetBaseName(source_file_path)

    ' 出力時に利用する拡張子を取得
    Dim extension_name As String
    extension_name = fso.GetExtensionName(source_file_path)

ここでは、パス操作のために FileSystemObject(FSO)を生成し、元画像のファイル名と拡張子をそれぞれ取得しています。

Set fso = New Scripting.FileSystemObject で FSO のインスタンスを作成します。

Set は VBA でオブジェクト型の変数に代入するときに必要なキーワードです。

GetBaseName は拡張子を除いたファイル名を返します。

たとえば C:\images\photo.jpg に対して photo が返ります。

GetExtensionName は拡張子部分(ドットなし)を返し、jpg が得られます。

この2つを組み合わせて出力ファイル名を作ります。

出力先の完全パスを組み立てて返す
    ' 出力フォルダ配下の保存先フルパスのファイル名を返す
    BuildResizedFilePath = output_folder_path & "\" & base_name & "_リサイズ済み." & extension_name

ここでは、出力フォルダパス・ファイル名・サフィックス・拡張子を文字列連結して完全なファイルパスを作り、関数の戻り値として返しています。

たとえば output_folder_pathC:\outputbase_namephotoextension_namejpg の場合、C:\output\photo_リサイズ済み.jpg が返ります。

このパスが App_ImageResizeRunner で出力先の確認と保存に使われます。

補足・注意点

  • 出力ファイル名のサフィックス _リサイズ済み はこの行にハードコードされています。命名ルールを変更したい場合はここを編集します。
  • GetExtensionName は元のファイルの拡張子をそのまま使うため、入力が photo.JPG であれば出力は photo_リサイズ済み.JPG になります。拡張子の大文字小文字は引き継がれます。
  • output_folder_path の末尾に \ がある場合、パスが C:\output\\photo_リサイズ済み.jpg のように二重スラッシュにってしまいます。このパスは、呼び出し元の TryBuildResizeSettings で OUTPUT_FOLDER_NAME を使って組み立てるため、OUTPUT_FOLDER_NAME には末尾の \ を含めない前提になっています。

GetLowerCaseExtensionプロシージャーの解説

全体像

GetLowerCaseExtension は、ファイル名から拡張子を取り出して小文字で返す関数です。

FileSystemObjectGetExtensionName で拡張子を取り出し、LCase$ で小文字に変換し、Trim$ で前後の空白を除去して返します。

Private 関数のため、このモジュール内の IsTargetImageFileName からのみ呼ばれます。

コード全文

コード全文は、次のとおりです。

Private Function GetLowerCaseExtension(ByVal file_name As String) As String
    ' Description
    '   ファイル名から拡張子を小文字で返す。
    '
    ' Arguments
    '   file_name : 対象ファイル名
    '
    ' Returns
    '   String
    '     小文字化された拡張子
    '
    ' Dependency Tree
    '   GetLowerCaseExtension
    '     (none)
    '
    ' References
    '   Microsoft Scripting Runtime

    ' 拡張子の抽出
    '--------------------------------------------------------------------------
    ' 拡張子取得に使用する FSO を生成
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ' 比較しやすいように小文字・前後空白除去で返す
    GetLowerCaseExtension = LCase$(Trim$(fso.GetExtensionName(file_name)))

End Function

ステップごとの解説

拡張子を取り出して小文字化する
    ' 拡張子の抽出
    '--------------------------------------------------------------------------
    ' 拡張子取得に使用する FSO を生成
    Dim fso As Scripting.FileSystemObject
    Set fso = New Scripting.FileSystemObject

    ' 比較しやすいように小文字・前後空白除去で返す
    GetLowerCaseExtension = LCase$(Trim$(fso.GetExtensionName(file_name)))

ここでは、FSO を使ってファイル名から拡張子を取得し、小文字化と前後空白除去を施して返しています。

fso.GetExtensionName(file_name) で拡張子文字列を取り出し、Trim$ で前後の余分な空白を除去してから、LCase$ で小文字に変換します。

処理はネストした形で1行に収まっており、内側から順に GetExtensionName → Trim$ → LCase$ と適用されます。

この関数の結果が IsTargetImageFileName の拡張子判定に使われます。

補足・注意点

  • LCase$Trim$ の末尾 $ は、戻り値を Variant ではなく String 型として明示するための記法です。余分な型変換を避けられます。
  • GetLowerCaseExtensionIsTargetImageFileName の中でのみ使われています。BuildResizedFilePath では GetExtensionName を直接使っており、こちらは拡張子の大文字小文字を維持したまま出力ファイル名に使います。
ポテ

以上で解説は終了です。

来歴

バージョン作成日
v1.1.02026-03-16

VBAスキルアップの参考情報

近年は、ChatGPTをはじめとするAIの登場によって、学習のスタイルが大きく変わりました。

分からないことがあれば、AIに尋ねれば答えがすぐに見つかる時代です。

とはいえ、AIを使いこなすには、自分自身の基本的な知識や理解力が欠かせません。

全体像をつかむためには、やはり書籍などで体系的に学んでおくことが今でも有効です。

そのうえでAIを活用すれば、自分の理解度に合わせた的確な解説や、応用のヒントを得ることができます。

「学んで基礎を築く → AIで補い発展させる」──このサイクルを重ねることで、VBAスキルは着実に高まっていくでしょう。

VBAのスキルアップ

VBAを学び始めるなら

入門書は、どれを選んでも大きな差はないように感じます。

どれを選ぶかに悩むことに時間をかけるよりも、まずは手頃な一冊を手に取って進めてみるのがおすすめです。

もし迷ったときには、私はインプレス社の「いちばんやさしい」シリーズを選ぶことが多いです。

基礎を超えて力をつけたいなら

私は上級者を目指していましたので、入門書にとどまらず、このような内容の濃い一冊を選んで学んでいました。

今は誰でもAIを活用できる時代になりましたが、上級者を目指す方にとっては、AIをより上手に活用するという意味でも、こうした本は今なお価値があります。

このレベルの本を一冊持っておくことに、損はないでしょう。


資格で能力を証明したいなら

VBAのプログラミング能力を客観的に示したい場合には「VBAエキスパート試験」があります。

特に「スタンダード」の方は上級者向けです。

あなたが社内業務の改善を行う立場であっても、VBAで作成したシステムをお客様に納める立場であっても、この資格は信頼や安心につながるでしょう。

以下の公式テキストが販売されています。



プログラミングの一般教養

「独学プログラマー」というプログラミングの魅力を解説した書籍があります。

これはVBAではなくPythonを題材としていますが、プログラミングの基本的な知識や思考法、仕事の進め方まで幅広く学べます。

今はAIにコードを尋ねれば、答えが返ってくる時代です。

しかし、この本からは「コード」以上に、プログラミングに向き合う姿勢や考え方を学ぶことができるでしょう。


こちらの記事でも紹介しています。もしよろしければご覧ください。

【初心者歓迎】無料相談受付中 

運営者・ポテ

いつもありがとうございます!

限られた時間をより良く使い、日本の生産性を高めたい──

みんなの実用学を運営するソフトデザイン工房では、業務整理や業務改善アプリケーション作成のご相談を承っております。

お気軽にご相談ください。


こちらの記事でも紹介しております。

おわりに

ポテ

以上で解説は終了です。

この記事では、複数の画像ファイルを一括リサイズするアプリについて、改訂版としてコード構成や処理の流れを整理した内容を紹介しました。

お問い合わせやご要望がございましたら、「お問い合わせ/ご要望」フォームまたはコメント欄よりお知らせください。

この記事が皆様のお役に立てれば幸いです。

なお、当サイトでは様々な情報を発信しております。よろしければトップページもあわせてご覧ください。

この記事を書いた人

ポテ
ポテソフトデザイン工房|日々の業務にちょうどいい自動化を
■人生を追求する凡人 ■日本一安全で、気の向くままに自分の時間を過ごせる、こだわりのキャンプ場を作るのが夢 ■ソフトデザイン工房運営(個人事業者) - 業務改善アプリケーションをご提供 ■人生は時間そのもの。ひとりでも多くの人が「より良い人生にするために時間を使って欲しい」と願い、仕事のスキルの向上、余暇の充実、資産形成を追求。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です