Excel作業をVBAで効率化 https://vbabeginner.net いつものExcel作業はVBAを使えば数秒で終わるかもしれませんよ Sat, 24 May 2025 12:50:25 +0000 ja hourly 1 https://wordpress.org/?v=6.6.2 https://vbabeginner.net/wp-content/uploads/2019/02/favicon-150x150.png Excel作業をVBAで効率化 https://vbabeginner.net 32 32 指定列の10行移動平均を計算して隣の列に出力するVBA https://vbabeginner.net/vba-moving-average-excel/ Sat, 24 May 2025 12:50:25 +0000 https://vbabeginner.net/?p=7571 VBAで10行ごとの移動平均を計算する(数式は面倒・・)

Excelでデータ分析や数値の扱う際に、「過去10行の平均を毎行ごとに出したい」というケースがあります。

FXや株式取引でのテクニカル分析で扱う「移動平均線」は有名です。他にも売上やアクセス数、温度や計測値など、一定期間の推移をなめらかに見るためには移動平均がとても有効です。

ただ、Excel関数で1行ずつ手動で数式を入れるのは手間がかかりますし、対象データが増えると面倒です。

そこでこの記事では、VBAを使って、指定した列のデータに対して10行ごとの平均を自動計算して隣の列に出力する方法をご紹介します。コードを編集すれば10日間を20日間などに変更することが可能です。

マクロ概要

対象列のデータを1行ずつ読みながら、その行を含めた直近10行の平均を計算します。

計算結果を隣の列に自動で書き出します。

シートのデータが空になるまで繰り返し実行します。

コード

Sub SetMovingAverage()
    Dim r               As Range    '// 処理対象セル
    Dim averageRange    As Range    '// 移動平均セル範囲
    Dim sum             As Double   '// セル範囲の合計値
    Dim cell            As Range    '// セル範囲のループ中セル
    Dim numRows         As Long     '// 移動平均を計算する範囲の行数
    Dim targetCol       As String   '// 計算対象列
    Dim outputColOffset As Long     '// 計算結果出力列(計算対象列からの何列右か)

    '// 設定
    numRows = 10            '// 行数(10であれば10行分での移動平均を求める)
    targetCol = "G"         '// 列
    outputColOffset = 2     '// targetColからどれだけ右の列に出力するか(G列で2列右であればI列に出力する)

    '// 基準セルを指定
    Set r = Range(targetCol & "11")
    
    '//
    Do
        '// セルに値がなければループを抜ける
        If r.Value <> "" Then
            Exit Do
        End If
        
        '// 基準セルからn行前までのセル範囲をセット
        Set averageRange = Range(r, r.Offset(-(numRows - 1), 0))
        
        '// 合計値を初期化
        sum = 0

        '// n行分のセル値の合計を計算
        For Each cell In averageRange
            sum = sum + cell.Value
        Next
        
        '// 計算結果出力列に移動平均(セル値合計÷行数)を出力
        r.Offset(0, outputColOffset).Value = sum / numRows
        
        '// 次のセルに対象セルを変更
        Set r = r.Offset(1, 0)
    Loop
End Sub

実行方法

株価のデータを使って実行方法を説明します。

ここではA列の「日付」昇順でE列の「終値」の10日間の移動平均をG列に出したい場合とします。

上のようにシートにデータを入力します。例では罫線を付けていますが罫線は無くても構いません。

あとは、入力したシートを表示(アクティブ)状態で上のコードのSetMovingAverage関数を実行するだけです。

実行後は以下のようにE列の各10日分の平均がG列に出力されます。

注意点

平均の対象となる10行分のデータが不足している場合もそのまま計算されます。

空白セルや非数値が混ざっている場合、エラーになることがあります。

]]>
消せないThumbs.dbを100%削除する方法 https://vbabeginner.net/delete-thumbs-db/ Sat, 23 Dec 2023 05:37:06 +0000 https://vbabeginner.net/?p=6905 VBA関係ありません

このページではVBAは出てきません。WindowsのThumbs.dbを確実に消す方法を紹介しています。

頭にくるThumbs.dbが消せない問題

Thumbs.dbを消そうとしたら出てくるこのメッセージ。頭に来ます。いいから消せ、と。

「~によってファイルは開かれているため、操作を完了できません。ファイルを閉じてから再実行してください。」

この原因はエクスプローラーです。Thumbs.dbはエクスプローラーでアイコン表示をする際に利用されるファイルのため、エクスプローラーが開いているとずっと捕まえた状態になっています。

いろんなサイトで扱っていますが、確実に消せる方法じゃないものを書いてあったりしますので、ここでは本当に確実にThumbs.dbを削除する方法を紹介します。

なお、ここで紹介する方法はPCの再起動が必須になります。

Thumbs.dbを削除する手順

  1. 起動しているアプリを全部終了する。今見ているWebブラウザは起動しててOK。
  2. エクスプローラーを起動し、Thumbs.dbがあるフォルダまで移動する。
  3. エクスプローラーのアドレスバーで「cmd」を入力してEnterキーを押す。そしたらコマンドプロンプトが起動する。このコマンドプロンプトはあとで使う。

  4. エクスプローラーを閉じる。
  5. タスクマネージャを起動する。タスクマネージャはAlt + Ctrl + Deleteキーを押すと青い画面で「タスクマネージャ」があるのでそれをクリックすると起動する。
    それか、タスクバーのアプリがない部分で右クリックを押すと「タスクマネージャ」があるのでそれを選んでもOK。
  6. タスクマネージャの「プロセス」タブを下の方にスクロールするとある「Windowsプロセス」の中のエクスプローラーを右クリックして「タスクの終了」を選ぶ。PCの画面のいたるところが真っ黒になるけど、気にしないこと
  7. 事前に起動していたコマンドプロンプトで以下を入力してフォルダを削除する。ここでは「C:\thumbsdbTest」が削除対象。
    cd ../
    rd /s /q C:\thumbsdbTest

    ここで、「削除できません」みたいなメッセージが出てなければ削除されている。

    ちなみに、1行目の「cd ../」は、1つ上のフォルダに移動して、2行目の「rd /s /q フォルダパス」はrdがフォルダパスを削除するコマンドで、/sは全てのサブフォルダとファイルも削除し、/qは「削除してよろしいですか(Y/N)?」メッセージを表示しないようにする。

  8. コマンドプロンプトで「shutdown /r」を入力してEnterキーを押して、1分放置すると再起動される。
  9. これで終わり。
]]>
VBA画面で標準モジュールが開かない場合 https://vbabeginner.net/module-does-not-open/ Sat, 09 Dec 2023 06:41:07 +0000 https://vbabeginner.net/?p=6890 ダブルクリックしても標準モジュールが開かない

