ファイル選択ダイアログ
GetOpenFileName関数
プログラムからファイルを読み書きするには、そのファイルの場所の情報(パス)が必要になります。
ユーザーに任意のファイルを選択させたい場合、手動でパス文字列を入力させていたのでは不便ですし、入力ミスのおそれがあります。
既存のファイルを指定するにはファイル選択ダイアログを使用すると便利です。
これはあらかじめWindowsが用意しているダイアログで、これをプログラム上から呼び出すだけでファイルの選択が可能になります。
ファイルを開くダイアログはGetOpenFileName
関数を使用します。
- BOOL GetOpenFileNameW(
LPOPENFILENAMEW unnamedParam1
); - ファイルを開くダイアログを作成する。
ユーザーが「OK」ボタンを選択しダイアログを閉じると戻り値はゼロ以外になる。
その他の方法で閉じるかエラーが発生した場合はゼロが返される。
この関数はOPENFILENAME
構造体のポインタを引数に取ります。
具体的な動作はこの構造体にセットした値により決定されます。
- typedef struct tagOFNW {
DWORD lStructSize;
HWND hwndOwner;
HINSTANCE hInstance;
LPCWSTR lpstrFilter;
LPWSTR lpstrCustomFilter;
DWORD nMaxCustFilter;
DWORD nFilterIndex;
LPWSTR lpstrFile;
DWORD nMaxFile;
LPWSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCWSTR lpstrInitialDir;
LPCWSTR lpstrTitle;
DWORD Flags;
WORD nFileOffset;
WORD nFileExtension;
LPCWSTR lpstrDefExt;
LPARAM lCustData;
LPOFNHOOKPROC lpfnHook;
LPCWSTR lpTemplateName;
LPEDITMENU lpEditInfo;
LPCSTR lpstrPrompt;
void *pvReserved;
DWORD dwReserved;
DWORD FlagsEx;
} OPENFILENAMEW, *LPOPENFILENAMEW; - GetOpenFileName関数で使用する情報を格納する構造体。
メンバが非常に多いですが、使用するメンバにだけ値をセットします。
lStructSize
メンバはこの構造体のサイズの指定です。
つまりsizeof(OPENFILENAME)
を指定します。
hwndOwner
メンバはこのダイアログボックスの所有者ウィンドウのハンドルを指定します。
NULL
にすることも可能です。
hInstance
メンバはFlags
メンバにOFN_ENABLETEMPLATEHANDLE
フラグが含まれる場合、ダイアログテンプレートを含むメモリオブジェクトへのハンドルを指定します。
Flags
メンバにOFN_ENABLETEMPLATE
フラグが含まれる場合、ダイアログテンプレートを含むモジュールへのハンドルを指定します。
どちらのフラグも含まれない場合、このメンバは無視されます。
lpstrFilter
メンバはファイルのフィルター文字列の指定です。
(ダブルNULL終端文字列)
特定の拡張子を持つファイルだけをダイアログ上に表示する場合に使用します。
NULL
を指定するとダイアログにフィルターは表示されません。
使い方は後述するサンプルコードを参照してください。
lpstrCustomFilter
メンバはユーザーが選択したフィルターパターン(ワイルドカード)文字列が格納される文字列バッファの指定です。
文字列はフィルターの説明文とフィルターパターン文字列のペアで構成されます。
それぞれの文字列はNULL文字で区切られ、終端はダブルNULLで終了します。
ユーザーがファイルを選択すると、選択されたフィルターパターンが文字列ペアの後半部分にコピーされます。
NULL
を指定するとファイルター文字列は保存されません。
(古いダイアログ用?)
nMaxCustFilter
メンバはlpstrCustomFilter
メンバのサイズ(文字数)の指定です。
少なくとも40文字分以上の長さが必要です。
lpstrCustomFilter
メンバがNULL
の場合は無視されます。
nFilterIndex
メンバはフィルター文字列のインデックスです。
1
を指定すると最初のフィルター文字列が使用されます。
0
を指定するとlpstrCustomFilter
メンバで指定されているフィルター文字列が使用されます。
lpstrCustomFilter
メンバがNULL
の場合は最初のフィルター文字列が使用されます
ファイルが選択されると自動的に更新されます。
lpstrFile
メンバはユーザーが選択したファイルの絶対パスが格納される文字列バッファの指定です。
関数呼び出し前に文字列をセットしておくと、ダイアログの初期値として使用されます。
(このファイルが選択された状態でダイアログが開かれる)
初期値を使用しない場合はバッファを空にしておく必要があります。
nMaxFile
メンバはlpstrFile
メンバのサイズ(文字数)の指定です。
少なくとも256文字分以上の長さが必要です。
lpstrFileTitle
メンバはユーザーが選択したファイルのファイル名を格納する文字列バッファの指定です。
(ドライブ名やディレクトリ名は含まれない)
必要なければNULL
を指定できます。
nMaxFileTitle
メンバはlpstrFileTitle
メンバのサイズ(文字数)の指定です。
lpstrFileTitle
メンバがNULL
の場合は無視されます。
lpstrInitialDir
メンバは、ダイアログの初期ディレクトリのパスの指定です。
ただしここに指定したディレクトリが常に初期値として使用されるわけではなく、細かい条件により決定されます。
(詳細は省きます)
lpstrTitle
メンバはダイアログボックスのタイトルバーに表示される文字列の指定です。
NULL
を指定するとデフォルトのタイトル(「開く」)が使用されます。
Flags
メンバはダイアログボックスの動作を変更するフラグです。
以下の定数の組み合わせを使用できます。
(以下の説明は後述する「名前を付けて保存」ダイアログで使用するものも含みます)
また、ダイアログボックスが閉じられたときに特定の状態を表すためにFlagsメンバは上書きされることがあります。
定数 | 説明 |
---|---|
OFN_READONLY |
「読み取り専用ファイルとして開く」ボタンを「ファイルを変更するために開く」に変更する
読み取り専用としてファイルを開くと、このフラグが |
OFN_OVERWRITEPROMPT | ファイルの保存時、既にファイルが存在する場合に上書き確認ダイアログを表示する |
OFN_HIDEREADONLY | 読み取り専用チェックボックスを非表示にする |
OFN_NOCHANGEDIR | ユーザーが別のディレクトリを選択した場合でもカレントディレクトリを移動しない 「開く」ダイアログ(GetOpenFileName関数)では無効 |
OFN_SHOWHELP | 「ヘルプ」ボタンを表示する。hwndOwner メンバにNULL を指定してはならず、HELPMSGSTRING メッセージを処理する必要がある |
OFN_ENABLEHOOK | lpfnHook メンバーで指定したフック機能を有効にする |
OFN_ENABLETEMPLATE | ダイアログボックスのテンプレートを有効にするhInstance メンバはlpTemplateName メンバが格納されているモジュールへのハンドルを指定する必要がある |
OFN_ENABLETEMPLATEHANDLE | ダイアログボックスのテンプレートを有効にするhInstance メンバはダイアログボックステンプレートが格納されたメモリオブジェクトを指定する必要がある( lpTemplateName メンバは無視される) |
OFN_NOVALIDATE | ファイル名に、ファイル名として無効な文字を使用できる (ファイル名のチェックをしない) |
OFN_ALLOWMULTISELECT | 複数のファイルを選択可能にするOFN_EXPLORER と同時に指定しない場合、古いスタイルのダイアログになる |
OFN_EXTENSIONDIFFERENT | lpstrDefExt メンバで指定した拡張子とは異なる拡張子をユーザーが入力した |
OFN_PATHMUSTEXIST | 有効なパスおよびファイル名しか入力できない |
OFN_FILEMUSTEXIST | 既存のファイル名しか入力できない このフラグは OFN_PATHMUSTEXIST フラグも有効にする「名前を付けて保存」ダイアログ(GetSaveFileName関数)では無効 |
OFN_CREATEPROMPT | ユーザーが存在しないファイル名を指定した場合、ファイルを新規作成する確認ダイアログを表示する |
OFN_SHAREAWARE | ネットワーク共有違反エラーを無視してファイル名を返す |
OFN_NOREADONLYRETURN | 読み取り専用ファイルおよびディレクトリは選択できない |
OFN_NOTESTFILECREATE | ダイアログを閉じるまでファイルを作成しない |
OFN_NONETWORKBUTTON | 「ネットワーク」ボタンを非表示および無効にする |
OFN_NOLONGNAMES | 長いファイル名を使用しない (8.3形式を使用する) 古いスタイルのダイアログボックスでのみ有効 |
OFN_EXPLORER |
フックプロシージャまたはカスタムテンプレートを使用する場合に指定する必要がある |
OFN_NODEREFERENCELINKS | ショートカットファイル(.lnkファイル)を選択した時、そのショートカットファイルのパスを返す このフラグを指定しない場合、ショートカットファイルが指す先にあるファイルへのパスを返す |
OFN_LONGNAMES |
古いスタイルのダイアログボックスで長いファイル名の使用を強制する ※MicorSoft Docではこのような説明になっているが、近年のOSではこの通りに機能しないかもしれない |
OFN_ENABLEINCLUDENOTIFY | ユーザーがフォルダを開いたとき、CDN_INCLUDEITEM メッセージをフックプロシージャに送信する |
OFN_ENABLESIZING | フックプロシージャまたはカスタムテンプレートの使用時、ダイアログボックスのサイズを変更可能にする (標準ではこのフラグの有無に関係なくサイズ変更可能) 古いスタイルのダイアログボックスはこのフラグに関係なくサイズ変更はできない |
OFN_DONTADDTORECENT | 選択したファイルを「最近使ったファイル(ファイル履歴)」に追加しない |
OFN_FORCESHOWHIDDEN | システムファイルおよび隠しファイルを強制的に表示する ただし隠しファイルに設定されているシステムファイルは表示しない |
nFileOffset
メンバはlpstrFile
メンバに格納されるパスの、ファイル名の位置を示すオフセット(先頭からの文字数)です。
先頭の文字は0文字目です。
例えば「C:\abc\file.txt」というパスの場合、このメンバには「7」が格納されます。
lpstrFile
メンバに複数のパスが格納されている場合、最初のパスのファイル名の位置を示します。
nFileExtension
メンバはlpstrFile
メンバに格納されるパスの、拡張子の位置を示すオフセット(先頭からの文字数)です。
これは「.」(ドット)の次の文字の位置を示します。
例えば「C:\abc\file.txt」というパスの場合、このメンバには「12」が格納されます。
拡張子がない場合は0となります。
lpstrDefExt
メンバはデフォルトの拡張子の指定です。
ユーザーが拡張子を入力しなかった場合に、ここで指定した拡張子が追加されます。
文字列には「.」(ドット)は含めません。
何文字の文字列を指定しても、最初の三文字のみが使用されます。
必要がない場合はNULL
を指定できます。
lCustData
メンバはフックプロシージャを使用する場合に、フックプロシージャに渡す任意の値の指定です。
フックプロシージャとは、ダイアログの動作をカスタマイズする際に使用するものです。
ここでは説明は省きます。
lpfnHook
メンバはフックプロシージャの指定(関数へのポインタ)です。
Flags
メンバにOFN_ENABLEHOOK
フラグを含めない場合、このメンバは無視されます。
ここでは説明は省きます。
lpTemplateName
メンバはダイアログテンプレートの指定です。
ダイアログテンプレートは、ダイアログボックスの見た目を変更するものです。
ここでは説明は省きます。
lpEditInfo
メンバ、lpstrPrompt
メンバ、pvReserved
メンバは使用しません。
FlagsEx
メンバはダイアログをカスタマイズするために、OFN_EX_NOPLACESBAR
という定数を指定することができます。
この定数を指定すると、ダイアログに特定のディレクトリへのクイックアクセスを提供するツリーコントロール(プレースバー)が表示されなくなります。
必要がなければ0を指定します。
サンプルコード
メンバ数が多くて面倒そうですが、単にファイルを選択するだけならば必要なメンバはそれほど多くありません。
以下にサンプルを示します。
#include <windows.h>
int APIENTRY wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nCmdShow)
{
//ファイルパスを格納する変数
//0で初期化しておく
WCHAR filePath[MAX_PATH] = { 0 };
//変数宣言後の初期化は↓のように先頭にNULL文字を入れるだけでも良い
//filePath[0] = L'\0';
OPENFILENAME ofn = { 0 };
ofn.lStructSize = sizeof(OPENFILENAME); //構造体のサイズ
ofn.hwndOwner = NULL; //オーナーウィンドウのハンドル
ofn.lpstrFilter = L"全てのファイル (*.*)\0*.*\0"; //拡張子フィルター
ofn.nFilterIndex = 0; //フィルターの初期値
ofn.lpstrFile = filePath; //選択したファイルパスを受け取るバッファ
ofn.nMaxFile = MAX_PATH; //↑のバッファサイズ
if (GetOpenFileName(&ofn)) {
MessageBox(NULL, filePath, L"情報", MB_OK);
}
else {
MessageBox(NULL, L"キャンセルされました", L"情報", MB_OK);
}
return 0;
}
選択したファイルのパスをメッセージボックスで表示するプログラムです。
コードを簡略化するために、ウィンドウの生成等はせずWinMain関数内でいきなりダイアログを呼び出しています。
ファイルパスを受け取るための変数filePathをあらかじめ用意しておきます。
この変数は空文字(先頭要素がNULL文字)である必要があります。
ローカル変数は宣言後の中身は不定なので、0で初期化しておきます。
(先頭要素にNULL文字を代入するだけでも良い)
同じように、OPENFILENAME構造体も0で初期化しておきます。
こちらは不要なメンバに値がセットされないようにする必要があるので、全てのメモリ領域を0で初期化します。
static変数の場合はすべて0で初期化されるのでこの処理は必要ありません。
また、今回は最初にすべて0で初期化しているので、メンバへのNULL
や0
代入は本来は必要ありません。
次に、OPENFILENAME構造体のメンバに値をセットしていきます。
今回は親となるウィンドウがないので、hwndOwner
メンバにはNULL
を指定します。
ダイアログ上で選択したファイルパスはlpstrFile
メンバに格納されます。
ここにはローカル変数filePathのポインタを指定しているので、この変数にファイルパスが格納されます。
lpstrFilter
メンバは、ダイアログ上に表示するファイルの種類の指定です。
ダイアログ上にあるドロップダウンリストから選択することで、ファイル表示をフィルタリングできます。
これはNULL文字を区切り文字とする特殊な文字列を指定します。
例えば「テキストファイル」「HTMLファイル」「全てのファイル」を選択したい場合は以下の文字列を指定します。
ofn.lpstrFilter =
L"テキストファイル (*.txt)\0*.txt\0"
L"HTMLファイル (*.htm;*.html)\0*.htm;*.html\0"
L"全てのファイル (*.*)\0*.*\0";
NULL文字の左側がファイルの種類を表す文字列、右側がそのファイルを示すワイルドカード文字列です。
これらはペアで指定する必要があります。
例えばテキストファイルならば「*.txt」を指定することで、拡張子「.txt」を持つ全てのファイルが指定されます。
(ワイルドカードについてはファイルとフォルダの検索#ワイルドカードを参照)
HTMLファイルは二種類の拡張子が存在します。
この場合はセミコロン(;)で区切ることで同時に複数の種類のワイルドカード文字列を指定できます。
(スペースなどは含めないようにしてください)
「*.*」は拡張子を持つすべてのファイルが対象になります。
なお、上の例ではファイル種類ごとに改行で文字列を分割してますが、連続して記述しても同じです。
(C言語では文字列リテラルを連続して記述するとひとつの文字列リテラルとして扱われる)
必要なメンバをセットしたら、GetOpenFileName関数にOPENFILENAME構造体変数のアドレスを渡して実行します。
複数のファイルを選択
ダイアログ上で複数のファイルを選択可能にするには、Flags
メンバにOFN_ALLOWMULTISELECT
フラグを含めます。
ただしこのフラグのみだと古いタイプのダイアログ形式になってしまうので、同時にOFN_EXPLORER
フラグもセットします。
OPENFILENAME ofn = { 0 };
ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
ダイアログ上ではCtrlキーやShiftキー、マウスドラッグなどで複数のファイルを選択可能になります。
取得したパスはlpstrFile
メンバに格納されるのですが、得られる文字列は「ディレクトリ名\0ファイル名1\0ファイル名2\0\0」というNULL区切り形式の文字列になります。
例えば「C:\test」フォルダ内にある「123.txt」と「456.html」という二つのファイルを選択すると、
"C:\test\0123.txt\0456.html\0\0"
という文字列になります。
(終端はダブルNULL)
そのまま文字列として使用すると最初のディレクトリ名までしか有効な文字列にならないので注意してください。
ファイルをひとつだけ選択した場合は通常通りのファイルパスが得られます。
GetSaveFileName関数
GetOpenFileName
関数はファイルを「開く」ためのダイアログですが、指定の場所にファイルを書き込む場合はGetSaveFileName
関数を使用します。
-
BOOL GetSaveFileNameW(
LPOPENFILENAMEW unnamedParam1
); - 名前を付けてファイルを保存するダイアログを作成する。
ユーザーが「OK」ボタンを選択しダイアログを閉じると戻り値はゼロ以外になる。
その他の方法で閉じるかエラーが発生した場合はゼロが返される。
ダイアログの外観やボタンなどが若干異なりますが、基本的にGetOpenFileName関数と同じです。
使用する構造体も同じですが、一部のフラグの動作が異なります。
どちらのダイアログボックスも選択したファイルのパスを得るもので、そのファイルにどのような処理を行うかはプログラマの自由です。