Drag&Dropされたファイルを取得してみる

hsgetmsg.dllのメッセージ取得機能を使った最初に紹介する拡張機能は、HSPウィンドウにドラッグ・アンド・ドロップされたファイルの取得であります。

ドロップファイル名取得の手順

ドラッグ・アンド・ドロップされたファイル名を取得する機能を使うには、基本的には以下のような操作を行ないます。

  1. ウィンドウをサブクラス化して、WM_DROPFILESメッセージ(0x0233)を取得するように設定する。
  2. ウィンドウへのドラッグ・アンド・ドロップを可能にする。(DragAcceptFiles関数)
  3. メッセージを取得して、メッセージがWM_DROPFILESであれば以下の操作をする。
    1. ドロップされたファイルの数を取得する。(DragQueryFile関数)
    2. ドロップされた数だけ繰り返しファイル名を取得する。(同じくDragQueryFile 関数)
    3. ドロップファイル用のメモリを解放する。(DragFinish関数)

まずは、取得メッセージの設定からです。ファイルがウィンドウにドラッグ・アンド・ドロップされると、そのウィンドウには WM_DROPFILES メッセージが送られます。

#define  WM_DROPFILES    0x0233

WM_DROPFILES
    hDrop = wParam

hDropパラメータ(wParamの値)は、ドロップファイル名を管理している内部データ構造体を操作するためのハンドルになります。このハンドルが何なのかはあとで説明します。

次に、ウィンドウがファイルがドロップされるファイルを受け取るのを許可する設定をします。ウィンドウにドロップ出来るようにするには、DragAcceptFiles関数を使います。

VOID DragAcceptFiles(
    HWND hWnd,      // ウィンドウハンドル
    BOOL fAccept    // 受け入れるかどうかのフラグ
);

この関数で、fAcceptパラメータに1を指定することで、ドラッグ・アンド・ドロップされるファイルの受け入れを許可します。

あとはメッセージが送られてくるのを待ちます。ドラッグアンドドロップされると、ウィンドウにWM_DROPFILESメッセージが送られてきます。このメッセージの付加情報wParamパラメータはドロップされたファイルの名前を管理している内部データ構造体を操作するためのハンドルを示します。ファイル名を取得するにはこのハンドルが必要になります。

まずはドロップされたファイルの数を取得する必要があります。ファイル数を取得するにはDragQueryFile関数を使います。

UINT DragQueryFileA(
    HDROP  hDrop,   // 内部データ構造体のハンドル
    UINT   iFile,   // ファイルインデックス
    PTSTR  pszFile, // ファイル名を格納するバッファのアドレス
    UINT   cch      // pszFile バッファのサイズ
);

ファイル数を取得する場合はこの関数のhDropパラメータに先ほどの内部構造体のハンドルを指定し、iFileパラメータに-1を指定します。こうすることで、関数の戻り値としてファイル数が返ります。

次にファイル名を取得します。ファイル名の取得は1つずつ行ないます。使用するAPI関数は、ファイル数取得のときと同様にDragQueryFile関数です。今度は、iFileパラメータにファイルのインデックスを、pszFileパラメータには、ファイル名を格納するための文字列変数のアドレスを指定しておきます。この文字列変数は出来れば260バイト以上のサイズを確保したものを指定しましょう。そして、cchパラメータにはそのサイズ(260)を指定します。ファイルのインデックスは0を基準としたものになります。したがって、1つ目のファイルを取得するならインデックスを0に、2つ目なら1に、……というようにして、これをドロップされたファイルの数だけ繰り返します。

必要なデータがすべて取得し終わったら、DragFinish関数を呼び出します。

VOID DragFinish(
    HDROP hDrop    // 内部データ構造体のハンドル
);

ファイルがドロップされると、システムは自動的にメモリを確保してドロップされたファイルの情報を保持しています。DragFinish関数は、このメモリ領域を解放するためのものです。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。ウィンドウにファイルがドラッグ・アンド・ドロップされたらそのファイル名を表示するプログラムを作ります。

    #include "llmod.as"
    #include "hsgetmsg.as"

    #define WM_DROPFILES      0x0233

    ; ウィンドウのサブクラス化
    set_subclass : hwnd = stat        ; ウィンドウハンドル(ID=0)
    set_message WM_DROPFILES          ; 取得メッセージを設定

    ; ドロップファイルの受け入れを設定
    pm = hwnd, 1
    dllproc "DragAcceptFiles", pm, 2, D_SHELL
    if stat == 0 {
        dialog "受け入れの設定に失敗しました。", 1, "エラー"
        end
    }
    mes "このウィンドウにファイルをドロップして下さい。"

    ; メッセージパラメータ用変数
    dup msg,  msgval.1
    dup wprm, msgval.2
    dup lprm, msgval.3

*mainloop
    get_message msgval
    if msgval == hwnd {
        ; メッセージを受け取ったとき
        if msg == WM_DROPFILES : gosub *OnDropFiles
    } else {
        wait 10
    }
    goto *mainloop

*OnDropFiles
    ; ファイルがドロップされたときの処理
    hDrop = wprm                      ; データ構造体のハンドル

    ; ファイルの数の取得
    pm = hDrop, -1, 0, 0              ; 第2パラメータを -1 に
    dllproc "DragQueryFileA", pm, 4, D_SHELL
    filenum = stat                    ; ドロップされたファイル数

    sdim filename, 260                ; ファイル名を格納するバッファ
    getptr p_filename, filename       ; バッファのアドレス

    repeat filenum                    ; ドロップされたファイルの数だけ実行
        ; ファイル名の取得
        pm = hDrop, cnt, p_filename, 260
        dllproc "DragQueryFileA", pm, 4, D_SHELL
        mes filename                  ; ファイル名を表示
    loop

    ; ドロップファイル用のメモリを解放
    pm = hDrop
    dllproc "DragFinish", pm, 1, D_SHELL

    return