VBAのソースコードを見る場合、VBAの画面の左側にある標準モジュールやクラスモジュールをダブルクリックや上のコード表示ボタンを押して開きます。

ところが、ブックが壊れている場合には標準モジュールをダブルクリックしてもコードが表示されません。右クリック→エクスポートの操作でもエクスポートされません。ちゃんと操作しているのにです。他のブックは標準モジュールが開くのに、このブックだけ開きません、なんてことが起きます。

原因はマクロ部分が壊れているためのようですが、何が壊れているのかは分かりません。けどどうにか復旧させたいです。

ここでの内容は私が実際に壊れたVBAありのブックを復旧させたときのメモです。試行錯誤したときのものをまとめました。

以下が手順概要です。
1. Macで開いて別名で保存する。
2. Windowsで開いて、開けるなら標準モジュールをエクスポートする。
3. エクスポートしたファイルの文字化け部分をエディタで直す。
4. 修正した標準モジュールをインポートしなおして保存する。

ブックの状況を確認する

ブック自体が保存できるかをまず確認します。どこかのセルに「aaa」とか入力して保存をすると、以下のダイアログでブックが壊れていることが分かります。

このダイアログが出たときは、大体復旧できません。

何度か続行すると以下のダイアログが出て、xlsbファイルが保存されることがあります。

xlsbファイルにはシート部分の入力データや背景色ぐらいが残っている程度で、VBA関連は一切保存されていません。

というわけで、やはり復旧されません。

Macで開けるならMacで保存しなおすWindowsで開けるようになる(開けるけどまだ直ってない)

ただ、理由は分かりませんが、Windowsで「破損しています」と表示されるブックでも、Macで開くと普通に開けることがよくあります。そこで、Macで開き、どこかのセルに「aaa」とか入力して別名で保存します。上書き保存だと本当に壊れたときに怖いので別名にします。

この別名のブックをWindowsで開くとなぜか開けます。標準モジュールなども表示できるようになります。

これで復旧は「大体」完了です。

しかし直ってない箇所があります。それはWindows特有の文字をMacでは正しく解釈できないという問題です。

なお、Macが用意できない場合は、WindowsPCにLinuxのUbuntuをデュアルブート(1のPCに複数のOSをインストールしている状態)して、UbuntuでLibreOfficeやFreeOfficeを使うと同じような修復作業が行えるかもしれません。試してません。

Macでの保存で円マーク部分の文字コードが変わってしまう

VBAで一番多いWindowsとMac間での文字化けの影響が大きいのが円マークです。

これですね。

このWindowsのパス区切り文字の円マークですが、Macでのブック保存時に文字コードが変わってしまいます。

Windowsの円マークは10進数=92、16進数=0x5cで、Macで変わったあとは10進数で128、16進数で0x80になります。0x80は貨幣ユーロの記号として使われることがあります。Windowsでは元の円マークの0x5cはバックスラッシュと同じ文字コードのため、今回と同様にシステム開発でよく問題になります。

ただ、そのあたりはどうでもいいので、とにかく変わっちゃったものは仕方ありません。いい感じに修正するツールがあるのかもしれませんが、ここでは自力で直す方法を採ります。

Macで変換された0x80文字はVBAの画面では表示上は空文字として扱われます。実際には1バイト0x80があるのに何も入力されていないように見えます。

こんな感じです。本来はダブルクォーテーションの間に円マークがあるのですが、0x80になっているため見えません。

本来は以下が正しい状態です。ちなみに、上のようになっている箇所を行ごと一度削除して、円マークを書き直せば直ります。

標準モジュールやクラスモジュールをエクスポートしてテキストファイル化する

上の絵のように、VBAの画面ではどこに0x80があるのかを探すことが出来ないため、標準モジュールやクラスモジュールを全てエクスポートしてテキストファイルとして見れるようにします。

エクスポートはVBAの画面で標準モジュールやクラスモジュールを右クリックしてエクスポートを選択して、保存先を選べばそこにbasファイルやclsファイルが出力されます。

1ファイルずつエクスポートするのが面倒な場合は「標準モジュール等の一括エクスポート」をご参照ください。

テキストエディタで0x80を円マークに置換する

エクスポートした標準モジュールは.bas、クラスモジュールは.clsの拡張子が付いています。

これらのファイルをバイナリエディタで開きます。ここではImHexを使います。ImHexはフリーの高機能なバイナリエディタです。海外のアプリですが言語設定で日本語対応しています。

ImHexリンク:https://imhex.werwolv.net/

バイナリエディタで元の円マークがあった0x80を16進数で検索します。ImHexの場合は以下のように、左右に16進数とASCII文字が表示されます。

あとは0x80の部分を0x92(Windowsの円マーク)に置換します。

数が多くて大変な場合はテキストエディタで置換しても構いません。ここではサクラエディタを使います。サクラエディタはフリーの高機能エディタです。

サクラエディタのリンク:https://sakura-editor.github.io/

サクラエディタで開いて元々円マークだった場所を調べます。そうすると1文字の点で表示されている箇所があります。

この点の部分を円マークに置換します。

置換後はこんな感じになります。

バイナリエディタで置換しても構いません。

この置換作業をエクスポートした全てのファイルに対して行います。

編集後のbasファイルとclsファイルをインポートしなおす

Macで修復したブックを開き、VBAの画面で標準モジュールとクラスモジュールを全て解放し、サクラエディタで修正したbasファイルとclsファイルをインポートします。

全てのインポートが終わったら保存します。

