今日のハマリ
家のCATV経由ネット環境が障害発生中でブログ更新を怠っている俺だが、このシリーズは書かねば。
Windowsの「ファイルを開く」コモンダイアログはGetOpenFileName()というAPIを使う。
このAPIのパラメータはOPENFILENAME構造体へのポインタのみという事で、ダイアログ表示だとか選択されたファイルに関する情報等は、この構造体に設定する事になる。
俺の記憶では、昔はこのダイアログで選択できるファイルは1度に1個ダケだったと思うのだが、最近は構造体のFlagsメンバに“OFN_ALLOWMULTISELECT”を追加する事で、複数ファイル選択可能となる。
だからとと言って、選択されたファイルを返す領域は相変わらず1個なので、以下のような形式のデータとして返ってくる。
(構造体のFlagsメンバに“OFN_EXPLORER”を追加した場合の形式)
“フォルダパス\0選択ファイル1\0選択ファイル2\0...\0選択ファイルn\0\0”
この形式はWindowsAPIでは「よくある形式」だ。
で、ここからがハマリ。
上記の形式は「あくまでも複数選択した場合」である。
構造体のFlagsメンバに“OFN_ALLOWMULTISELECT”が追加してあっても、選択されたファイルが1件なら従来の形式で選択ファイル情報が返ってくる。
従って、選択ファイル数で処理を分けなければならないが、返ってくる情報として「選択ファイル数」というモノが無い。
どうするかというと、構造体のnFileExtensionメンバを見るのだ。
nFileExtensionメンバは選択されたファイル名で、拡張子が始まる位置を指している。
で、複数選択された時は「必ず値が0」になっている。
という事で、「1件選択したダケだと不正なファイルパスを生成してしまう」という異常はクリア。
そんなこんなでユニット試験を継続していると新たな異常が。
フォルダ配下のファイルを複数選択した場合は問題ないが、ルートでファイルを複数選択した場合に異常が発生するのだ。
つー事で調べた所、上記の書式の“フォルダパス”がルートとフォルダ配下で形式が微妙に異なるのだ。
例えば、Cドライブ直下(ルート)のファイルを選択した場合は“C:\”という形式になるが、Cドライブの“hoge”フォルダ配下のファイルを選択した場合は“C:\hoge”と返ってくる。
つまり、ルートの時は末尾に“\”が付くが、フォルダの時は付かないのである。
くそたわけっ!(名古屋弁)
“\”が無いものとしてパス名を生成していたので、“C:\\fuga.txt”みたいな値になって異常ってワケだ。
UNIX系OSの場合、パス区切り文字(“\”とか“/”の事)が連続していた場合1個とみなしてくれるが、Windowsは厳しいようだ。
まぁ、Windowsの場合パスの“\\”に特別な意味も有るしな。
で。
まぁ、これくらいはPathRemoveBackslash()APIでフィルタしてしまえばOKなんて軽く考えたらハマリまスた。
ナント、PathRemoveBackslash()APIはパラメータとして渡されたパスがルートの時は末尾のバックスラッシュ(日本の場合は“\”)を削除してくれない。
でらーくそたわけだでかんわー!(名古屋弁)
つー事で、PathIsRoot()APIを使ってパス名生成方法を切り替える事になった。
なんかさー、仕様が腐ってないか色々。
OPENFILENAME ofn;
TCHAR szPath[MAX_PATH];
PTSTR pszPath;
size_t nLen;
BOOL bRes = FALSE;
nLen = MAX_PATH * MAX_PATH;
pszPath = LocalAlloc( LPTR, nLen * sizeof(TCHAR) );
if ( pszPath == NULL ) {
pszPath = szPath;
nLen = MAX_PATH;
}
ZeroMemory( pszPath, nLen );
ZeroMemory( &ofn, sizeof(OPENFILENAME) );
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWnd;
ofn.lpstrFilter = "*.*\0*.*\0";
ofn.lpstrCustomFilter = NULL;
ofn.lpstrFile = pszPath;
ofn.nMaxFile = nLen;
ofn.lpstrTitle = "ファイルの追加";
ofn.Flags = OFN_FILEMUSTEXIST
| OFN_HIDEREADONLY
| OFN_PATHMUSTEXIST
| OFN_EXPLORER;
ofn.lpstrDefExt = "";
if ( pszPath != szPath ) {
ofn.Flags |= OFN_ALLOWMULTISELECT;
}
if ( GetOpenFileName(&ofn) == TRUE ) {
if ( pszPath != szPath && ofn.nFileExtension == 0 ) {
PTSTR pszName = &pszPath[ofn.nFileOffset];
PathRemoveBackslash( pszPath ); // 要らないと思うが念のため
while ( *pszName != '\0' ) {
if ( PathIsRoot(pszPath) == FALSE ) {
StringCbPrintf( szPath, MAX_PATH, "%s\\%s", pszPath, pszName );
} else {
StringCbPrintf( szPath, MAX_PATH, "%s%s", pszPath, pszName );
}
やりたい処理( szPath );
pszName += (lstrlen(pszName) + 1); // to next filename
}
} else {
やりたい処理( szPath );
}
bRes = TRUE;
}
if ( pszPath != szPath ) {
LocalFree( pszPath );
}
return bRes;
おお、見事にインデントが消えておる。
| 固定リンク
« 下の文字化け記事 | トップページ | 渦中の人 »


コメント