« 下の文字化け記事 | トップページ | 渦中の人 »

2006.01.20

今日のハマリ

家の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;


おお、見事にインデントが消えておる。

|

« 下の文字化け記事 | トップページ | 渦中の人 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/65113/8246363

この記事へのトラックバック一覧です: 今日のハマリ:

» GetOpenFileName() [Navigation Log - なびろぐ -]
GetOpenFileName() 。 nFileExtensionが0かどうかで判定するのは正しくない。 MSDN - OPENFILENAME Structure によると、nFileExtensionの項に「If the user... [続きを読む]

受信: 2006.01.24 12:03

« 下の文字化け記事 | トップページ | 渦中の人 »