和風スパゲティのレシピ

日本語でコーディングするExcelVBA

FileSystemObjectの呼び出しを短縮する方法

たとえばFileSystemObject(以下FSO)を使うサンプルコードとして、

Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")

' ファイルをコピー
FSO.CopyFile コピー元ブック.FullName _ 
                    コピー先フォルダパス & "\" & コピー元ブック.Name

こんなコードを書くとします。

FSOはとても便利なのですが、この2行が面倒なんですよね。


慣れないうちはいちいちググってコピーしたり、
前に使った場所を探しに行ったりしてしまいます。


しかしこれ、実はもっと簡単に宣言することが可能です。

それを解決するのがこちらです↓

' Public変数として宣言(Subの外で宣言)
Public FSO As New FileSystemObject

Sub プロシージャ1

    ' ファイルをコピー
    FSO.CopyFile コピー元ブック.FullName _ 
                        コピー先フォルダパス & "\" & コピー元ブック.Name
    ' ↑いきなり「FSO」と書き出してOK

End Sub

Sub プロシージャ2

    ' フォルダを作成
    FSO.CreateFolder フォルダパス
    ' ↑別のSubプロシージャと使いまわしてもOK

End Sub

なんと「ブック1つにつき1行だけ」で済むようになっていますね。


このように、Public変数としてFileSystemObjectを宣言し、
しかもその場で「New」まで済ませてしまうことができます。

すごく簡単ですね。



ただしこの方法を用いるには、
Microsoft Scripting Runtime」の参照設定が必要です。

「ツール」→「参照設定」を開き、Scripting Runtimeにチェックを入れて下さい。


この操作が多少面倒かもしれませんが、
「どのプロシージャでもFSOと書くだけでいい」というのは、
それを補って余りある便利さです。

是非とも活用して下さい。


なお、参照設定で「Microsoft Scripting Runtime」を探すのが面倒な場合は、
「Microsoft Scripting Runtime」を参照した他のブックを開いておく
という技が使えます。


Scripting Runtimeを参照した他のブックを開いていると、
Scripting Runtimeのチェック欄が一番上に並び変わり、
「ツール」→「参照設定」を開いたすぐそこに設置されています。

Scripting Runtimeを上部に

これなら5秒もかかりませんので、この方法を知っておきましょう。



ちなみに参照設定をあまりやっていないという方にご説明しますと、
そもそもFileSystemObjectは参照設定をして使うべきものです。


なぜかというと、

FileSystemObjectのメンバー候補

この入力候補を出すためには参照設定が必要であり、
この入力候補こそがFSOを使用するメリットだからです。


今まで

Dim FSO As Object
Set FSO = CreateObject("Scripting.FileSystemObject")

という書き方をしていて、

  • FSOとDirの違いってなんだろう
  • なぜFSOを使うんだろう

なんて疑問を抱いたことがある方は、こちらの記事でこれを説明しています。

よろしければご覧下さい。
www.limecode.jp


おまけ:参照設定しない(遅延バインディングの)メリット

参照設定をしてFSOを使うことを「事前バインディング」と呼び、
CreateObjectでFSOを使うことを「遅延(実行時)バインディング」と呼びます。


前述の通りFSOは参照設定を行い、

FileSystemObjectのメンバー候補

この入力候補を使って書くためのものです。

なので、「参照設定しなくていい」はあまりメリットではありません。


では「CreateObject("Scripting.FileSystemObject")」のメリットは何かというと、
コピペで動かせる」ことにあります。


FileSystemObjectを知らない人にコピペでコードを渡しても、
そのまま動くというのがひとつのメリットになります。

ネットのコードにCreateObject("Scripting.FileSystemObject")を使ったものが多いのは、こういった理由が大きいのでしょうね。


また自分でFSOを使う場合も、例えば汎用関数などを
コピー(ドラッグ&ドロップ)だけで使える関数/モジュール
として作りたい場合は、遅延バインディングで書く意味があります。


そういったメリットを享受しつつFSOの呼び出しを短縮したい場合は、
FileSystemObjectを返す関数を用意しておくという方法があります。

' Create部分を汎用関数として持っておく
Function FSO() As Object
    Static FSO_ As Object
    If FSO_ Is Nothing Then Set FSO_ = CreateObject("Scripting.FileSystemObject")
    Set FSO = FSO_
End Function

Sub サンプルマクロ

    ' ファイルをコピー
    FSO.CopyFile コピー元ブック.FullName _
                      , コピー先フォルダパス & "\" & コピー元ブック.Name
    ' ↑Public変数と同じようにいきなりFSOと書いてよい
End Sub


このように、常に同じオブジェクトを使うようなコードは、
そのオブジェクトを返す関数を作ってしまうと便利です。

こうすることで、そのオブジェクトをあたかも定数のように扱えます。


他の簡単な例として、

' 汎用関数
Function Fx() As WorksheetFunction
    Set Fx = WorksheetFunction
End Function

' 使用例
Fx.Sum(合計したいセル範囲)

などもおすすめですね。


コードを解説しますと、FSOを何個も作るのは好ましくなさそうなので、

Function FSO() As Object
    Static FSO_ As Object
    If FSO_ Is Nothing Then Set FSO_ = CreateObject("Scripting.FileSystemObject")
    Set FSO = FSO_
End Function

とStatic変数に入れて、初回呼び出し時のみCreateしています。


一応これ↓でも同じ動きをしてくれるんですが、

Function FSO() As Object
    Set FSO = CreateObject("Scripting.FileSystemObject")
End Function

呼び出すたびにCreateObjectされてしまうため若干遅くなります。

※ FileSystemObjectは、「プロジェクト全体で生成インスタンスが1個だけ」になるよう制御されているとのことで、厳密には何個もFileSystemObjectを作れるわけではありません。



なお、これでは前述の「入力候補」がでないのですが、
この対策として、

' Function FSO() As FileSystemObject ' コーディング用
Function FSO() As Object ' 配付用
    Static FSO_ As Object
    If FSO_ Is Nothing Then Set FSO_ = CreateObject("Scripting.FileSystemObject")
    Set FSO = FSO_
End Function

と、Functionの宣言部を2通り書いておき、開発時と配布時でコメントアウトを切り替えるというトリッキーな技があります。

いずれも汎用関数モジュールを作っている人向けの技ですが、
結構便利なので参考にしてみてください。


まあ「Scripting Runtime」ってDictionaryでも使うので、
Dictionaryをマスタ-する頃には

  • マクロブック作ったら即参照!
  • マクロブック作るテンプレートブック作って常時参照!

になってる気もしますけどね。


それでも

Function Fx() As WorksheetFunction
    Set Fx = WorksheetFunction
End Function

こっちは永遠に使えると思います。

「常に同じオブジェクトを返す関数」という考え方は応用が利きますので、
使える局面がないか考えてみてください。