DROP が 定義されていない識別子 とエラーが出るのですが・・

解決


  2004-09-24 14:45:07  No: 54539

初めまして。柚と申します。

今、VC++6の環境で、APIのみで、
ドラッグドロップに関するアプリケーションを作成しています。
それで、新規作成でWin32 Applicationを選び、標準的な"Hello World"アプリケーション
を元に機能追加したのですが、ビルド時に次のようなエラーが出ます。
    error C2065: 'HDROP' : 定義されていない識別子です。
    error C2146: 構文エラー : ';' が、識別子 'hDrop' の前に必要です。
    error C2065: 'hDrop' : 定義されていない識別子です。
    error C2065: 'DragAcceptFiles' : 定義されていない識別子です。
    error C2146: 構文エラー : ';' が、識別子 'wParam' の前に必要です。
    error C2065: 'DragQueryFile' : 定義されていない識別子です。
    error C2065: 'DragFinish' : 定義されていない識別子です。

追加した内容は
#include <windows.h>
と、
LRESULT CALLBACK WndProc
で、変数に
    HDROP hDrop;
    UINT uFileNo;
    int i;
    LPTSTR lpszFile[_MAX_PATH + 1];
と、switchで
case WM_DROPFILES:
    hDrop = (HDROP)wParam;
    uFileNo = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
    for(i=0; i<(int)uFileNo; i++){
      DragQueryFile(hDrop, i, lpszFile, sizeof(lpszFile)-1);
      MessageBox(hWnd, (LPCTSTR)lpszFile, "Dropped!", MB_ICONINFORMATION | MB_OK);
    }
    DragFinish(hDrop);
    return 0;
    break;
になります。

一体、何が問題なのか分かりますでしょうか?
また宜しければ教えて頂きたいのですが、
DragQueryFileの4つ目のパラメータで -1 とする方としない方がいらっしゃるようですが、
これは \0 を考慮してのことなのでしょうか?
実際MSDNを読んでも Size, in characters, of the lpszFile buffer. だけで、
具体的なところが分からず悩んでいます。
よろしくお願いいたします。


tetrapod  2004-09-24 17:40:43  No: 54540

D&D 関係は MFC でしか組んだことがありませんがとりあえず...

1.#include <shellapi.h> が必要です。
これは stdafx.h 中に書くべきです。windows.h は stdafx.h 中で読まれているので
xxxx.cpp 中に再度書く必要はありません(コンパイルが遅くなるだけです)

2.MSDN 文書中の DragQueryFile の説明を読むと確かに「バッファサイズ」とだけあります。
一般的にこういう場合、バッファには C の文字列 (文字の列+'\0') がセットされ・セットします。
単純にバッファの文字数を入れればよいと解釈してください。

提示のコードは未初期化のポインタを渡しているので大きく誤っています。
きちんと文字のバッファを渡したとしても sizeof(str) では
文字数ではなくバイト数を返すのでやはり誤りです。


  2004-09-24 22:32:01  No: 54541

tetrapod様  早速のお返事ありがとうございます。

> 1
MSDNで、「Handle to an internal drop structure.」だけでしたので
shellapi.h は初めて知りました。
ありがとうございます。
でも、どうすればどのヘッダの呼出が必要なんて気付くのでしょう^^;

> 2
LPTSTR lpszFile[_MAX_PATH + 1];  を
char lpszFile[_MAX_PATH + 1];  にして、エラーは解決しました。

