WM_DROPFILEメッセージの引数の構造体は?

解決


ともみ  2004-08-07 13:02:42  No: 84997

ウィンドウに画像ファイルをエクスプローラ等からドラッグ&ドロップすると
サムネイルを表示するアプリケーションがあります。

今VBで作っているアプリケーションから画像ファイルの一覧を表示して
ドラッグ&ドロップする機能は実装できたのですが、実際にドラッグ&ドロップ
しないでも外部アプリケーションにドラッグ&ドロップしたような機能を追加
したいと思います。
WM_DROPFILESメッセージをターゲットのウィンドウにSendMessageで送れば良い
ということまで分かりましたが引数wParamの内部ドロップ構造体のハンドルを
どのように処理すればよいのかが分かりません。

質問は内部ドロップ構造体とはどんな構造体なのか?ということです。

ご存知の方がいらっしゃったらご教授願います。
よろしくお願いします。

開発環境 VB6.0 SP5
OS Windows XP


名前  2004-08-07 16:13:49  No: 84998

DROPFILES Structure
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/dropfiles.asp
だと思います。


ともみ  2004-08-08 01:02:44  No: 84999

名前さん回答ありがとうございます。
さっそくリンク先のページを参考にして処理を組んでみましたが、思うように動作しませんでした。
何か解釈を誤っていると思うのですが。作った処理は以下のとおりです。

DROPFILES構造体はC表記で

typedef struct _DROPFILES {
    DWORD pFiles;
    POINT pt;
    BOOL fNC;
    BOOL fWide;
} DROPFILES, *LPDROPFILES;

でした。そこでVBで以下のように宣言し

Private Type POINTAPI
  x As Long
  y As Long
End Type

Private Type DROPFILES
  pFiles As Long
  pt     As POINTAPI
  fNC    As Boolean
  fWide  As Boolean
End Type

下記処理でメッセージを送ってみました。ターゲットのウィンドウハンドルは今はSpy++で探して直接指定しています。

  Dim stDrop    As DROPFILES
  Dim lngCnt    As Long
  Dim i         As Long
  Dim strFiles  As String
  Dim lngHDROP  As Long
  Dim lngPDROP  As Long
  Dim strFile() As String
  
  stDrop.pt.x = 20              'クライアント領域のドロップする座標を指定
  stDrop.pt.y = 20
  stDrop.fNC = False            'ptはターゲットウィンドウのクライアント領域の座標を指示
  stDrop.fWide = True           'ファイル名はUnicode文字列
  stDrop.pFiles = 4& + 8& + 2&  'ファイル名の先頭領域のオフセット
  lngCnt = ImPrv.GetSelectedPictures(strFile) 'strFile()にはファイルの一覧がセットされる
  If lngCnt = 0 Then Exit Sub   'ファイルが何も取得されていなければ終了
  
  For i = 0 To lngCnt - 1
    strFiles = strFile(i) & ChrB$(0)  'ファイル名の最後にNull文字を追加してstrFilesに足してゆく
  Next
  strFiles = strFiles & ChrB$(0)      '全てのファイルの最後にさらにNull文字を追加
  
  lngHDROP = GlobalAlloc(GHND, stDrop.pFiles + LenB(strFiles))  'DROPFILES構造体の領域確保
  lngPDROP = GlobalLock(lngHDROP)
  
  'DROPFILESのヘッダー部をコピー
  CopyMemory ByVal lngPDROP, stDrop, stDrop.pFiles
  'ファイルリストの部分をコピー
  CopyMemory ByVal (lngPDROP + stDrop.pFiles), ByVal strFiles, LenB(strFiles)
  GlobalUnlock lngPDROP
  
  'ターゲットウィンドウにメッセージを投げる
  SendMessage &H1480510, WM_DROPFILES, lngHDROP, ByVal 0&

しかし、ターゲットウィンドウには何も変化がなく、ターゲットウィンドウがまずいのか
それともDROPFILES構造体の記述が悪いのか切り分けができていません。

そこで上記処理中でDROPFILES構造体を作成していく上で何か間違っているところがないのかを
考えています。

考えられる点は

1.pFiles(Offset of the file list from the beginning of this structure, in bytesと書いてありました)
    の値が14で良いのか?Booleanは1バイト?
