初めて投稿させていただきます。Janと申します。
環境はWindows2000 SP3、Visual Studio6 SP5、SDKです。
現在、私は「ファイルを開くダイアログ」のプログラムをしておりまして、
ファイルを複数選択できるようにするために、文字列バッファサイズフリーな
プログラムを考えています。
参考になるものはないかと思いネットで検索してみたところ、マイクロソフトの
KnowledgeBaseにそれらしきものがあり、それを真似て(というかほぼそのまま)
作成したみたのですが、CommDlgExtendedError()よりFNERR_BUFFERTOOSMALLの
エラーが出てしまいます。
最初は自分のプログラムが間違ってるのかと思っていろいろ修正したり試してみたり
したのですが、どれもかわりはありませんでした。
もしかしてこのサンプルでは無理なのでしょうか?
どなたか情報をいただけたらと思い、確認の意味で投稿させていただきます。
もし、これが駄目なら他の方法でいこうと思っているのですが、自分が悪いのか
サンプルが駄目なのか白黒はっきりさせたいと思いまして・・・・
参考にしたところ
http://support.microsoft.com/default.aspx?scid=kb;en-us;131462
とりあえず移植したコードを記載します。
void CFileDialog::CheckStrBuf(LPOPENFILENAME lpOFN)
{
UINT nFile = ::SendMessage(GetDlgItem(GetParent(), edt1),
WM_GETTEXTLENGTH, NULL, NULL);
UINT nPath = ::SendMessage(GetParent(), CDM_GETFOLDERPATH, NULL,
NULL);
UINT Necessarybuf = nFile + nPath + 3;
if(ofn.nMaxFile < Necessarybuf ){
//一旦、解放
HeapFree(GetProcessHeap(), 0, szFileName);
//新たに必要なだけ確保
szFileName = (LPTSTR)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, Necessarybuf);
if(szFileName){
ofn.lpstrFile = szFileName;
ofn.nMaxFile = Necessarybuf;
}
else{
MessageBox(NULL, 文字列バッファのメモリ確保に失敗しま
した,エラー,MB_OK | MB_ICONWARNING);
}
}
}
'CFileDialog'は自作したクラス名になります。
'GetParent()'はpublicのインラインメンバ関数で、中で'::GetParent(hWndDlg)'としてあり
ます。
'ofn'はそのクラスのprotectedメンバ、'szFileName'は同じくprotectedメンバでLPTSTR型で
す。
サンプルではSetProp、GetPropでLPOPENFILENAMEを保存してありますが、メンバ変数として保
持
しているので必要ないと思います。(ちなみに使用しても変化はありませんでした。)
CommDlg_OpensSave_GetSpec()は、文字数しか得られないので、edt1からWM_GETTEXTLENGTH
で
必要なバイト数を取得しています。
どうかよろしくお願いいたします。
長文失礼しました。
補足です。
'szFileName'はコンストラクタで
szFileName = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
としてあります。
記載している関数は、サンプルと同じくCDN_SELCHANGEメッセージ内から呼び出しております。
CommDlgExtendedError()でFNERR_BUFFERTOOSMALLが
出たときはlpstrFileに必要なバッファのサイズが格納
されるようなのでメモリダンプなりをしてみて確保(期待)
した値と比べてみてはどうでしょう。
MSDN OPENFILENAME:lpstrFile
>If the buffer is too small, the function returns FALSE and the
>CommDlgExtendedError function returns FNERR_BUFFERTOOSMALL.
>In this case, the first two bytes of the lpstrFile buffer
>contain the required size, in bytes or characters.
あとは
>szFileName = (LPTSTR)HeapAlloc(GetProcessHeap(),
>HEAP_ZERO_MEMORY, Necessarybuf);
のNecessarybufに 10000とかを入れてみて動作するかを
確認してみるといいかもしれません。
>if(dlg.m_ofn.nMaxFile < Necessarybuf ){
> 省略
>}
の部分ですが、こちらで試したところうまく動作しました。
(確保するサイズについてはは直に数値を入れてみました)
mfcのCFileDialogで試したので環境が違うのですけど。。
>けみ様
レスありがとうございます。
わざわざ試していただきまして申し訳ありません。
>10000とかを入れてみて動作するかを確認してみるといいかもしれません
これに関してはわざと長いファイル名を作成してそれを大量にコピーして試していて、
10000、20000という量を確保させてみているのですが、アサーションエラーダイアログや
デバッグモードで動かした際などにAccessViolationしていないので正常に確保できて
いると思いますが・・・・(_heapchk()関数などでも試してみました。)
>lpstrFileの始めの2バイトに必要なサイズを格納しています。
知ってはいたのですが、文字で格納されるのかと思ったら違うみたいで、取り出し方が
わからないで悩んでおります・・・(^^;A
例えばトンでもないことをしているかもしれませんが、
int ResBuff = (int)ofn.lpstrFile;
wsprintf(str, 必要なバッファは%dです,ResBuff);
MessageBox(NULL, str,バッファサイズ, MB_OK);
などして試してみてはいるのですが、147万とかいう数字が出てきてしまって・・・・
DWORD ResBuff;
// nMaxFileがDWORD型(unsigned)なので同じunsigned型の変数を用意。
WORD* pWord = ( WORD* )ofn.lpstrFile;
// ofn.lpstrFileをまず「2バイトの大きさを持つunsigned型変数(例:WORD)」
// を指すポインタ(WORD*)にキャストして、
ResBuff = *pWord;
// 続いてそのポインタが指す内容を代入すると、先頭から2バイト分が
// コピーされる。
これで多分必要な情報が取れると思います。
int *ResBuff= (int*)ofn.lpstrFile;
wsprintf(str, 必要なバッファは%dです,ResBuff);
MessageBox(NULL, str,バッファサイズ, MB_OK);
ではいかがでしょう。
私はウォッチウィンドウ(alt + 3)に
*ofn.lpstrFile,d
を追加して確認してました。
あ、nさんのおっしゃるとおりです。
(2バイトでunsignedですね)
更新かけないで登録してしまいました。
すいません。
>けみ様、n様
お二人様からアドバイスしていただけてとてもうれしいです!!
本当にありがとうございます!!
早速やってみた所、lpstrFileの中身を取り出せました!!
しかし、少ない文字数の場合はきちんと必要な総バイト数を格納してくれるのですが、
徐々に増やしていったところ、281バイト以上は全て281バイトとして格納されておりまし
た。
もしこれが仕様ならばあまりにも少ないような・・・・
>私はウォッチウィンドウ(alt + 3)に
>*ofn.lpstrFile,d
>を追加して確認してました。
私もウォッチウィンドウは値の確認に良く使用しておりますが、こんな方法があるなんて
初めて知りました。
'd'ということは10進表示なんでしょうか?
試してみたのですが、なんかとんでもない桁(12桁)の数字が・・・・
これは何の数字を表すのでしょうか?確保されたバイト数にしても大きすぎますし、
文字列はまだ格納しておりませんし・・・・
*が抜けてませんか?
*ofn.lpstrFile,u が正しいのかな。
詳しくはMSDN ウォッチ ウィンドウ, 書式指定記号で
確認してみてください。
>徐々に増やしていったところ、281バイト以上は全て281バイト
>として格納されておりました。
私のところでもそうでした。原因ははよくわからないです。
で、よく考えてみると、
DWORD nMaxFile; となっているので頑張っても複数ファイルのときって
ファイル名の全文字数が0xffff(64KB)文字しか扱えないのかなぁと。。
いっそのこと初期化のときにこの値で確保してしまうのが楽なのかも。
>ファイルを複数選択できるようにするために、文字列バッファサイズフリーな
>プログラムを考えています。
この方法ではだめそうですね。全然お役に立てませんでした。
申し訳ないです。
>けみ様
早速MSDNで調べてみました。
ウォッチウィンドウでこんな細かいことが指定できるとは・・・
これでデバッグがまた楽しくなる・・・?(笑
あれからいろいろと試行錯誤してこの方法で何とかやっては見たのですが無理そうでした。
この方法はあきらめようかと思います。
>この方法ではだめそうですね。全然お役に立てませんでした。
>申し訳ないです。
とんでもございません。
こちらこそ、わざわざ試していただいたりして本当にありがとうございます!!
この方法では無理だということがわかった事だけでもすっきりしました。
これで心おきなくも一つの方法へ移れます。
>けみ様、n様
お付き合いして頂いて本当にありがとうございました!!
まだまだ未熟なものでこれからもお知恵を借りることがあるかと思いますが、そのときは
またよろしくお願いいたします。
それにしても、マイクロソフトもちゃんと動作確認してから記載しているのでしょうか?
確かに、可能ならばこの方法が一番いいかと思いますができなくて本当にがっかりです。
> 環境はWindows2000 SP3、Visual Studio6 SP5、SDKです。
> CommDlg_OpensSave_GetSpec()は、文字数しか得られないので、edt1から
> WM_GETTEXTLENGTH で必要なバイト数を取得しています。
から、非 UNICODE でプログラミングされたものと推察しました。この場合
FNERR_BUFFERTOOSMALL になるのは正常な動作です。
> 参考にしたところ
> http://support.microsoft.com/default.aspx?scid=kb;en-us;131462
には、NT系でこの手法が使えることのは UNICODE のときのみであることが
ちゃんと書かれています。
# 95系でバッファー乗せ換えができるのは知っていましたが、NT + UNICODE
# でも可能でとは知りませんでした。情報ありがとうございました。
>しっぽ様
ご指摘ありがとうございます。
一番最後にきちんと記されてありましたね・・・・お恥ずかしい。
文字列はまだ詳しくわからないのですが、要するにこれが可能になったとしても
日本語環境(2バイト文字の環境)でしか正常に動作しないということなのでしょうか?
今、試しに'NecessaryBuf'を単純にx2してみても駄目だったのですが、そういう
問題でもなさそうですね・・・
> 日本語環境(2バイト文字の環境)でしか正常に動作しないということなのでしょうか?
まずは UNICODE でプログラムしてみれば分かることと思います。
UNICODE, wWinMain を調べてください。
問題としては 95系 では動作しないプログラムとなります。
> 今、試しに'NecessaryBuf'を単純にx2してみても駄目だったのですが、そういう
非UNICODE + NT では ::GetOpenFileName で渡された OPENFIELNAME を
内部でコピーしてしているように見えます。よって「駄目だった」のは正常動作と
思います。
>しっぽ様
返信遅くなりまして申し訳ございません。
>非UNICODE + NT では ::GetOpenFileName で渡された OPENFIELNAME を
>内部でコピーしてしているように見えます。
やはりしっぽ様もそう考えてらっしゃいますか。
自分も、いくら途中から値を変えても変化がないことからもしかしたらそう
なのではないか、と考えていましたが、これではっきりしました。
私としましては、このことはすばらしい情報です。
どうもありがとうございます。
UNICODEプログラムの件に関しては少し調べ、勉強してみようかと思っております。
とりあえず、今後は、別の方法で行こうと決意しましたので、このサンプルについては
解決ということにします。
今までどうもありがとうございました。