これで修復完了です。

]]>
VBAでフォルダやファイルの更新日時を取得する(FileDateTime) https://vbabeginner.net/filedatetime/ Wed, 30 Aug 2023 15:33:54 +0000 https://vbabeginner.net/?p=6876 FileDateTime関数

FileDateTime関数は指定したファイルやフォルダやドライブの更新日時を返します。

この更新日時はDate型のため年月日時分秒のデータになります。ミリ秒やマイクロ秒は持っていません。

FileDateTime関数は引数のファイルやフォルダが存在しない場合にエラーになり、処理が止まるため、引数のファイル存在チェックが必須になります。また、日時を文字列に変換するFormat関数も一緒に扱った方が都合がよいです。後述のサンプルコードで説明します。

構文

Function FileDateTime(PathName As String)

PathName 更新日時を確認したいファイルやフォルダのパスを文字列で指定します。

存在しないパスの場合は実行時エラー53が発生して処理が止まります。

また、パスが不完全な場合は実行時エラー76が発生し処理が止まります。

戻り値 厳密には戻り値型は規定されていないためVariant型になりますが、実質ではDate型でPathNameで指定されたパスの更新日時が返されます。

発生するエラー

FileDateTime関数では主に2つのエラーが発生します。

1つは、FileDateTime関数の引数のパスが存在しない場合に「実行時エラー’53’ ファイルが見つかりません。」が発生します。例えば、ファイル名だけの「abc.txt」だけを引数に指定すると発生します。

もう1つは、FileDateTime関数の引数のパスにファイル名しか指定していないなどでフォルダパスが不完全な場合に「実行時エラー’76’ パスが見つかりません。」が発生します。例えば、本来は「C:\abc\test.txt」のフルパスを「abc\test.txt」のようにドライブや親フォルダが書いてないと発生します。

いずれの場合も、「C:\aaa\test.txt」のようにドライブからファイル名までの正しいフルパスが書いてないことが原因です。

ファイルの更新日時を取得するサンプル

Sub FileDateTimeTest0()
    Debug.Print FileDateTime("C:\aa.txt")
End Sub

実行結果例
2020/01/16 2:32:47

引数のファイルパスが存在していれば、そのファイルの更新日時が返されます。

フォルダやドライブの更新日時を取得するサンプル

Sub FileDateTimeTest1()
    Dim d   As Date
    d = FileDateTime("C:\abc\testあああ")
    Debug.Print d
End Sub

実行結果例
2019/12/25 0:58:10

引数のフォルダパスが存在していれば、そのフォルダの更新日時が返されます。

注意点

フォルダパス(ドライブパスも同様)を指定する場合に注意点があります。

それは、フォルダパスの右端にパス区切り文字の「\」や「/」を指定してはいけない、という点です。パス区切り文字が右端にあると「実行時エラー’53’ ファイルが見つかりません。」のエラーになります。

そのため上のサンプルコードの引数の右端に「\」があるとエラーになります。

Sub FileDateTimeTest2()
    Dim d   As Date
    d = FileDateTime("C:\abc\testあああ\")
    Debug.Print d
End Sub

引数右端に「\」があるためこれはエラーになります。

FileDateTime関数は引数存在チェックとFormat関数が必須

先に書いたとおり、FileDateTime関数は引数が存在しない場合にエラーになります。また、戻り値がDate型のため、指定フォーマットの文字列で日時を取得したい場合はFormat関数を利用することが必要になります。

以下のサンプルはそれらの問題に対応した関数です。

ファイルパスが存在するかをDir関数で判定しています。

Dir関数でのファイル存在チェックの詳細については「VBAでファイルの存在をチェックする」をご参照ください。

また、ファイルの更新日時を”yyyy-mm-dd hh:mm:ss”の文字列で取得するためにFormat関数を利用しています。

Sub GetFileTimestamp()
    Dim filePath    As String       '// ファイルパス
    Dim updDateTime As Date         '// 更新日時
    Dim s           As String       '// 更新日時文字列
    
    '// ファイルのパスを指定します
    filePath = "C:\aaa\料理\とり肉の山賊焼き.pdf" ' ファイルの実際のパスに置き換えてください
    
    '// ファイルの存在しない場合
    If Dir(filePath) = "" Then
        MsgBox "ファイルが見つかりません。"
        Exit Sub
    End If
    
    '// ファイルの更新日時を取得
    updDateTime = FileDateTime(filePath)
    
    '// 指定フォーマットで取得
    s = Format(updDateTime, "yyyy-mm-dd hh:mm:ss")
    
    Debug.Print s
End Sub

実行結果例
2021-07-31 01:38:40

On Error Resume Nextでエラーを回避する

上のコードのようにファイル存在チェックを入れてエラー回避することをお勧めしますが、「On Error Resume Next」でエラーが発生しても処理を続行するようにすることも可能です。

Sub FileDateTimeTest4()
    On Error Resume Next
    Dim d   As Variant
    d = FileDateTime("C:\aaa\料理\とり肉の山賊焼き.pdf")
    Debug.Print d
    
    If Err.Number > 0 Then
        Debug.Print Err.Description
    End If
End Sub

]]>
VBAのWorkbooks.Openでリンク更新ダイアログを消す https://vbabeginner.net/prevent-link-update-dialog-workbooks-open/ Wed, 02 Aug 2023 15:52:19 +0000 https://vbabeginner.net/?p=6867 Workbooks.OpenでApplication.DisplayAlartが効かない?

VBAを使ってブックを開いて処理する場合、Workbooks.Openメソッドを利用します。

Workbooks.Open("C:\test\abc.xlsx")

ただ、このときに以下のリンク更新ダイアログが表示されることがあります。

「このブックには、安全ではない可能性のある外部ソースへのリンクが1つ以上含まれています。リンクを信頼できる場合、リンクを更新して最新データを取り込みます。信頼できない場合は、データをそのまま手元で処理してもかまいません。」

相変わらず意味が分かりにくいメッセージです。ボタンを押すこと自体が恐怖です。これをユーザーに見せて理解できると本当にMicrosoftは思ってるのか謎です。

