第105章 ドラッグ・アンド・ドロップ


さて、今回は前回作った「何でも開ける(?)」プログラムに もう少し機能を追加します。ファイルをドラッグしてきてこのプログラムに ドロップすると、そのファイルを開くというものです。

実際には、このプログラムは実用的ではありません。ファイルを 開くのにわざわざドラッグして持ってくるより、そのファイルを ダブルクリック(またはクリック)すれば事足ります(笑)。 しかし、まあドラッグ・アンド・ドロップの解説のためこのような プログラムを作ります。特別な知識は必要ありません。

VOID DragAcceptFiles( HWND hWnd, // 登録するウィンドウ BOOL fAccept // アクセプトオプション );

これで、ファイルをドロップするのを許可するかどうか指定します。 許可すると指定されたウィンドウにファイルがドロップされると そのウィンドウにWM_DROPFILESメッセージが来ます。
fAcceptが、TRUEなら許可、FALSEなら不許可です。

WM_DROPFILES hDrop = (HDROP) wParam; // 内部のDROP構造体へのハンドル

プロシージャでWM_DROPFILESメッセージを捕まえたらWPARAMを調べて DROP構造体のハンドルを取得します。さて、VC++5.0のヘルプを見ると
hDrop = (HANDLE) wParam;
となっていますが型キャストをこのようにするとコンパイル時にワーニングが出ます。 実際HDROPという型が定義されているのでこれを使います。
shellapi.hのなかでは

DECLARE_HANDLE(HDROP);

と定義されています。DECLARE_HANDLEって何だ?と思われるかもしれませんが これは

#define DECLARE_HANDLE(x) typedef WORD x

というマクロになっています。結局単にWORDの値です。

さて、WM_DROPFILESメッセージを捕まえたあとドロップされた ファイルについての情報を知る必要があります。

UINT DragQueryFile( HDROP hDrop,// WM_DROPFILESのwParamの値 UINT iFile, // 問い合わせるファイルのインデックス(0から) LPTSTR lpszFile, // ファイル名を入れるバッファ UINT cch // バッファのサイズ );

ドロップされたファイルの個数を知るにはiFileを0xFFFFFFFFにします。 そうするとこの関数がドロップされたファイルの個数を返します。

また、iFileを0からドロップされたファイルの個数−1の間で指定すると そのファイルのファイル名がlpszFileに格納されます。

ファイル名がわかればあとは、好きなようにプログラムすればよいですね。

VOID DragFinish( HDROP hDrop // 解放するhDrop );

hDropを使い終わったらメモリから解放します。

「ファイル」「開く」を選択すると前章同様のダイアログボックスが出て ここから、開くファイルを選択します。直接ファイルをこのウィンドウに ドラッグ・アンド・ドロップしても開けます。

また、あんまり関係ないですが気分転換の意味でアイコンをWindowsの旗 にしてみました。

// shex02.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "開く(&O)", IDM_OPEN MENUITEM "終了(&X)", IDM_END END END

今回はメニューも単純にしました。

// shex02.cpp #define STRICT #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void OpenF(HWND); char szClassName[] = "shex02"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

いつもと同じですが、自作関数のOpenFの引数が前章よりも少なくなりました。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO); return (RegisterClassEx(&wc)); }

いつもとほぼ同じですがアイコンをIDI_WINLOGOにしてみました。 (深い意味はありません)

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるD&D",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 270, //幅 160, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

いつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, i; HDROP hDrop; UINT uFileNo; char szFileName[256]; switch (msg) { case WM_CREATE: DragAcceptFiles(hWnd, TRUE); break; case WM_DROPFILES: hDrop = (HDROP)wp; uFileNo = DragQueryFile((HDROP)wp, 0xFFFFFFFF, NULL, 0); for(i = 0; i < (int)uFileNo; i++) { DragQueryFile(hDrop, i, szFileName, sizeof(szFileName)); if(ShellExecute(hWnd, NULL, szFileName, NULL, NULL, SW_SHOWNORMAL) <= (HINSTANCE)32) MessageBox(hWnd, "ファイルを開けませんでした", "失敗", MB_OK); } DragFinish(hDrop); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_OPEN: OpenF(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

ウィンドウができたらすぐにDragAcceptFiles関数でファイルのドロップを 許可します。

WM_DROPFILESが来たら、まず落とされたファイルの数を調べます。 これは、DragQueryFile関数の第2引数を0xFFFFFFFFにする事でわかります。
落とされたファイルの個数がわかったらその個数分ループを作って ファイル名を調べます。ここではファイル名を調べてShellExecute関数を 実行しています。
やりたいことが終わったらDragFinishでhDropを解放します。

void OpenF(HWND hWnd) { OPENFILENAME ofn; char szFileName[64]; char szFile[MAX_PATH]; memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "All files(*.*)\0*.*\0\0"; ofn.lpstrFile = szFileName; ofn.lpstrFileTitle = szFile; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrTitle = "実行したいファイルを選択"; if(GetOpenFileName(&ofn) == 0) return; if(ShellExecute(hWnd, NULL, szFile, NULL, NULL, SW_SHOWNORMAL) <= (HINSTANCE)32) MessageBox(hWnd, "実行に失敗しました", "失敗", MB_OK); return; }

この関数も前回より少し簡略化しています。

[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Feb/02/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。