ウィンドウに画像ファイルをエクスプローラ等からドラッグ&ドロップすると
サムネイルを表示するアプリケーションがあります。
今VBで作っているアプリケーションから画像ファイルの一覧を表示して
ドラッグ&ドロップする機能は実装できたのですが、実際にドラッグ&ドロップ
しないでも外部アプリケーションにドラッグ&ドロップしたような機能を追加
したいと思います。
WM_DROPFILESメッセージをターゲットのウィンドウにSendMessageで送れば良い
ということまで分かりましたが引数wParamの内部ドロップ構造体のハンドルを
どのように処理すればよいのかが分かりません。
質問は内部ドロップ構造体とはどんな構造体なのか?ということです。
ご存知の方がいらっしゃったらご教授願います。
よろしくお願いします。
開発環境 VB6.0 SP5
OS Windows XP
DROPFILES Structure
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/structures/dropfiles.asp
だと思います。
名前さん回答ありがとうございます。
さっそくリンク先のページを参考にして処理を組んでみましたが、思うように動作しませんでした。
何か解釈を誤っていると思うのですが。作った処理は以下のとおりです。
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文字の扱い方は正しいのか?
だと思うのですが、何かお気付きの点があればご指摘ください。
長文になりましたが、よろしくお願いします。
記述ミスがありました
> GlobalUnlock lngPDROP
GloablUnlock lngHDROP
です
よろしくお願いします。
ざっと見て、気になった点だけ。
> 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 バイト)
魔界の仮面弁士さん、ご指摘ありがとうございます。
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に修正しました。
しかし、状況は変わりませんでした。
これ以外にもなにかおかしいところがあるようです。
もう少し調べてみます。
私もファイルドロップを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だと正常に動作するという話もありました。
横から乗っかり質問ですみません。
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さんへ
きたないソースですがご参考になりましたでしょうか?
いろいろやって、気づいたことがあります。
どうしてもターゲットとなるアプリケーションにドロップすることが
できないのでSpy++でメッセージを監視して調べてみました。
私のアプリケーションからWM_DROPFILESのメッセージはつたわっている
ことが分かりましたが、何も表示されません。
エクスプローラからファイルをドロップするとWM_DROPFILESのメッセージ
がないのに画像が表示されました。
私のアプリケーションからもOleDragでターゲットのアプリケーション
にドロップしたときもWM_DROPFILESのメッセージなしに画像が表示されました。
これらより推測し、ターゲットとなるアプリケーションはOleによるataObject
を介したドロップしか受け付けていないのでは?という結論になりました。
相手側にOleDragDropイベントを起こさせるようなウィンドウメッセージはあるのでしょうか?
せっかく作ったコードもお蔵入りです。。。
ともみさん、サンプルコードを載せて頂きありがとうございました。
私の方の目的のアプリでは、正常に動作させる事ができました。
メモリ関連の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=
まと外れかも知れません。すみません。
ツイート | ![]() |