分かりやすく書くと、「他のブックのセルにハイパーリンク貼ってるけど、値変わってるかもしれんよ。確認して値を最新にする?」って話です。

で、このようなメッセージダイアログを出さないようにする仕組みとして「Applicationo.DisplayAlart = False」がありますが、このメッセージの場合はこの方法が効きません。

というのも、「Applicationo.DisplayAlart = False」は全てのダイアログを消すことはできず、セキュリティ上問題がある場合にはダイアログの表示が優先されてしまいます。

リンク更新ダイアログを出さない方法

では、どうやってリンク更新ダイアログの表示を回避するかというと、Workbooks.Openメソッドの引数でUpdateLinksに0を指定します。

Sub UpdateLinksZero()
    Dim wb      As Workbook                 '// ワークブック
    Dim sPath                               '// ブックファイルパス
    
    '// 開くブックを指定
    sPath = "C:\test\abc.xlsx"
    
    '// Filename:開くブック
    '// UpdateLinks:0=リンク更新しない
    '// ReadOnly:True=読み取り専用で開く
    '// IgnoreReadOnlyRecommended:True=読み取り専用推奨ダイアログを非表示
    Set wb = Workbooks.Open(Filename:=sPath, UpdateLinks:=0, ReadOnly:=True, IgnoreReadOnlyRecommended:=True)
    
    '// ブックを閉じる
    Call wb.Close
End Sub

0を指定するとリンク更新は「更新しない」扱いになります。リンク更新ダイアログで「更新しない」ボタンを押したときと同じ挙動になります。

上のコードではUpdateLinks以外にReadOnly、IgnoreReadOnlyRecommendedも引数で設定していますが、リンク更新ダイアログを非表示にしたい場合は、ブックをとにかく参照したい場合が多いため、これらの引数は大体セットで設定します。

Workbooks.Openの詳細については「VBAでExcelブックを開く方法(Workbooks.Open)」をご参照ください。

そもそもリンク更新ダイアログが出るような状態が問題

リンク更新ダイアログが出る原因は、他ブックのセルを参照していることがほとんどですが、意図せずこの状況になっていることが多々あります。

この状況になるとVBAのWorkbooks.Openメソッドだけでなく、普通にブックを開くときにも以下のセキュリティの警告が表示されます。

ハイパーリンクで他のブックのセルを参照するのは便利な機能ではありますが、ほとんどの場合は別シートにリンクを貼ったシートを、別のブックにコピーすることが原因です。

再現方法は、Book1.xlsxにSheet1とSheet2があり、Sheet2のA1セルで「=Sheet1!A1」として、そのSheet2を別ブックにコピーすると、元の「=Sheet1!A1」が「=’C:\test\[Book1.xlsx]Sheet1′!A1」のように外部ブックを指すようになります。

これが、別ブックを参照していることになってしまう原因です。

これを解消するには、Ctrl+Fキーで検索ダイアログで「.xls」を検索すると、外部ブックへのハイパーリンクが見つかります。ごくまれにですが、オートシェイプをボタンのようにハイパーリンクを貼っていることもありますので、セルの検索で見つからない場合はオートシェイプを探してみてください。

あとは見つかった外部ブックのリンクを書き換えると、リンク更新ダイアログやセキュリティ警告は出なくなります。

]]>
VBAで全シートの指定列の値を集約して出力する https://vbabeginner.net/output-specified-column-of-all-sheets/ Sat, 29 Jul 2023 14:50:25 +0000 https://vbabeginner.net/?p=6858 同じフォーマットのシートが複数ある場合のデータ確認

Excelで1つのブックの中に表形式の同じフォーマットのシートが複数あることがあります。

よくあるのは個数や金額のフォーマットや、データベースなどの項目定義のフォーマットなどが挙げられます。他にもいろんな業種で表形式のフォーマットは利用されています。

これらのフォーマットを複数のシートで利用する場合に、全シートで何が設定されているのかを確認しなければならないことがあります

よくあるのが、「入力内容がそもそも合ってるのか間違ってるのか、何が記入されているのかもよく分からない」という場合です。

こういう場合は片っ端から確認していくしかないのですが、量が多い場合や目視での確認に不安がある場合や、修正後の再確認が発生したりするとまた同じことのやり直しになってしまいます。

そこで以下では、全シートの指定列の内容を集約してイミディエイトウィンドウに出力するマクロを紹介します。

事前準備

以下のコードではDictionaryオブジェクトを利用しています。

VBA画面のツールメニュー→参照設定を選び、参照設定ダイアログで「Microsoft Scripting Runtime」にチェックを付けます。これでDictionaryが使えるようになります。

CreateObject関数を使う方法もありますが、詳細は「VBAのDictionaryの使い方(全メソッドとプロパティ網羅)」をご参照ください。

全シートの指定列の全行の値を取得して集約して出力する

以下のマクロでは、全シートの指定された列の全行の内容を取得して、同じ値であれば重複を除去してイミディエイトウィンドウに出力します。

例えば「北海道」から「沖縄」までの全都道府県の47シートがあるブックの場合、「福岡」シートのC2セルを選択している状態で以下のマクロを実行すると、47シートそれぞれのC列の全行の値を取得して重複を除去して出力します。

「福岡」シートのC2セルは「どの列を取得対象の列にするか?」という「C列」が欲しいだけで、「福岡」シートでなくても構いません。

Sub 全シートの指定列の値を集約して取得()
    Dim sht     As Worksheet        '// シート(全シートループ用)
    Dim col     As Integer          '// 基準となる列
    Dim dic     As New Dictionary   '// 同一値のみを取得するための辞書
    Dim iMaxRow As Long             '// 各シートの最終行
    Dim i       As Long             '// 行ループ用の行位置指定用ループカウンタ
    Dim s       As String           '// セルの文字列
    Dim key     As Variant          '// 辞書のキー
    
    '// 現在のアクティブシートのアクティブセルの列を基準列として取得する
    col = ActiveCell.Column
    
    '// 全シートループ
    For Each sht In Worksheets
        '// このシートの最終行を取得
        iMaxRow = sht.UsedRange.Rows.Count
        
        '// 行ループカウンタを先頭行位置の1に初期化
        i = 1
        
        '// シートの全行ループ
        Do
            '// 最終行に達した場合
            If i > iMaxRow Then
                '// 全行ループを抜ける
                Exit Do
            End If
            
            '// 現在行のセルの値を取得。列は先に取得した位置。
            s = sht.Cells(i, col).Value
            
            '// セルの値が未取得の場合
            If dic.Exists(s) = False Then
                '// 辞書に追加する
                Call dic.Add(s, s)
            End If
            
            '// 次行に設定
            i = i + 1
        Loop
    Next
    
    '// 辞書の全キーをループ
    For Each key In dic.keys
        '// イミディエイトウィンドウに出力
        Debug.Print key
    Next
