ファイルのドラッグ・アンド・ドロップ

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

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

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

  1. oncmd命令を用いて、WM_DROPFILESメッセージを処理するように設定する。
  2. ウィンドウへのドラッグ・アンド・ドロップを可能にする。(DragAcceptFiles関数)
  3. WM_DROPFILESメッセージを受け取ったときの処理として以下の手続きを実行する。
    1. ドロップされたファイルの数を取得する。(DragQueryFile関数)
    2. ドロップされた数だけ繰り返しファイル名を取得する。(同じくDragQueryFile 関数)
    3. ドロップファイル用のメモリを解放する。(DragFinish関数)

処理メッセージの登録

まずは、処理を行うウィンドウメッセージの登録からです。ファイルがウィンドウにドラッグ・アンド・ドロップされると、そのウィンドウにはWM_DROPFILESメッセージが送られます。このメッセージコードは次のように定義されています。

#define WM_DROPFILES    0x0233

このメッセージの付加情報として渡されるwParamパラメータの値はドロップされたファイルの名前を管理している内部データ構造体を識別するハンドルであり、後でファイル名を取得するにはこのハンドルが必要になります。

このメッセージのlParamパラメータは使われません。常に0が渡されます。

ウィンドウへのドラッグ・アンド・ドロップを許可

次に、ウィンドウがファイルがドロップされるファイルを受け取るのを許可する設定をします。ウィンドウにドロップ出来るようにするには、DragAcceptFiles関数を使います。この関数はshell32.dllによって提供されており、次のように定義されています。

VOID DragAcceptFiles(
    HWND hWnd,      // window handle
    BOOL fAccept    // accept flag
);

hWndパラメータには対象ウィンドウのハンドルを、fAcceptパラメータにはファイルのドラッグ・アンド・ドロップを許可するかどうかを表す値を指定します。ファイルをドロップできるようにするにはfAcceptパラメータに1 (TRUE) を、ドロップできないようにするには0 (FALSE) を指定します。

この関数でドラッグ・アンド・ドロップを許可するように設定したら、あとはメッセージが送られてくるのを待ちます。ドラッグアンドドロップされると、ウィンドウにWM_DROPFILESメッセージが送られてきます。

ドロップされたファイルの情報を取得

ウィンドウにWM_DROPFILESメッセージが送信されたとき、wParamパラメータとして渡される内部構造体のハンドルを用いることによって、次の情報を得ることができます。


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

UINT DragQueryFileA(
    HDROP hDrop,   // internal structure handle
    UINT  iFile,   // file index
    PTSTR pszFile, // buffer for file name
    UINT  cch      // buffer size
);

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

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


ファイルがウィンドウのどの座標にドロップされたかを取得することもできます。これには、shell32.dllDragQueryPoint関数を呼び出します。

DWORD DragQueryPoint(
    HDROP  hDrop,  // internal structure handle
    PPOINT ppt     // POINT structre
);

最初のhDropパラメータには、ドロップファイルの情報を持つ内部構造体のハンドルを指定します。pptパラメータには、ドロップされた座標を格納するためのPOINT構造体のアドレスを指定します。

POINT構造体は点の座標を格納するための構造体で、以下のように定義されています。

typedef struct tagPOINT {
    LONG x;      // x-coordinate
    LONG y;      // y-coordinate
} POINT, *PPOINT, *NPPOINT, *LPPOINT;

この構造体のxメンバとyメンバには、それぞれ点のx座標とy座標が格納されます。DragQueryPoint関数にこの構造体を渡すと、ファイルがドロップされた点の座標がクライアント座標の形で格納されます。

ドロップファイル情報のメモリ領域を解放

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

VOID DragFinish(
    HDROP hDrop  // internal structure handle
);

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


DragAcceptFiles関数によってファイルのドラッグ・アンド・ドロップを許可するように設定する場合には、必ずWM_DROPFILESメッセージに対する処理を行うようにしてください。そして、メッセージ処理の際には、DragFinish関数を使って、必ずハンドルを解放するようにしてください。これらのことを怠ると、アプリケーションがメモリリークを起こしてしまう可能性があります。

サンプルスクリプト

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

#uselib "shell32.dll"
#func DragAcceptFiles "DragAcceptFiles" int,int
#func DragQueryFile   "DragQueryFileA"  int,int,int,int
#func DragQueryPoint  "DragQueryPoint"  int,int
#func DragFinish      "DragFinish"      int

#define WM_DROPFILES      0x0233

; メッセージ処理の登録
oncmd gosub *OnDropFiles, WM_DROPFILES

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

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

; ファイルがドロップされた座標の取得
dim point, 2                      ; POINT 構造体
DragQueryPoint hDrop, varptr(point)
mes "ドロップ座標 ( " + point(0) + ", " + point(1) + " )"

; ファイルの数の取得
DragQueryFile hDrop, -1, 0, 0     ; 第2パラメータを -1 に
filecount = stat                  ; ドロップされたファイル数
mes "ファイル数 " + filecount + " 個"

; ファイル名の取得(ドロップされたファイルの数だけ実行)
sdim filename, 260                ; ファイル名を格納するバッファ
repeat filecount
    DragQueryFile hDrop, cnt, varptr(filename), 260
    mes filename                  ; ファイル名を表示
loop

; ドロップファイル用のメモリを解放
DragFinish hDrop
return 0