2.ファイル名一覧の作り方が正しいのか?(ファイル名の区切りは1バイトのヌルキャラで良いのか?)
3.Unicode文字の扱い方は正しいのか?

だと思うのですが、何かお気付きの点があればご指摘ください。
長文になりましたが、よろしくお願いします。


ともみ  2004-08-08 01:13:19  No: 85000

記述ミスがありました
>  GlobalUnlock lngPDROP
GloablUnlock lngHDROP
です
よろしくお願いします。


魔界の仮面弁士  2004-08-08 04:02:26  No: 85001

ざっと見て、気になった点だけ。

>    fNC    As Boolean
>    BOOL fNC;

VB6 の Boolean 型は 16bit で、False = 0、True = -1 です。
一方の    BOOL 型は 32bit で、FALSE = 0、TRUE = +1 です。

この場合は、As Long で宣言しておいた方が妥当でしょう。

# 蛇足ですが、BOOLEAN 型を VB にマッピングする時は、
# As Byte で書く事になります。( BOOLEAN は 1 バイト)


ともみ  2004-08-08 06:50:18  No: 85002

魔界の仮面弁士さん、ご指摘ありがとうございます。
CのBOOL型の長さは4バイトだったのですね。

Private Type DROPFILES
  pFiles As Long
  pt     As POINTAPI
  fNC    As Long
  fWide  As Long
End Type
として
stDrop.pFiles = 4& + 8& + 4& + 4&
にFalseは0、Trueは1に修正しました。
しかし、状況は変わりませんでした。
これ以外にもなにかおかしいところがあるようです。

もう少し調べてみます。


hatahata  2004-08-08 20:14:08  No: 85003

私もファイルドロップをVBで実現したいのですが、ずっと正解のコードが得られず困っています。
できましたら、この場でVBでのサンプルコードをご教授頂ければと願っています。ともみさんのコードをあとどう直せば実現できるのか知りたいです。

参考)過去の質問です↓
http://madia.world.coocan.jp/cgi-bin/VBBBS/wwwlng.cgi?print+200406/04060067.txt

私の場合、C++にもメモリ操作に知識が乏しい為、C++のサンプルを見つけても解読できませんでした。
C++ではこのようなコードになるそうです。
http://zidney.define.jp/programing/wm/wm_dropfiles/
より引用させて頂きました。
=========================================================
const char *FileNameList[]={
  "c:\\autoexec.bat",
  "c:\\config.sys",
  NULL,
};

// ファイル名の記述に必要なメモリ量を計算
int size=0;
for(int i=0;FileNameList[i];i++)
  size+=strlen(FileNameList[i])+1;
size++;
HANDLE hMem=GlobalAlloc(GMEM_ZEROINIT,sizeof(DROPFILES)+size);
char *p=(char *)GlobalLock(hMem);
DROPFILES *DropFiles=(DROPFILES *)p;
DropFiles->pFiles=sizeof(*DropFiles);
DropFiles->pt.x=10;
DropFiles->pt.y=10;
DropFiles->fNC=1;
DropFiles->fWide=0;
p+=sizeof(*DropFiles);
for(int i=0;FileNameList[i];i++)
  strcpy(p,FileNameList[i]);
  p+=strlen(p)+1;
}
*p='\0';
GlobalUnlock(hMem);
::PostMessage(hWnd,WM_DROPFILES,(WPARAM)hMem,0);
=========================================================

また、他のページにての情報ですが、SendMessageではうまく行かずPostMessageだと正常に動作するという話もありました。

横から乗っかり質問ですみません。


ともみ  2004-08-09 00:06:22  No: 85004

hatahataさん、サンプルの提供ありがとうございました。
そこから、最初のコードではUnicode文字を使用する場合は終端文字もNull1バイトではないという
ことが分かりました。
それを修正して、とりあえずメモ帳をターゲットとしてファイルをドロップすることは成功しました。
以下に最初のコードを修正したものを記します。

'構造体定義
Private Type POINTAPI
        x As Long
        y As Long
End Type

Private Type DROPFILES
  pFiles    As Long
  pt        As POINTAPI
  fNC       As Long
  fWide     As Long
End Type