End Sub

コード説明

コードの内容はコメントに大体書いてますので、何をやろうとしているのかを説明します。

変数colはColumnの略で、どの列の全行を取りたいのか、という基準となる列を設定しています。マクロを実行した際のアクティブシートのアクティブセルを対象としているため、A1セルを選択してればA列、D100セルを選択していればD列を基準としています。

ActiveCell.Columnは実際には英字のAなどではなく列位置を示す数値が変数Colには格納されます。

あとは、For Eachで全シートを表すWorksheesオブジェクトのループで1シートずつ回して、その各シートの最終行をUsedRange.Rows.Countで取得し、1行目から最終行までの基準列のセルを1つずつ取得して、辞書に格納されていなければ格納しています

UsedRangeを使った最終行の取得の詳細については「VBAで編集セル範囲の選択と最終行と最終列の取得」をご参照ください。

Dictionaryを使っているのは重複を除去するのにExistsメソッドでの判定がラクなためです。

Dictionaryの詳細については「VBAのDictionaryの使い方(全メソッドとプロパティ網羅)」をご参照ください。

実行例

以下のように都道府県のシートがあるとします。ここでは北海道と沖縄の一部の市町村を書いてます。

<北海道>

<沖縄>

ここで北海道シートのB2セルを選択して、上のマクロを実行すると、各シートのB列の内容をイミディエイトウィンドウに出力します。

]]>
VBAで指定フォルダをエクスプローラーで開く https://vbabeginner.net/open-folder-in-explorer/ Sun, 16 Jul 2023 16:29:47 +0000 https://vbabeginner.net/?p=6855 エクスプローラーで指定フォルダを開くには

VBAを実行中に、実行結果が格納されているフォルダをエクスプローラーで開きたい場合があります。VBAで新規ブックやテキストファイルを作成して、それを確認したい場合などです。

ここでは指定したフォルダをエクスプローラで開く方法を紹介します。

エクスプローラーは「C:\Windows\explorer.exe」がアプリケーション本体です。VBAのコードでもこの「explorer.exe」を利用します。

WindowsにはXP、Vista、7、8、8.1、10、11といくつかのOSバージョンがありますが、explorer.exeはいずれも基本的には同じところにあります。ただ、業務用で使うPCなどではWindowsのインストールドライブがDドライブだったりすることもあります。

もしexplorer.exeの場所が分からなければ、エクスプローラーを普通に起動してアドレス欄に「%SystemRoot%」と入力+Enterを押せばexplorer.exeの場所が分かります。「%SystemRoot%」とはWindowsを操作するのに重要なファイルやアプリケーションがいろいろ格納されているフォルダの「C:\Windows」を表す環境変数です。

explorer.exeを実行するとエクスプローラーが起動しますが、その起動はShell関数を使います。詳しくは以下のコードをご参照ください。

VBAのExcelマクロブックがあるフォルダをエクスプローラーで開くコード

以下のコードはVBAを実行しているExcelマクロブックと同じフォルダをエクスプローラーで開きます。

