sample_02_droptarget.hsp

sample\cbcom\sample_02_droptarget.hsp » Plain Format

;============================================================
;   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