;============================================================ ; COM コールバックインターフェース sample 02 — IDropTarget ; ; HSP のウィンドウを OLE drop target として登録し、エクスプローラから ; ファイルをドラッグ&ドロップで受け取る。 ; ; hsp3net 系 GUI ランタイム (hsp3_64.exe) で実行 ; ; 使い方: ; 1. このスクリプトを実行 (hsp3_64.exe) ; 2. エクスプローラで適当なファイルを掴んで、HSP のウィンドウに ; ドロップする ; 3. 各イベント (DragEnter / DragOver / DragLeave / Drop) が ; 発火したログがウィンドウに表示される ;============================================================ #define IID_IDropTarget "{00000122-0000-0000-c000-000000000046}" #usecom IDropTarget IID_IDropTarget "{}" ;------ ドロップされたファイルパスを取り出すための IDataObject ------ #define IID_IDataObject "{0000010e-0000-0000-c000-000000000046}" #usecom IDataObject IID_IDataObject "{}" ; vtable: 3=GetData(FORMATETC*, STGMEDIUM*) #comfunc DO_GetData 3 intptr, intptr ;------ コールバッククラス宣言 ------ #defcbcom MyDT IDropTarget ; vtable: 3=DragEnter, 4=DragOver, 5=DragLeave, 6=Drop ; ; HRESULT DragEnter(IDataObject*, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) ; HRESULT DragOver (DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) ; HRESULT DragLeave() ; HRESULT Drop (IDataObject*, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) ; ; POINTL は { LONG x, LONG y } の構造体 = 8 byte で、x64 ABI では ; 値渡しでも 8byte = 1 register (rdx 等) に乗る。intptr で受ければ ; 下位 32bit に x、上位 32bit に y が入る。 #cbmethod 3 int comobj, int, intptr, intptr, *dt_enter #cbmethod 4 int int, intptr, intptr, *dt_over #cbmethod 5 int, *dt_leave #cbmethod 6 int comobj, int, intptr, intptr, *dt_drop #endcbcom ;------ OLE / Win32 ------ #uselib "ole32.dll" #cfunc OleInitialize "OleInitialize" intptr #func OleUninitialize "OleUninitialize" #cfunc RegisterDragDrop "RegisterDragDrop" intptr, comobj #cfunc RevokeDragDrop "RevokeDragDrop" intptr #func ReleaseStgMedium "ReleaseStgMedium" var #uselib "shell32.dll" ; UINT DragQueryFileW(HDROP, UINT iFile, LPWSTR buf, UINT cch) ; iFile = 0xFFFFFFFF (-1) で「ファイル数」を返す ; それ以外は indexed file path を buf に書き、必要 char 数を返す #cfunc DragQueryFileW "DragQueryFileW" intptr, int, var, int ;------ FORMATETC / STGMEDIUM 構造体定数 ------ #define CF_HDROP 15 #define DVASPECT_CONTENT 1 #define TYMED_HGLOBAL 1 ;------ ウィンドウ準備 ------ title "IDropTarget sample — ファイルをドラッグ&ドロップしてください" screen 0, 600, 400 color 240, 240, 250 : boxf : color 0, 0, 0 pos 10, 10 : mes "ファイルをこのウィンドウにドラッグ&ドロップしてください" pos 10, 30 : mes "(ESC キーで終了)" pos 10, 50 : mes "----- イベントログ -----" objmode 2 pos 10, 70 ; OLE 初期化 (HSP screen が既に初期化している場合は S_FALSE = hr=1 が返る、これは正常) hr = OleInitialize(0) if hr < 0 : mes "OleInitialize failed hr=" + hr : stop ; コールバック生成 + 登録 newcomcb dt, "MyDT", "main_window" if stat ! 0 : mes "newcomcb failed" : stop hr = RegisterDragDrop(hwnd, dt) if hr ! 0 : mes "RegisterDragDrop failed hr=" + hr : stop mes "drop target 登録 OK (tag=" + comcbtags() + ")" ; メインループ: stop ではなく wait を使ってメッセージを処理しつつ留まる onkey *on_key *main_loop wait 5 goto *main_loop *on_key if iparam = 27 { ; ESC _hr = RevokeDragDrop(hwnd) delcom dt OleUninitialize end } return ;------ コールバック実装 ------ *dt_enter ; DragEnter(IDataObject*, DWORD key, POINTL pt, DWORD* pe) ; comprm(0) = IDataObject* (raw int64) ; comprm(1) = key ; comprm(2) = POINTL packed (lo32=x, hi32=y) ; comprm(3) = pdwEffect (out) _key = comprm(1) _pt = comprm(2) _pe = comprm(3) ; DROPEFFECT_COPY = 1 を out パラメータに書き戻す dupptr _eff_slot, _pe, 4, 4 _eff_slot = 1 mes "DragEnter key=" + _key + " x=" + (_pt & 0xffffffff) + " y=" + ((_pt >> 32) & 0xffffffff) comret 0 : return *dt_over _pe = comprm(2) dupptr _eff_slot, _pe, 4, 4 _eff_slot = 1 comret 0 : return *dt_leave mes "DragLeave" comret 0 : return *dt_drop _do_ptr = comprm(0) ; raw IDataObject* _key = comprm(1) ; DWORD grfKeyState _pt = comprm(2) ; POINTL _pe = comprm(3) ; LPDWORD pdwEffect (out) dupptr _eff_slot, _pe, 4, 4 _eff_slot = 1 mes "Drop @ x=" + (_pt & 0xffffffff) + " y=" + ((_pt >> 32) & 0xffffffff) ; --- IDataObject から CF_HDROP データを取り出す --- ; FORMATETC を構築 (x64 では sizeof = 28 byte だが安全に 32 で確保) ; offset 0 : USHORT cfFormat = CF_HDROP ; offset 8 : DVTARGETDEVICE* ptd = NULL ; offset 16 : DWORD dwAspect = DVASPECT_CONTENT ; offset 20 : LONG lindex = -1 ; offset 24 : DWORD tymed = TYMED_HGLOBAL sdim _fmt, 32 wpoke _fmt, 0, CF_HDROP lpoke _fmt, 16, DVASPECT_CONTENT lpoke _fmt, 20, -1 lpoke _fmt, 24, TYMED_HGLOBAL ; STGMEDIUM (24 byte) は GetData が埋める ; offset 0 : DWORD tymed ; offset 8 : HGLOBAL hGlobal (= HDROP) ; offset 16 : IUnknown* pUnkForRelease sdim _stg, 24 ; raw IDataObject* を newcom -2 で一時 wrap して GetData を呼ぶ newcom _do, IDataObject, -2, _do_ptr DO_GetData _do, varptr(_fmt), varptr(_stg) _hr = stat delcom _do if _hr ! 0 { mes " GetData failed hr=" + _hr comret 0 : return } ; STGMEDIUM offset 8 から HDROP を取得 (8 byte) _hdrop = qpeek(_stg, 8) ; ファイル数取得 _nfiles = DragQueryFileW(_hdrop, -1, _dummy_buf, 0) mes " " + _nfiles + " file(s) dropped:" ; 各ファイル名を取得 (#cfunc なので戻り値受けで呼ぶ) sdim _wpath, 1024 ; UTF-16 用 (512 WCHAR) repeat _nfiles _used = DragQueryFileW(_hdrop, cnt, _wpath, 512) ; UTF-16 → SJIS に変換して表示 mes " " + cnvwtos(_wpath) loop ; STGMEDIUM を解放 ReleaseStgMedium _stg comret 0 : return