Sub OpenVBABookFolderInExplorer()
    Dim sPath       As String       '// エクスプローラーで開くフォルダパス
    Dim sCommand    As String       '// 実行するコマンド文字列
    
    '// エクスプローラーで開くフォルダを指定する
    sPath = ThisWorkbook.Path
    
    '// Shell関数に渡すコマンド文字列を指定する
    sCommand = "explorer.exe """ & sPath & """"
    
    '// エクスプローラーを起動して指定フォルダを開く
    Call Shell(PathName:=sCommand, WindowStyle:=vbNormalFocus)
End Sub

「ThisWorkbook.Path」がVBAが保存されているExcelブックのフォルダパスを表します。この部分に開きたいフォルダパスを指定すれば、任意のフォルダをエクスプローラーで開くことが出来ます。

「ThisWorkbook」は実行しているVBAが保存されているExcelブックを示すWorkbookオブジェクトです。

「ThisWorkbook.Path」は実行しているVBAの関数が「C:\test\a.xlsm」に保存されているのであれば「C:\test\a.xlsm」を指し、「PERSONAL.XLSB」のVBAの関数を実行しているのであればPERSONAL.XLSBを指します。

ThisWorkbookの詳細については「ThisWorkBookとActiveWorkBookの違い」をご参照ください。

あとはShell関数でエクスプローラーのアプリケーション本体である「explorer.exe」と半角スペースで区切って「explorer.exe」への引数として開く先のフォルダパスであるExcelマクロブックがあるフォルダを指定しています。

「explorer.exe」はフォルダパスを指定していませんが、環境変数「SystemRoot」にフォルダパスの「C:\WINDOWS」として定義されているため、「explorer.exe」にフォルダパスが付いてなくても環境変数のどれかに定義されているフォルダパスに「explorer.exe」が無いかを探しにいきます。

Shell関数の第二引数の「WindowStyle」は起動したアプリケーション(ここではエクスプローラー)のウィンドウの状態を指定します。ここで指定している「vbNormalFocus」はエクスプローラーを前回起動したときと同じウィンドウサイズで開くための定数です。

Shell関数の詳細については「VBAで他のアプリケーションで起動する(Shell)」をご参照ください。

現在利用しているブックのフォルダをエクスプローラーで開くコード

上のコードではVBA実行中のExcelマクロブックのフォルダをエクスプローラーで開く内容でしたが、現在利用中のアクティブブックのフォルダを開く場合は以下になります。

Sub OpenActiveWorkbookFolderInExplorer()
    Dim sPath       As String       '// エクスプローラーで開くフォルダパス
    Dim sCommand    As String       '// 実行するコマンド文字列
    
    '// エクスプローラーで開くフォルダを指定する
    sPath = ActiveWorkbook.Path
    
    '// Shell関数に渡すコマンド文字列を指定する
    sCommand = "explorer.exe """ & sPath & """"
    
    '// エクスプローラーを起動して指定フォルダを開く
    Call Shell(PathName:=sCommand, WindowStyle:=vbNormalFocus)
End Sub

上のコードと異なるのは6行目の「ActiveWorkbook.Path」の部分だけです。あとは同じです。

]]>
batファイルからExcelブックのマクロを呼び出す方法 https://vbabeginner.net/call-macro-from-bat/ Sat, 15 Jul 2023 07:08:07 +0000 https://vbabeginner.net/?p=6850 batファイルからExcelブックのマクロを呼び出すには

Excelブックに書いてあるマクロは、通常は同じExcelブックか別のExcelブックから呼び出します。

例えば、aaa.xlsmに書いてある「Sub GetData()」という関数があった場合、シートにボタンを置いたりして実行できるようにするのが一般的な使い方です。

しかし、ExcelブックにあるマクロをExcelではない別のアプリケーションから呼び出したい場合があります。

ここではそれをbatファイルを使って実現する方法を紹介します。

batファイルからExcelマクロを呼び出す用途

batファイルからExcelマクロを呼び出す用途ですが、手動でマクロを実行できない場合などが考えられます。

通常マクロを実行するにはボタンやクイックアクセスツールバーなどをクリックしたり、セルの値が変わった場合などのイベント発生時になりますが、そのような手動でマクロを実行できない時間帯に自動で動かしたい、とかの場合はどうしたらよいか、という場面があります。

それを解決する方法の1つに、VBAには指定時刻に実行するOnTimeメソッドが用意されています。これはテレビレコーダーの録画予約みたいなものですが、Excelが起動していることを前提としているため、Excelの起動忘れや異常終了が発生しているとOnTimeでの実行が出来ません。

これを回避するために一番使い勝手がよいのがWindowsのタスクスケジューラ機能を利用する方法です。

タスクスケジューラは事前に設定した日時にアプリケーションを実行する機能です。exeファイルだけでなくbatファイルなども実行できます。WindowsOS自体が管理するため、OnTimeメソッドと異なりExcelが起動していなくても構いません。

なお、OnTimeについての詳細は「VBAで指定時刻にマクロを実行する(OnTime)」をご参照ください。

batファイルからExcelブックを呼び出す手順

具体的には以下のような実行手順になります。

  1. batファイルを実行してVBScriptを呼び出す。
  2. VBScriptからExcelマクロを呼び出す。
  3. Excelマクロが実行される。

batファイル(*.bat) → VBScriptファイル(*.vbs) → Excelマクロ(*.xlsm、PERSONAL.XLSBなど) という呼び出し順になります。

Excelマクロをbatファイルから呼び出すことは出来ないため、Excelを実行することが出来るVBScriptを途中に挟みます。

VBScriptは拡張子がvbsのファイルで、メモ帳などのテキストエディタで作成できます。

VBScriptファイルではなく他のプログラミング言語で作成したアプリケーション(*.exe)などを呼び出してもいいのですが、難易度が上がるのでここではテキストファイルとして作成できるVBScriptを利用します。

batファイルのコード

batファイルには以下のように書きます。

cscript "V:\test\tasksc\vbacall.vbs" "123" "abc"

ここではbatファイル名をVBScriptCall.batとして保存しています。

保存の際の注意点として、文字コードはShift-JIS(SJIS)で保存してください。UTF-8などで保存すると日本語表記の部分が文字化けして動作しないことがあります。特にファイルパスなど日本語が使われやすい部分で問題になります。

スペース区切りで4つ書いています。

“cscript”はVBScriptを実行することを示すコマンドです。

その次に実行するVBScriptファイル名を指定します。batファイルとVBScriptファイルが同じフォルダにある場合はファイル名だけでいいですが、別フォルダにある場合はフォルダパスを指定してください。3番目以降はカンマ区切りで引数を指定します。引数が無い場合は省略可能です。

VBScriptファイルのコード

VBScriptファイルには以下のように書きます。

Dim sArg1           '// 第一引数
Dim sArg2           '// 第二引数

'// 引数受け取り
sArg1 = WScript.Arguments(0)
sArg2 = WScript.Arguments(1)

WScript.Echo sArg1
WScript.Echo sArg2

'// Excelを起動する(既にExcelが起動済みの場合は、さらにもう一つ起動する)
Dim excelApp
Set excelApp = CreateObject("Excel.Application")

'// Excelを表示する
excelApp.Application.Visible = True

'// Excelマクロブックを開く
Dim wb              '// Excelブック
Set wb = excelApp.Workbooks.Open("V:\test\tasksc\a.xlsm")

'// Excelマクロブックの関数を実行する
excelApp.Application.Run "a.xlsm!Module1.SubSample", sArg1, sArg2

'// Excelを終了する
excelApp.Quit

ここではbatファイル名をvbacall.vbsとして保存しています。

文字コードはbatファイルと同様で、Shift-JISで保存してください。

このコードでやっているのは、新しくExcelを起動して、対象のExcelマクロブックを開き、マクロを実行する、という流れです。マクロを実行後は新しく起動していたExcelを終了しています。

CreateObject関数でExcelを起動しているため、既に起動しているExcelがあったとしても新規プロセスのExcelとして起動します。

通常、Excelブックは同時に同じ名前のブックを開くことができませんが、仮に対象のExcelマクロブックが開いていたとしても、別プロセスのExcelで起動しているため、開くことが可能です。

VBScriptはVBAとほとんど同じプログラミング構文になっています。ただ、変数のデータ型を付けられず全てVariantで扱う、などの違いはあります。

VBScriptでハマるポイントがRunメソッドの書き方です。大事なのは3つあります。

ハマりやすい1つ目は、「Excelブック名!モジュール名.関数名」の形で書くという点です。どのブックの、どのモジュールの、どの関数か、ということをきちんと明示する必要があります。ブックを1つしか開いていないなど、状況によってはExcelブック名を書かなくても動作することがありますが、きちんと書いた方が確実です。

ハマりやすい2つ目は、引数の渡し方です。引数がない場合は以下のように書きます。

excelApp.Application.Run "a.xlsm!Module1.SubSample"

引数がある場合は渡す引数の数だけカンマで区切って渡します。

excelApp.Application.Run "a.xlsm!Module1.SubSample", sArg1, sArg2

ハマりやすい3つ目は、データ型です。VBScriptではデータ型の概念が無いため変数は全てVariant型になります。そのため、呼び出す先のVBAの関数の引数がStringやIntegerなどになっていると「型が一致しません」のエラーになります。

なので、よほどのことがない限り、VBAの関数の引数のデータ型もVariant型にしておき、VBAの関数内部でVariant型から別のデータ型に変換するようにした方がよいでしょう。ここで紹介しているコードもそのようにしています。

VBScriptでデータ型変換関数を使って渡すことも出来ますが、データ型の運用をVBScriptとVBAの両方で行わないといけなくなります。

どうしてもVBAの引数のデータ型に合わせないといけない場合は以下のようにVBScript側のRunメソッドでの呼び出し時にデータ型をVBAの引数の型に合わせてCInt関数やCStr関数などで変換する必要があります。

excelApp.Application.Run "a.xlsm!Module1.SubSample", CInt(sArg1), CStr(sArg2)

データ型の変換関数については「VBAの型変換関数(キャスト)」をご参照ください。

呼び出すVBAの関数コード

上記のbatファイル→VBScriptファイルから呼び出されるVBAのコードは以下のようになります。

普通のVBAの関数の書き方でOKです。ちなみにSubの左にPrivateを付けてても呼び出せます。非公開なのになんで動くの?と思いますが動いてしまうのでそういうもんと考えましょう。

Sub SubSample(arg1 As Variant, arg2 As Variant)
    Dim iCount  As Integer
    Dim sData   As String
    
    iCount = Val(arg1)
    sData = CStr(arg2)
    
    MsgBox "データ件数は[" & CStr(iCount) & "]件です。データ名は[" & sData & "]です。"
End Sub

実行するとbatファイルからVBScriptに引数で渡された”123″と”abc”が、メッセージボックスで表示されます。

VBScriptの説明のところで書きましたが、VBScriptから受け取る変数はVariantで受け取り、VBA側でデータ型を変換しています。

使い方

コマンドプロンプトを起動して、batファイルを実行すると、VBScriptファイルを介してExcelマクロブックの関数が呼び出されます。

ここではV:\test\taskscフォルダの中にbatファイル(VBScriptCall.bat)、VBScriptファイル(vbacall.vbs)、Excelマクロブック(a.xlsm)を格納している状態でコマンドプロンプトでVBScriptCall.batを実行しています。

実行するとa.xlsmが開き、そのブックの中に書いてあるSubSample関数が呼び出されています。SubSample関数の中でMsgBox関数でメッセージ出力しています。

タスクスケジューラで指定日時に実行したい場合は、VBScriptCall.batを設定すればよいです。タスクスケジューラの使い方は詳しいサイトがあると思いますのでそちらを探してみてください。

]]>
VBAでExcelを終了する(Application.Quit) https://vbabeginner.net/application-quit/ Mon, 10 Jul 2023 15:52:46 +0000 https://vbabeginner.net/?p=6846 Excelを終了する方法

Excelを終了するときは「Application.Quit」メソッドを呼び出します。

Quitメソッドには引数も戻り値もありません。

以下の関数を実行すると、開いているExcelブックも含めてExcelを終了します。

Sub ApplicationQuit()
    '// Excelを終了する
    Application.Quit
End Sub

ただ、この場合、未保存のブックがあると、保存するかどうかのダイアログが表示されます。

5つのブックを開いていて、2つのブックが未保存だった場合は2回ダイアログが表示されます。

未保存のブックを全て保存せずにExcelを終了する方法

Excelを終了する際に、未保存のブックがあっても保存せずに終了したい場合は以下のように書きます。

Sub ApplicationQuitWithoutSaving()
    '// 保存確認ダイアログを表示しない
    Application.DisplayAlerts = False
    
    '// Excelを終了する
    Application.Quit
End Sub

Application.DisplayAlertsにFalseを設定すると、保存するかどうかのダイアログが表示されません。

そのため、未保存のブックがあっても保存せずにExcelが終了します。Book1のような新規ブックが未保存の場合も保存されません。

未保存のブックを全て保存してExcelを終了する方法

Excelを終了する際に、保存していないブックがある場合に自動的に保存したい場合は以下のように書きます。

Sub ApplicationQuitWithSaving()
    Dim wb  As Workbook     '// ブック
    
    '// 保存確認ダイアログを表示しない
    Application.DisplayAlerts = False
    
    '// 開いているブックを1つずつループ
    For Each wb In Workbooks
        '// ブックが未保存の場合
        If wb.Saved = False Then
            '// 保存する
            wb.Save
        End If
    Next wb
    
    '// Excelを終了する
    Application.Quit
End Sub

保存確認ダイアログは表示されませんが、未保存のブックは保存して閉じられます。

新規ブックのBook1などで未保存のままになっているブックはドキュメントフォルダの直下にBook1.xlsxのように保存されます。

Book1にマクロが記載されていて、それを保存するかどうか、という話はありますが、コードを書いておいて保存せずにApplication.Quitメソッドで強制的にExcelを終了する、ということはまず考えられないため、ここでは紹介しません。

もしそういう用途があるのであれば、「Application.DisplayAlerts = False」の行を削除して保存ダイアログを出すようにした方がよいでしょう。

今動いているExcelとは別のExcelを起動して終了する方法

VBAを実行しているExcelとは別に、VBAの中で別のExcelを起動することが出来ます。その場合の別Excelアプリケーションを終了する場合について説明します。

ここでは別Excelアプリでの新規ブックを作成してA1セルに”abc”と入力して、ドキュメントフォルダにabc.xlsxで保存してから別Excelを終了しています。

変数exが別Excelアプリケーションを指しています。

別Excelは「New Excel.Application」として変数を作成することで別Excelが起動します。

Sub OtherApplicationQuit()
    Dim ex  As New Excel.Application    '// 別のExcelアプリケーション
    Dim wb  As Workbook                 '// ブック
    
    '// 新規ブック作成(この時点では未保存)
    Set wb = ex.Workbooks.Add
    
    '// A1セルに入力
    wb.Worksheets(1).Range("A1").Value = "abc"
    
    '// ブックを保存
    Call wb.SaveAs(Filename:="C:\Users\username\Documents\abc.xlsx")
    
    '// ブックを閉じる
    Call wb.Close
    
    '// 別Excelを終了する
    ex.Quit
End Sub

別Excelの変数宣言の部分を「Dim ex As New Excel.Application」ではなくCreateObject関数を使って書く場合は以下のようになります。

Sub OtherExcelCreateObject()
    Dim ex  As Object
    
    Set ex = CreateObject("Excel.Application")
    
    '// 以下省略
End Sub

]]>
どのような場面でVBAを導入するのが効果的か https://vbabeginner.net/when-introduce-vba/ Sun, 09 Jul 2023 16:09:57 +0000 https://vbabeginner.net/?p=6840 VBAをなぜ使うのか

VBA(Visual Basic for Applications)はExcelの作業の自動化を行うことで作業時間の短縮にとても活躍してくれます。

VBA自体はプログラミング言語ですので、プログラミングをしなければならないというハードルはありますが、それ以前の問題として、どういう場面でVBAを使えばいいのかが分からないので、結果としてVBAを使うに至らないこともありえます。

プログラミングというのは「ある課題を解決するための手段」に過ぎません。それはVBAも同じです。なんらかの問題・課題が無ければ別にVBAを使う必要はありません。そして、その課題・問題に気付かなければVBAを使った方がいいかどうかの判断もできません。

そこで、実際にVBAを導入する場合にどういう場面でVBAを使えばよいのか? というその見つけ方気づき方や、効果的なVBAの使い方について紹介します。

VBAでの効率化を考えた方がいいのはどういうときか

VBAを使うと効果的な場面は以下の2つです。

  • 同じことを何度も行っている場合
  • 作業が面倒な場合

このどちらかを感じたら、VBAの出番です。

「同じことを何度も行っている場合」や「作業が面倒な場合」の例として以下のような一連の作業が挙げられます。

  1. テキストファイルの内容を新規Excelブックに転記する。
  2. 入力したデータが正しいかチェックする。
  3. 不要なデータを削除する。
  4. データの並び順を変更する。
  5. セルの書式設定を行う。
  6. シート名を付ける。
  7. 指定されたブック名で保存する。

このような一連の作業を大量に毎日繰り返し行っているのであれば、間違いなくVBAでラクをした方がいいでしょう。データ量にもよりますが、手作業の数倍どころではなく10倍、100倍、1000倍という速さで終わります

これらの他にも、Excelを触ってて「あー、これ毎日毎日、同じことやってんなー」とか「もうこれホント、メンドクサイ」とかを感じることは多々あります。そう感じたらVBAの出番です。

VBAでなくてもExcelの標準機能のワークシート関数や条件付き書式などで対応できることもありますがやはり出来ることに限界があります。VBAは複数のことを一度で終わらせることが出来る利点があります。

ここまでが「どういうときにVBAを使えばよいか」を探す方法です。ここから先は実際にVBAでプログラミングを行う話になるため、プログラミング自体の壁がありますが、「VBAでどういうことを解決したいか」という課題は明確になります。

VBAを使うと時間短縮だけでなくミスが無くなる

上記のように、「同じことを何度も行っている場合」や「作業が面倒な場合」に対するVBAでのプログラミングをすることで、作業がラクになるという利点があります。

ただ、作業がラクになるのは、単に手を動かす時間が短縮されることだけではありません。

それとは別に、「ミスが無くなる」という多大な効果があります。ミスが減る、ではなく、ミスが無くなる、です。

手作業のミスが一切無くなります。目に見えませんが、これは絶大な効果です。

人間の作業の中で時間が掛かるものの一つに「ミスしたことへの対応」があります。これは本当に時間が掛かります。ミスが無ければ「ミスの対応」などそもそもやらなくてよい作業ですし、ミスしたら「報告書を書け」だの、「原因分析しろ」だの、うるさいことを言ってくる時間泥棒に遭遇することもなくなります。

あなたの大事な人生の時間をVBAを使って守りましょう。

もちろん、ミスが無いようにプログラミングをしておかなければなりませんが、ミスがあっても修正すればいいだけですし、望んでいる形になってしまえば状況が変わらない限りずっと使い続けることが出来ます。

VBAのコードを書くには

VBAを使って効率化を図りたい作業が見つかったあとは、実際にVBAでプログラミングをすることになります。

熟練の人であればいきなりプログラムを書いてしまうことも可能ですが、そのような人はおそらく少数派です。

そうでない場合は、VBAの本を読んでみることをお勧めします。書店に行って自分に理解できる内容かどうかを確認した方がいいでしょう。

以下の本は基礎から分かる読みやすい良書です。2019年出版ですがVBA自体はバージョンで変わることはほとんどないので気にしなくていいです。知人の経理事務の仕事をしている方は元々プログラミングを全くできませんでしたがこれを読んでVBAを自分で書けるようになりました。

よくわかるExcel 2019/2016/2013 マクロ/VBA

本にも書いてありますが、実際にVBAに慣れるにはまずマクロの記録機能を使うのが一番の近道でしょう。

あとは、ネットでサンプルコードを探すか、ChatGPTのようなAIを利用してコードを書いてもらうか、などの方法がありますが、そのコードが自分が探しているものなのかを判定できないのであれば、闇雲に彷徨っている状況ですので、結局は学習した方が近道です。

上記は基本の基本の本なので、ある程度理解が進むと物足りなくなります。そういう場合はさらに自分のレベルに合う本を書店で探してみてください。

]]>