概要

ファイルを選択するコモンダイアログを使用しファイル名を取得するプログラムです。
ファイルを選択するコモンダイアログはGetOpenFileName APIを呼び出すことにより表示できます。
GetOpenFileName APIは、動作を指定するためにOPENFILENAME 構造体を初期化する必要がありますが、煩雑なので、GetFileName関数を作成して、親ウィンドウのハンドル、ファイル名の格納するバッファのポインタ、バッファのサイズ、初期フォルダーを指定できるようにしています。

上図はWindows 7で実行した状態です。
以下、動作を説明する図はWindows 7で実行した状態を表しています。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。
GetFileName関数を呼び出してファイル名を取得します。

GetFileName

OPENFILENAME構造体はメンバーが多いためZeroMemory APIで全部クリアしてから必要なメンバーのみ設定します。
OPENFILENAME構造体の主要メンバーは以下の通りです。

lpstrFilterメンバー

ファイル名のフィルタを設定するために使用します。
フィルタは下図のようにコンボボックスに表示され選択できます。
表示される文字\0ワイルカード\0を繰り返して設定します。
\0\0が2回続くとフィルタが終了したと認識されます。
下図のように拡張子txtとすべてのファイルを対象にする場合、次のように記述します。


"TXTファイル(*.TXT)\0*.TXT\0全てのファイル(*.*)\0*.*\0"
文字列の最後にコンパイラが文字の終了を表す\0を自動的に付加するのでソースコード上では\0は1個のみ記述しています。
また、フィルタごとに以下のように分割して記述すると読みやすいプログラムになると思います。

 _TEXT("TXTファイル(*.TXT)\0*.TXT\0") _TEXT("全てのファイル(*.*)\0*.*\0");
また例えば*.txtと*.logを同時にフィルタに適用したい場合は、;で区切ります。
_TEXT("TXTファイル(*.TXT *.LOG)\0*.TXT;*.LOG\0") _TEXT("全てのファイル(*.*)\0*.*\0"); NULLを指定した場合、フィルタが適用されず、フィルタを選択するコンボボックスが表示されなくなります。

lpstrDefExtメンバー

標準拡張子のバッファへのポインタを指定します。 ユーザーが拡張子を入力しなかった場合、 ファイル名にこの拡張子を追加します。
先端の3文字だけが追加されます。 この文字列にピリオド[.]は必要ありません。

nFilterIndexメンバー

lpstrFilterメンバーのうち何個目を適用するか指定します。1を指定すると最初のフィルタが適用されます。2を指定すると2個目のフィルタが適用されます。
0を指定するとlpCustomFilterメンバーのフィルタが適用されます。

lpstrTitleメンバー

ダイアログボックスのタイトル文字列を指定します。

lStructSizeメンバー

構造体のサイズを指定します。
ANSI版の場合バイト数、UNICODE版の場合文字数を指定します。

lpstrFileメンバー

選択されたファイルのフルパスを保存するバッファのアドレスを指定します。
初期ファイル名を指定する場合は、このバッファにファイル名を指定します。
初期ファイル名を使用しない場合は、バッファの先頭に0を書き込んでおきます。

nMaxFileメンバー

選択されたファイルのフルパスを保存するバッファのサイズを指定します。
ANSI版の場合バイト数、UNICODE版の場合文字数を指定します。

lpstrInitialDirメンバー

初期フォルダー名を指定します。

lpstrFileTitleメンバー

選択されたファイルのファイル名が保存されます。
例えばc:\test\good.txtというファイルが選択された場合は、good.txtが保存されます。

hwndOwnerメンバー

ダイアログボックスの親ウィンドウを指定します。親ウィンドウがない場合はNULLを指定します。

nFileExtension

取得されたファイルの拡張子先頭の位置を返します。

Flagsメンバー

以下の定数を組み合わせてコモンダイアログボックスの動作を指定します。
定数は、ヘッダーファイルはcommdlg.hで定義されています。
OFN_ALLOWMULTISELECT
複数のファイルを選択できるようにします。
OFN_EXPLORERフラグを指定しない場合
このフラグを指定してOFN_EXPLORERフラグを指定しないと下図のようなオールドスタイルで表示されます。
昔のWindowsでよく見たスタイルです。

取得されるファイル名は、以下のように空白で区切られます。
nFileOffsetメンバーは、a.txtの先頭を示します。

c:¥ a.txt b.txt c.txt
OFN_EXPLORERフラグを指定した場合
通常のスタイルで表示されます。取得されるファイル名は、以下のように¥0で区切られます。
nFileOffsetメンバーは、a.txtの先頭を示します。

c:¥¥0a.txt¥0b.txt¥0c.txt¥0¥0
OFN_CREATEPROMPT
ユーザーが存在しないファイルを選択しようとしたとき、 確認ダイアログを表示します。
OFN_EXTENSIONDIFFERENT
ダイアログボックスが終了した時、FlagsメンバーとOFN_EXTENSIONDIFFERENTのAND演算をした結果が0でない場合、フィルタで指定された拡張子と異なる拡張子であることを示します。
OFN_FILEMUSTEXIST
ユーザーが存在するファイルしか選択できないようにします。エディットボックスに直接ファイル名を入力したときのチェックに役立ちます。
OFN_HIDEREADONLY
リートオンリーを非表示になります。
このフラグを指定しない場合は、下図のように読み取り専用ファイルとして開くが有効となります。

このフラグを指定すると、下図のように読み取り専用ファイルとして開くが無効となります。
OFN_NODEREFERENCELINKS
ショットカット(*.lnk)を選択すると通常はショットカットのリンク先へ移動しますが、このフラグを指定するとショートカットファイル(*.lnk)自体を取得することができます。
OFN_READONLY
このフラグを指定すると、下図のように読み取り専用が標準状態となります。

プログラムソース


// オープンファイルコモンダイアログを使用してファイル名を取得
// Visual C++ 2013 32/64bit

#include <windows.h>
#include <tchar.h>

// ファイル名を指定するコモンダイアログを表示
BOOL GetFileName(HWND hWnd, TCHAR* fname, int sz, TCHAR* initDir = 0);

// 最初に呼び出される関数

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, TCHAR* lpsCmdLine, int nCmdShow){
        TCHAR szFile[MAX_PATH*256];
        if (GetFileName(0, szFile, sizeof(szFile) / sizeof(TCHAR), _TEXT("C:\\")))
                MessageBox(0, szFile, _TEXT("選択されたファイル名"), MB_OK);
        return 0;
}

// ファイル名を指定するコモンダイアログを表示

BOOL GetFileName(HWND hWnd, TCHAR* fname, int sz, TCHAR* initDir){
        OPENFILENAME o;
        fname[0] = _T('\0');
        ZeroMemory(&o, sizeof(o));
        o.lStructSize = sizeof(o);              //      構造体サイズ
        o.hwndOwner = hWnd;                             //      親ウィンドウのハンドル
        o.lpstrInitialDir = initDir;    //      初期フォルダー
        o.lpstrFile = fname;                    //      取得したファイル名を保存するバッファ
        o.nMaxFile = sz;                                //      取得したファイル名を保存するバッファサイズ
        o.lpstrFilter = _TEXT("TXTファイル(*.TXT)\0*.TXT\0") _TEXT("全てのファイル(*.*)\0*.*\0");
        o.lpstrDefExt = _TEXT("TXT");
        o.lpstrTitle = _TEXT("テキストファイルを指定");
        o.nFilterIndex = 1;
        return GetOpenFileName(&o);
}

ソースファイルと実行ファイルのダウンロード