'ドロップ処理
Private Sub mnuDropFiles_Click()

  Dim stDrop    As DROPFILES
  Dim lngCnt    As Long
  Dim i         As Long
  Dim lngHDROP  As Long
  Dim lngPDROP  As Long
  Dim bytD()    As Byte
  Dim lngSize   As Long
  Dim strFile() As String       'ドロップするファイルパスの配列
  
  'strFile()にはファイルの一覧がlngCntには取得した配列の大きさがセットされる
  lngCnt = ImPrv.GetSelectedPictures(strFile)
  If lngCnt = 0 Then Exit Sub       'ファイルが何も取得されていなければ終了
  
  stDrop.pt.x = 10                  'クライアント領域のドロップする座標をセット
  stDrop.pt.y = 10
  stDrop.fNC = 0                    'ptはクライアント領域の座標を指示
  stDrop.fWide = 1                  'ファイル名はUnicode文字列
  stDrop.pFiles = 4& + 8& + 4& + 4& 'ファイル名の先頭領域のオフセット
  
  For i = 0 To lngCnt - 1
    lngSize = lngSize + LenB(strFile(i)) + 2  '文字列+終端文字の長さを足してゆく
  Next
  lngSize = lngSize + 2 '文字列の最後の終端文字の長さを足す
    
  lngHDROP = GlobalAlloc(GHND, stDrop.pFiles + lngSize)  'DROPFILES構造体の領域確保
  lngPDROP = GlobalLock(lngHDROP)

  'DROPFILESのヘッダー部をコピー
  CopyMemory ByVal lngPDROP, stDrop, stDrop.pFiles
  'ファイルリストの部分をコピー
  lngPDROP = lngPDROP + stDrop.pFiles
  For i = 0 To lngCnt - 1
    bytD = strFile(i)
    CopyMemory ByVal lngPDROP, bytD(0), UBound(bytD) + 1 + 2
    lngPDROP = lngPDROP + UBound(bytD) + 1 + 2
  Next
  GlobalUnlock lngHDROP
  
  '&H50061Eはメモ帳のウィンドウハンドル(Spy++で調べ、とりあえず直接指定)
  PostMessage &H50061E, WM_DROPFILES, lngHDROP, 0&
  
End Sub

ただし、私が目的とするターゲットアプリケーションのドロップにはまだ成功していません。
あとはターゲットウィンドウを探すだけだと思いますので、ひとまず解決とさせていただきます。
>hatahataさんへ
きたないソースですがご参考になりましたでしょうか?


ともみ  2004-08-09 05:34:01  No: 85005

いろいろやって、気づいたことがあります。
どうしてもターゲットとなるアプリケーションにドロップすることが
できないのでSpy++でメッセージを監視して調べてみました。

私のアプリケーションからWM_DROPFILESのメッセージはつたわっている
ことが分かりましたが、何も表示されません。
エクスプローラからファイルをドロップするとWM_DROPFILESのメッセージ
がないのに画像が表示されました。
私のアプリケーションからもOleDragでターゲットのアプリケーション
にドロップしたときもWM_DROPFILESのメッセージなしに画像が表示されました。

これらより推測し、ターゲットとなるアプリケーションはOleによるataObject
を介したドロップしか受け付けていないのでは?という結論になりました。

相手側にOleDragDropイベントを起こさせるようなウィンドウメッセージはあるのでしょうか?
せっかく作ったコードもお蔵入りです。。。


hatahata  2004-08-09 08:34:34  No: 85006

ともみさん、サンプルコードを載せて頂きありがとうございました。
私の方の目的のアプリでは、正常に動作させる事ができました。
メモリ関連のAPIは理解出来ていませんので、勉強させて貰います。
これで先に進めそうです。ありがとうございます。

しかし、ともみさんの方はまた別の問題があるみたいですね。
適当にWM_OleDragDropかな?なんて思ってMSDNを見てもヒットしませんでしたが、以下のサイトでは使っているみたいです。
http://www.cppbuilderdevjournal.com/articles/issues/0011/Advanced_string_grid_techniques.htm
これまたC++のサンプルで私には理解できてませんけど、参考になりますでしょうか?
あとWM_OleDropでも1件だけヒットです。
http://www.google.co.jp/search?hl=ja&ie=UTF-8&q=WM_OleDrop&lr=

まと外れかも知れません。すみません。


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

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






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