TL;DR —
Application.GetOpenFilenameは「開く」ダイアログを表示し、ユーザーが選んだパスを文字列として 返します — ファイルを開くことは しません。実際に開くには、やはりWorkbooks.Openを呼びます。ユーザーがキャン セルをクリックするとブール値のFalseを返すので、変数は(As Stringではなく)As Variantで宣言し、使う前にIf fname = False Then Exit Subを確かめます。
Sub AskThenOpen()
Dim fname As Variant ' Variant なので、キャンセルは False を返せる
fname = Application.GetOpenFilename( _
"Excel Files (*.xlsx;*.xlsm), *.xlsx;*.xlsm", , "Choose a workbook")
If fname = False Then Exit Sub ' ユーザーがキャンセルした
Workbooks.Open fname ' GetOpenFilename が渡したのはパスだけ
End Sub
GetOpenFilename は、パスのハードコーディングをやめる一番短い方法です。おなじみの「開く」ダイアログを表示し、ユ
ーザーに参照させ、選んだものを返してくれます。ですが、この関数についての二つの事実がネット上の質問のほとんどを生
んでいて、そのどちらも同じ思い違いから来ています。この関数はパスを返すのであって、ファイルを開かないし、ユーザ
ーがキャンセルしても声高に失敗しません。 この二つをはっきりさせれば、何度も手を伸ばすことになる一行の道具になり
ます。
この記事で学べること
GetOpenFilename・GetSaveAsFilename・FileDialogを結びつける考え方- なぜ開かれたブックではなく パス を返すのか — そして、まだ書かなければならない一行
- キャンセルの落とし穴:
Falseを返すので、変数はAs Variantでなければならない - 複数の拡張子と複数のフィルターグループを扱う
FileFilterの構文 MultiSelect:=Trueで一度に多くのパスを返すこと(1 始まりの配列)GetOpenFilenameで足りるのはいつで、FileDialogに格上げすべきなのはいつか
考え方の軸:「どのファイル?」と書かれた付箋
GetOpenFilename は、「どのファイル?」 とたずねる付箋をユーザーに手渡すものだと考えてください — ユーザーはパス
を書いて返してきます。するのはそれだけです。キャビネットまで歩いて行って、ファイルを引き出してくれるわけではあり
ません。
fname = Application.GetOpenFilename(...) ' パス(PATH)の文字列を返す。例:"C:\Data\Jan.xlsx"
Workbooks.Open fname ' 開くのはあなた
「GetOpenFilename が動かない」という報告で一番多いのは、実のところ「呼んだのにファイルが開かなかった」です。開か
なくて当たり前です — 手に入れたのはパスで、それを開くのは別の行なのです。パスをたずねること と パスに対して動く
こと が二つのステップだと腹に落ちれば、この関数は単純で予測どおりのものになります。
キャンセルの落とし穴:False を返すので Dim As Variant
一番痛いのがこれです。ユーザーが キャンセル をクリックすると、GetOpenFilename はブール値の False を返しま
す。そのせいで、変数を宣言するときに選択を迫られます。
Dim fname As Variant ' 正しい - パスの String かブール値の False を保持できる
' Dim fname As String ' 誤り - False がテキストの "False" に強制変換される
fname As String と宣言すると、VBA は返ってきた False をこっそり 文字列 の "False" に強制変換します。すると
If fname = False は決して発動せず、コードは False という名前のゴミの「パス」を抱えたまま進んでいきます。
As Variant と宣言すればブール値はブール値のままなので、If fname = False Then Exit Sub がちゃんとキャンセルを捕
まえます。これは、キャンセルされた InputBox を読むときとまったく同じ落とし穴で — 直し方も
同じです。結果を Variant に保持し、信用する前に、受け取った答えの型を確かめることです。
FileFilter:説明、カンマ、パターン
最初の引数は、ダイアログがどのファイルを表示するかを制御します。その形は、説明、カンマ、そしてパターン — さらに、 いくつかのグループをつなげられます。
' グループ一つ:ラベル、そしてパターン
fname = Application.GetOpenFilename("Text Files (*.txt), *.txt")
' 一つのグループに複数の拡張子:パターンはセミコロンで区切る
fname = Application.GetOpenFilename("Excel Files (*.xlsx;*.xlsm), *.xlsx;*.xlsm")
' ユーザーが切り替えられる複数のグループ:ラベルとパターンの組をカンマで区切る
fname = Application.GetOpenFilename( _
"Excel Files, *.xlsx;*.xlsm, CSV Files, *.csv, All Files, *.*")
一つのフィルターの中では、複数の拡張子を セミコロン でつなぎます(*.xlsx;*.xlsm)。ユーザーがドロップダウンで
切り替えられる複数のフィルターを用意するには、ラベルとパターンの組を カンマで区切り ます。省略できる二番目の引
数 FilterIndex は、既定でどのグループを選ぶかを決め、三番目の Title はダイアログの見出しを設定します。カンマと
セミコロンの使い分けの規則を正しく守れるかどうかが、ちゃんと動くフィルターと、何も表示しないフィルターの分かれ目
です。
複数のファイル:MultiSelect は 1 始まりの配列を返す
MultiSelect:=True を渡すと、戻り値の型が変わります。一つのパスの文字列の代わりに、パスの 配列 を受け取ります
— ユーザーがキャンセルすれば、やはり False です。
Sub OpenSeveral()
Dim files As Variant, i As Long
files = Application.GetOpenFilename( _
"Excel Files, *.xlsx;*.xlsm", , , , True) ' 最後の引数 = MultiSelect
If Not IsArray(files) Then Exit Sub ' False(キャンセル)は配列ではない
For i = LBound(files) To UBound(files) ' 配列は 1 始まり
Workbooks.Open files(i)
Next i
End Sub
大事な細部が二つあります。= False ではなく IsArray(files) で確かめること。成功したときに手にしているのは、文字
列ではなく配列だからです。そしてループは LBound から UBound まで回すこと — GetOpenFilename
が返す配列は 1 から始まり、ここでは For i = 1 To ... とハードコーディングしてもたまたま動きますが、LBound/UBound
は決して破綻しない習慣です。各要素は Workbooks.Open にそのまま渡せる完全なパスです。
GetOpenFilename か FileDialog か:何が必要かで選ぶ
日々の仕事 — 読み込むファイルを一つたずねる — なら、GetOpenFilename が単純さで勝ります。一行で、パスがまっすぐ
返ってきて、設定するオブジェクトもありません。フォルダー が必要なとき(GetOpenFilename では選べません)、ダイ
アログオブジェクトのより豊かな制御が欲しいとき、あるいは .SelectedItems から読むほうが好みのときは、
FileDialog に格上げしましょう。何を開くか ではなく どこに保存するか をたずねるなら、
鏡写しの関数が GetSaveAsFilename です。三つとも同じ規則を共有します。返すのはパス
で、開くのも保存するのも次の行はあなたの仕事です。
ExcelMaster の活用
ここでの二つの間違いは、静かな類のものです。結果を As String で宣言すること(そのせいでキャンセルがテキストの
"False" としてすり抜ける)、そして受け取ったのがパスだけなのにファイルが開くと思い込むこと。どちらもコンパイルは
通り、他人のマシンで実行時に失敗します。
ExcelMaster には、やりたいことを言うだ
けです — 「CSV をたずねて、新しいシートに取り込んで」 — すると、正しいフィルター、Variant によるキャンセルのガー
ド、そしてパスに対して実際に動く Workbooks.Open(あるいは取り込み)を備えた GetOpenFilename の呼び出しを書いて
くれます。ブックとコードはあなたの手元に残ります。
よくある質問
Excel VBA で GetOpenFilename を使うには?
fname = Application.GetOpenFilename(fileFilter, filterIndex, title) を呼びます。fname As Variant で宣言し、キャン
セルを捕まえるために If fname = False Then Exit Sub を確かめます。成功すると fname は選ばれたパスを文字列として
保持します — 開くには Workbooks.Open に渡します。GetOpenFilename 自体が返すのはパス
だけで、何も開きません。
GetOpenFilename がファイルを開かないのはなぜですか?
そういうものではないからです — ユーザーが選んだ パス を返し、そこで止まります。ファイルを開くのは別のステップです。
ブックなら Workbooks.Open fname、それ以外のファイルなら FileSystemObject や
Dir を使います。ダイアログは「どのファイル?」とたずねているだけで、その答えに対して動くのは
あなたのコードです。
GetOpenFilename でユーザーがキャンセルをクリックしたかを確かめるには?
変数を As Variant で宣言し、If fname = False Then Exit Sub を確かめます。キャンセル時、関数はブール値の False
を返し、成功時はパスの文字列を返します。代わりに変数を As String で宣言すると、False はテキストの "False" に
強制変換され、そのチェックは決して発動せず、マクロは無効なパスを抱えたまま進みます。
GetOpenFilename で複数のファイルを選ぶには?
MultiSelect:=True(五番目の引数)を渡します。戻り値はパスの文字列の配列になり、キャンセル時は False です。まず
IsArray(result) を確かめ、それから For i = LBound(result) To UBound(result) をループして、各 result(i) を開きま
す。戻り値の型が配列に変わるので、キャンセルの判定に = False を使ってはいけません — IsArray を使いましょう。
GetOpenFilename の FileFilter の構文は?
各フィルターは、説明のあとにカンマとパターンが続きます。"Excel Files (*.xlsx), *.xlsx" です。一つのフィルターで複
数の拡張子をつなぐにはセミコロンを使います。"*.xlsx;*.xlsm" です。複数のフィルターを用意するには、ラベルとパター
ンの組をカンマで区切ります。"Excel, *.xlsx, CSV, *.csv, All, *.*" です。省略できる FilterIndex 引数が、既定でど
れを選ぶかを設定します。
検証環境
検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-09-26。
関連ガイド: VBA FileDialog · VBA GetSaveAsFilename · VBA Open Workbook · VBA InputBox · VBA Dir · VBA UBound
