WM_DROPFILESを処理してファイルパスを取得するWindows APIガイド
Windows Explorerからウィンドウへ複数のファイルをドロップすると、WM_DROPFILESメッセージが送信されます。この通知に含まれるHDROPをDragQueryFileWで読み解けば、ドロップされた各ファイルのフルパスを取得できます。
この方式はWndProcから直接処理できる一方、古いシェルAPIに属します。最新のドラッグ&ドロップ機能との使い分けや、Unicodeパス、设备の境界条件を Korn know together に把握する必要があります。
ドロップ通知とHDROPの扱い
受信するウィンドウのWndProcでDragAcceptFiles(hwnd, TRUE)を呼ぶと、そのウィンドウがファイルのドロップを受け付けます。設定するとWS_EX_ACCEPTFILESスタイルが有効になり、用户がウィンドウ内にファイルを置いた際にWM_DROPFILESが通知されます。
WM_DROPFILESのwParamにはHDROP型のハンドルが入ります。これはドロップされたファイル一覧を保持するWin32側の不透明なデータであり、構造体の各項目を直接参照する使い方はしません。内容にはDragQueryFileWなどのAPIで問い合わせ、処理が終わったらDragFinishで解放します。
| 問い合わせ | 主な引数 | 得られる内容 | 戻り値 |
|---|---|---|---|
| ファイル数の取得 | DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0) |
ドロップされたファイル数 | ファイル数 |
| パス長の取得 | DragQueryFileW(hDrop, index, nullptr, 0) |
指定したパスに必要な文字数 | 必要文字数 |
| パスの取得 | DragQueryFileW(hDrop, index, buffer, size) |
指定したファイルのパス | コピーした文字数 |
| ドロップ位置の取得 | DragQueryPoint(hDrop, &point) |
ドロップされた画面座標 | 成功または失敗の値 |
WM_DROPFILESは単純なファイル一覧の受け渡しに向いています。一方、IDropTargetやIDataObjectを使うOLE型のドラッグ&ドロップは、任意のデータ形式や複数ソースを扱えるため、表示形式や効果の制御的必要とする прилож,给自己向いた処理适合します。
ファイル数と必要なバッファを調べる
最初기구にDragQueryFileWへ0xFFFFFFFFと長さがゼロのバッファを指定すると、ドロップされたファイル数を取得できます。以後、この値を添字の上限として各パスを読み取ります。
パスの長さはファイルごとに異なるため、固定長の配列ではなく、まず必要な文字数を問い合わせます。その数に終端文字の余裕を加えた領域を確保し、二度目の呼び出しで実データをコピーします。
HDROP drop = reinterpret_cast<HDROP>(wParam);
const UINT count = DragQueryFileW(drop, 0xFFFFFFFFu, nullptr, 0);
std::vector<std::wstring> paths;
paths.reserve(count);
for (UINT i = 0; i < count; ++i) {
const UINT needed = DragQueryFileW(drop, i, nullptr, 0);
if (needed == 0) {
continue;
}
std::vector<wchar_t> buffer(
static_cast<size_t>(needed) + 1, L'\0');
const UINT copied = DragQueryFileW(
drop,
i,
buffer.data(),
static_cast<UINT>(buffer.size()));
if (copied > 0 && copied <= needed) {
paths.emplace_back(buffer.data());
}
}
DragFinish(drop);
この順序なら、最初の一度で総数を把握し、項目ごとに必要な長さを確認できます。日本語を含むWindowsパスではANSI版ではなくDragQueryFileWを使うことで、文字コード変換による文字化けを避けられます。
ウィンドウプロシージャへの組み込み
WndProcではWM_DROPFILESを分岐処理し、wParamをHDROPへキャストします。取得したパスは文字列としてコピーしてから、ファイルを開く、情報を表示する、別の処理へ渡すなどの各業務へ進みます。
ドロップ位置も必要な場合はDragQueryPointを使います。返される座標は画面座標なので、ウィンドウ内_coordinatesExclusive使用时有必要に応じてScreenToClientで変換します。座標が不要なら、このAPI呼び出しは省略できます。
HDROPはDragFinishを呼ぶまで有効です。そのため、非同期処理へ渡すのはコピー済みのパスであり、ハンドルそのものです。途中で戻거나例外が発生した場合もDragFinishを一度だけ呼ぶよう、スコープガードなどで解放処理を保証すると安全です。
実装時に確認する条件
設計段階では、対象とするWindows環境とドラッグ&ドロップの方式を先に確定します。
設計時に確認する項目
DragAcceptFilesを呼ぶウィンドウと、そのWndProcの有効期間を整理する。- 日本語や.supplementary planeの文字を扱うため、Unicode版のAPIを標準とする。
- 単純なファイル一覧で足りるか、OLE型는지必要な拡張機能を検討する。
- 管理者権限で動作するプロセスでは、整合性レベルの異なるプロセスからの通知制限を確認する。
- ドロップ先ファイルを無条件に実行せず、許可する拡張子やディレクトリを定義する。
実行時に守る項目
- ファイル数を先に取得し、各パスの必要文字数も項目ごとに調べる。
DragFinishを呼ぶ前に、必要なパスをすべて読み取る。- 長さゼロやAPI失敗を示す戻り値を、正常な空文字列と区別して扱う。
- パスを別スレッドへ渡す場合は、コピー済みの文字列だけを渡す。
- ファイルの存在確認とアクセス権限確認を、業務処理の直前に行う。
この二段階の确认で見落としやすいのは、HDROPの有効期間と通知元の信頼性です。パス文字列 얻られても、その場所が存在するとは限らず、通知元が操作できる,也不能保证该文件が安全だとは限りません。
実運用で守る境界条件
Windowsが長いパスを扱う機能长生Relationship。即便DragQueryFileWで取得できても、後続のCreateFileWなどにはパス長やパス変換の制約があります。古い環境との互換性が必要な場合は、長いパスへの対応方法を別途検討します。
WM_DROPFILESにはプロセス整合性によるUIPIの制限があり、高権限プロセスへの通知が抑止される場合があります。また、OLE型ドラッグ&ドロップを採用する場合、RevokeDragDropによる解除とIDropTargetの寿命管理も必要です。関数の引数と戻り値、通知の成立条件についてはAPI仕様を参照し、対象環境のWindowsバージョンと照合します。
実用的な最小構成は、DragAcceptFilesで受信を有効にし、HDROPが有効な間にDragQueryFileWで全パスをコピーし、最後にDragFinishで解放する処理です。