ですが、「文字数ではなくバイト数を返すのでやはり誤り」というのが
いまいち理解できません。。
MSDNでは、「sizeof を構造体型または変数に使うと、実サイズを返します。」とあり、
文字数はcharで1byte単位なのですから
文字数 = バイト数  のように思えるのです(^^;

また、確認なのですが、
「DragQueryFile(hDrop, i, lpszFile, sizeof(lpszFile)-1);」により
  lpszFile に \0 で終端されるバッファが返る ために 4つ目のパラメータで -1 にして \0 を引く
という認識でよろしいのでしょうか。

知識浅なもので、お手数おかけいたします。


tetrapod  2004-09-24 23:04:26  No: 54542

HDROP については MSDN (日本語 CD-ROM 版) の説明が不足な気がします。
http://msdn.microsoft.com から直検索すると良いのですが...
Windows Data Types の解説中には shellapi.h で宣言されているとあります。

Internet に常時接続でないのなら VC++ のヘッダファイルを直接検索。
$(VS6)\VC98\Include や $(VS6)\VC98\MFC\Include を grep する。
VS6 のメニューからだと「ファイルから検索」を使う。

> 文字数はcharで1byte単位なのですから
これが誤りです。DragQueryFile には char* ではなく TCHAR* を渡します。
MBCS ビルドでは TCHAR は char ですが、
UNICODE ビルドすると TCHAR は wchar_t になります。
wchar_t は 1byte ではありません。
文字「数」を求めている関数に sizeof を渡すと UNICODE ビルドでは誤動作します。

>char lpszFile[_MAX_PATH + 1];  にして、エラーは解決しました。
ここは TCHAR szFile[_MAX_PATH+1]; のほうが適切です。
char lpszFile[_MAX_PATH+1]; はポインタではないので。
私はハンガリアン記述を好みません。こーいう場合に誤解の元となります。

#define elementsof(n) (sizeof(n)/sizeof((n)[0]))
と定義して
DragQueryFile(..., elementsof(szFile));
と渡すのがよさそうです。
# もっと良いのは DragQueryFile に NULL を渡して文字数を教えてもらい
# それにしたがって適切な長さのバッファを動的に取ることだと思う。

MSDN で「バッファサイズ、文字数で数える」を渡すよう指示された引数に対し
私は -1 したことはありません。
C で「文字列」というときは必ず \0 が存在するわけで、
「文字列の長さ」というなら strlen() の返す値で \0 を含まない
「バッファの大きさ」というなら \0 を含めた文字列を保持するに十分な大きさ
と個人的に解釈しています。
その辺、人によって「解釈の揺れ」があるようですが...
# 今まで誤動作したこと無いし問題ないと思う


  2004-09-25 13:32:37  No: 54543

うーん・・

> 1
やはり、Microsoftサイドで検索するしかないのですね^^;
grepというか、ファイルの全文検索は行ってみたのですけど、
結局複数のヘッダで使用されていましたし、
ドラッグドロップに関する他の方の資料を見ても載ってはいませんでしたので、
必要性や関係が分かりませんでした。
ご説明、ご丁寧にありがとうございます。

> 2
16bitCやPerlでは今まで気にはしませんでしたが、Unicodeなんですねぇ。
現在は仰る
  DragQueryFile(hDrop, i, szFile, elementsof(szFile));
で無事動作しております。
ありがとうございます。

ただ、
  > # もっと良いのは DragQueryFile に NULL を渡して文字数を教えてもらい
  > # それにしたがって適切な長さのバッファを動的に取ることだと思う。
については、気持ちはなんとなく分かるのですが、
  BuffSize = DragQueryFile(hDrop, 0, NULL, NULL);
ってことなのでしょうか(^^;

それと、MSで検索していますと
http://support.microsoft.com/default.aspx?scid=kb;en-us;185572
というサンプルに出会ったのですが、
  TCHAR szFileName[_MAX_PATH + 1];
  DragQueryFile(hdrop, nNames, (LPTSTR)szFileName, _MAX_PATH + 1);
とあり、
4つ目のパラメータで +1 は \0 を考慮してるのでしょうけど、
結局のところ
Unicodeでも _MAX_PATH + 1 に落ち着くのではないかと思いますが、いかがでしょうか。
wchar_t が 2bytes単位 として x文字あっても 2x/2=x として x になるかと。 

> 個人的に解釈
参考になります。ありがとうございます。


tetrapod  2004-09-26 18:17:24  No: 54544

>Unicodeでも _MAX_PATH + 1 に落ち着くのではないかと思いますが、いかがでしょうか。
最初からそう言ってるのですが...

Drag... には szFileName[_MAX_PATH+1] の [] 内要素数を渡す必要がある  いこーる
渡すべき数値は _MAX_PATH+1 である  いこーる
sizeof だと (_MAX_PATH+1)*2 になるので誤動作する。→
でも _MAX_PATH+1 を2度書くとバグの元
(修正時、片方だけ直してもう片方を忘れる可能性が有る)→
elementsof を使えば1度で済むからバグが減る
という論理です。

わたしはてっきり
szFileName[_MAX_PATH] が良いのか szFileName[_MAX_PATH+1] が良いのか
という議論だと思っていましたが。

>結局複数のヘッダで使用されていましたし、
でも定義している場所は1箇所です。
shellapi.h 中に DECLARE_HANDLE(HDROP); とありますよね。
他の場所は指摘のとおり使っているだけです。


  2004-09-27 00:47:08  No: 54545

> 1
Cの知識しかありませんでしたので、
DECLARE_HANDLE(HDROP); が定義とは、分かりませんでした(苦笑

> 2
  > szFileName[_MAX_PATH] が良いのか szFileName[_MAX_PATH+1] が良いのか
うーん・・
szFileNameについては、
最初は _MAX_PATH がファイルまでのパスの長さ なので +1 でいいのだと思ってました。
ただ、悩んでいたのは、 sizeof(lpszFile)-1 のほうで^^;
人のサンプルを探っていますと -1 していたり(ex1) していなかったり(ex2) で、
  ex1. http://icp.hicorp.co.jp/letter/p_mame_c20205.html
  ex2. http://www.kumei.ne.jp/c_lang/sdk2/sdk_105.htm
その理由が分からず、どちらが正しい? としか疑問を抱かなかったのです。
この時点では、とりあえず sizeof() は使うんだなーとも思っていました。

それで、お話を聞くうちに、
Unicodeを考えなくてはいけない ということが発覚し^^;
  >>  Unicodeでも _MAX_PATH + 1 に落ち着くのでは
と言いましたのは、考え整理のための確認でした。

丁寧にご回答いただいて恐縮なのですが、誤解を生んだようで失礼いたしました。

今の状況は
試作のサンプルが動作しているので少し安心はしているのですが、
  >> BuffSize = DragQueryFile(hDrop, 0, NULL, NULL);
が、正しいかどうかが分からないのが少し気になっているだけです^^;

ご回答ありがとうございました。


tetrapod  2004-09-27 23:24:58  No: 54546

BuffSize = DragQueryFile(hDrop, 0, NULL, NULL);
は、それだけではダメでしょう。これでは最初の1つだけしか処理されません。

UINT nFiles=DragQueryFile(h, -1, NULL, 0);
for (UINT n=0; n<nFiles; ++n) {
  UINT nFNbufsize=DragQueryFile(h, n, NULL, 0)+1;
  TCHAR* pFN=new TCHAR[nFNbufsize];
  DragQueryFile(h, n, pFN, nFNbufsize);
  ...
}
とか。

提示のページは UNICODE ビルドを考察していないか、または
説明のためにあえて無視しているか、どちらかでしょう。
# 猫...はあえて無視していると信じたい。
文字数が必要なところに単純 sizeof buf を使うのはバグであり、個人的には0点です。
http://www.runan.net/program/tips/API/shell32_DragQueryFile.shtml
は sizeof を使ってないあたりは良いのだけど char なのが減点ですね。


  2004-09-28 16:29:25  No: 54547

返信ありがとうございます。

そうですね^^;
最初、各々については考えてはいなかったです(恥
> TCHAR* pFN=new TCHAR[nFNbufsize];
> DragQueryFile(h, n, pFN, nFNbufsize);
については、大変興味深いテクニックで、参考になりました。
実際ドロップするファイル数が多いと、メモリの上で効率的かもしれませんね。

ところで、[解決]してからで申し訳ないのですけど、
後からバッファについて考えることできました。
それは wsprintf() や MessageBox() なのですが、
Unicodeを考慮すると、実際バッファに割り当てるべき(最低限の)サイズは、どう決めればいいのかと^^;

http://www.microsoft.com/JAPAN/developer/library/jpuipf/_win32_wsprintf.htm
を見ますと、「バッファの最大サイズは、1,024 バイト」ということから
  →Max: char buf[1024]
バイトサイズで確保するのが分かるのですけど、
charならば1byte単位で求められますが、
  →半角10文字: char buf[11]
  →全角10文字; char buf[21]
Unicodeとなりますと
http://e-words.jp/w/Unicode.html
のように、2bytes-4bytesで変動するため
> TCHAR szFile[_MAX_PATH+1];
で足りるのか疑問に^^;
つまり、文字数で考えれば配列数は足りるが、バイトサイズで確保ならば *4 にする必要あり?
って、考えてしまいます(汗
TCHARは例外なのでしょうか...。


tetrapod  2004-09-28 17:24:54  No: 54548

VC++6 は wchar_t を 2byte として実装しています。
他の処理系 (cygwin-gcc 等) では 4byte なものもあります。
とりあえず VC++6 で wchar_t が 4 になることはないです。

http://support.microsoft.com/default.aspx?scid=kb;en-us;77255
によれば「wsprintf が取り扱える出力先の長さは1KBである」そうです。
これは文字のエンコーディグに関係ない。
よって char であれば 1023 文字まで wchar_t が 2byte であれば 511 文字まで、
しか、 wsprintf では取り扱うことはできないということになりそうです。

文字長を気にするのであれば C-Runtime Library のほうの書式化関数
_sntprintf (_snprintf/_snwprintf) を使うほうが良いとありますね。

>> TCHAR szFile[_MAX_PATH+1];
>で足りるのか疑問に^^;
いやだからこれは Drag... に渡すときに必要な長さなのであって、
書式化とは無関係な話です。

書式化の際に長すぎるファイル名は最初と最後を取り出して表示とか、
そーいう話は Drag... 系関数の知ったことではありませんし。


RAPT  2004-09-29 06:56:23  No: 54549

TCHAR filename[_MAX_PATH+1];
size_t length = sizeof(filename)/sizeof(filename[0]);
とすれば、suzeof(TCHAR) がいくつだろうと、バッファに格納できる文字列長
は定まります。

defineで定義されている値は「定数」なので、その定数値を要求しているAPI
について、なんで?  と言われてもどうしようもありません。仕様なんですから。


  URL  2004-09-29 17:03:58  No: 54550

tetrapod様、RAPT様 お返事ありがとうございます。

考えが混同してしまったようで、皆様の指摘のおかけで氷解しました。
また、VC++6でwchar_tが2bytesだと判明して、とても役立ちます。
ありがとうございました。

[スッキリ]


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加