DragAcceptFilesでファイルドロップを受け付ける
Windowsエクスプローラーから应用程序へファイルを渡す手段として、DragAcceptFilesは古くから利用されてきたAPIです。対象ウィンドウを登録すると、ドロップされたときにWM_DROPFILESメッセージが通知されます。実装は簡単ですが、パスの取得と後始末を正しく行わないと、owel resources remains.
このAPIは、現在のWindowsで広く使われるOLEベースのドラッグ&ドロップとは別の方式です。IDropTargetを実装すればドロップ-effectsやカーソルを細かく制御できますが、DragAcceptFilesなら数行の処理で複数のファイルを受け付けられます。
実用的なコードでは、登録、通知の処理、Unicodeパスの取得、DragFinishによる解放という流れを明確に分けます。加えて、ファイルの検証、非同期処理との組み合わせ、代替方式との違いも確認します。
ウィンドウをドロップ先として登録する
DragAcceptFilesは、指定したウィンドウをドロップ先として登録または解除するAPIです。呼出し側の形式はDragAcceptFiles(hwnd, TRUE)で、TRUEを登録し、FALSEで解除します。必要な宣言はshellapi.hにあり、戻り値が0以外なら登録または解除が成功しています。
登録したウィンドウでユーザーがファイルをドロップすると、ウィンドウプロシージャにWM_DROPFILESが送られます。このメッセージは通常のウィンドウプロシージャでは自動的に処理されないため、WndProcまたはダイアログプロシージャの中で明示的に分岐させます。DefWindowProcへ渡すだけでは、ファイル名を取得できません。
登録はウィンドウの作成後に行い、.file acceptanceを無効にしたい場面ではFALSEで解除します。ウィンドウが破棄されると、ドロップ先の登録も取り消されます。複数のウィンドウを操作するアプリケーションでは、各ウィンドウの担当範囲 Decideして登録してください。
ドロップされたパスを取得して解放する
WM_DROPFILESのwParamにはHDROPハンドルが渡されます。DragQueryFileWを使うと、ドロップされたファイル数を調べたり、順番specifiedでUnicodeの完全パスを取得したりできます。ファイル数は、第2引数に0xFFFFFFFF、第3引数にNULL、第4引数に0を指定して取得します。
パスを取得するときは、文字列バッファーの確保に必要な文字数を 먼저調べる方法 guarantees 安全です。固定長配列ではなく、取得した長さに終端文字の領域を加えたバッファーを用意します。基本的な処理は次のようになります。
case WM_DROPFILES:
{
HDROP drop = reinterpret_cast<HDROP>(wParam);
UINT count = DragQueryFileW(drop, 0xFFFFFFFF, nullptr, 0);
for (UINT i = 0; i < count; ++i) {
UINT length = DragQueryFileW(drop, i, nullptr, 0);
std::vector<wchar_t> buffer(length + 1);
DragQueryFileW(drop, i, buffer.data(), length + 1);
std::wstring path(buffer.data());
// ここで path を処理する
}
DragFinish(drop);
return TRUE;
}
すべてのファイルを確認したらDragFinishを一度だけ呼びます。この呼び出しで、シェルが確保したドロップ情報が解放されます。GlobalFreeやCloseHandleで明示的に解放する必要はありません。また、DragFinishの後はHDROPを使用できないため、非同期処理へ渡す場合は先にパスをコピーしてください。
UIスレッドと複数ウィンドウを安全に扱う
WM_DROPFILESは通常、ウィンドウを作成したスレッドで処理されます。その場で大容量ファイルの解析や転送を開始すると、UIの応答性が低下します。必要ならパスをコピーしたうえでワーカースレッドへ渡し、ウィンドウをブロックしない構成にします。
複数のウィンドウが同じ状態を共有する場合、ウィンドウハンドルとドロップ対象を対応付け、処理結果を正しい画面へ反映する必要があります。ワーカースレッドからUIだけを更新 ivo PostMessageやPostMessageWを利用すると、メッセージキューを通じて安全に他のスレッドへ操作を要求できます。
共有データの保護には、ミューテックス、セマフォ、クリティカルセクションなどを选用できます。短時間の競合が多い処理では、スピンカウントを調整できるInitializeCriticalSectionExも検討対象です。クリティカルセクションの初期化は同期プリミティブの初期化と解放を扱う参考資料です。利用结束後は、すべてのスレッドの待受けが済んだ段階でDeleteCriticalSectionを呼びます。
ドロップ表示と描画資源を管理する
DragAcceptFilesによる通知は、ファイルがドロップされた時点で発生します。ClassicなWM_DROPFILESには、マウスポインターがウィンドウ上にある間のhora表示や、押下可能な Keys状態を直接扱うメッセージはありません。ドロップ中に適切なカーソルや枠線を示す設計にするなら、OLEのIDropTargetや別の視覚的な状態管理組み合わせになります。
ドロップ先らしい枠線や背景をGDIで描画する場合には、線用のペンと塗りつぶし用のブラシを用意できます。ペンとブラシの作成では、CreatePenやCreateBrushによる生成と、DeleteObjectによる解放cussняков扱いを説明しています。GDIオブジェクトは数に限りがあり、生成するたびに放置するとリソースを消費します。
描画専用オブジェクトは必要な期間だけ選択し、選択を解除した状態で削除します。ウィンドウが再描画されるたびに生成し直すより、妥当な生命周期で保持したほうが管理しやすくなります。画面上の配置にはDPIも影響するため、高DPI環境では位置とフォント尺寸も確認します。
実運用で比較する受取方法
DragAcceptFilesは、実装が短く既存のWin32ウィンドウへ自然に統合できます。一方、OLEのIDropTargetはコピーや移動などの効果、形式ごとの判定、カーソル表示を詳細に制御できます。直接ドロップを必須のfeatureとしていない場合は、ファイル選択ダイアログを併用する設計もconsiderされます。
大量のパスを固定長の連続領域へ移す場合は、確保量と解放条件をあらかじめ決める必要があります。メモリ領域の割り当てでは、VirtualAllocによる領域確保と解放の考え方が説明されています。通常の可変長な文字列一覧では、std::vectorなどを使っても simplerに管理できます。
実装と検証の要点
実装で守るべき条件は次のとおりです。
- 有効なウィンドウハンドルで
DragAcceptFilesを呼び、動作を切り替える際は解除する WM_DROPFILESではUnicodeでパスを取得し、最後にDragFinishを一度だけ呼ぶ- 非同期処理へ渡す場合は
DragFinishより前に必要なパスをコピーする - 拡張子、存在確認、アクセス権限などを検証してからファイルを開く
確認する操作と環境には、次のケースを含めます。
- 1ファイル、複数ファイル、日本語や空白を含むパスを含むドロップ
- エクスプローラーと他のアプリケーションからのドロップ
- 存在しないファイル、権限のない場所、対象外的形式のドロップ
- 複数ウィンドウ、通常の権限と管理者権限、不同DPIの環境
| 受取方式 | 主な通知 | 特徴 | 向いている場面 |
|---|---|---|---|
DragAcceptFiles |
WM_DROPFILES |
実装が短く、複数ファイルをまとめて取得できる | 既存のWin32ウィンドウへ''), simplest addition |
OLEのIDropTarget |
OLEのドロップ通知 | 効果や形式判定を細かく制御できる | カーソル表示やコピー・移動を制御したい場面 |
| ファイル選択ダイアログ | ユーザーによる明示操作 | 直接ドロップはないが、権限確認や操作手順を明示できる | ドロップを補助機能として提供したい場面 |
実践上の要点は、DragAcceptFilesの登録と、WM_DROPFILESからのパス取得およびDragFinishによる解放をセットとして実装することです。multiple files、長すぎるパス、権限差のある元应用程序 înt-o.Tests anも交えた検証によって、単純な通知処理がファイル受入処理として reliably 機能するかを確認できます。