さて、ここまでくれば、あとは WM_DROPFILES メッセージを受け取った時の処理を書くだけです。
タンゴレンではドロップされたファイル名をvectorに格納して返すというインターフェースで共通ライブラリ的に使える基底クラスを作って使っています。
class WSCbase; #include <vector> #include <string> namespace al { namespace gui { /** ドラッグ&ドロップを実装するための基底クラス。 **/ /** 実装用データ */ class CALDragDropImpl; class CALDragDrop { friend class CALDragDropImpl; public: CALDragDrop(); virtual ~CALDragDrop(); /** win に対して Drag&Dropが機能するように準備する。 */ bool InitDragDrop(WSCbase* win); /** 最後の Drag&Drop で引き取られたファイル名のリストを取得する。 @return Drag&Drop されていない時は false。*/ bool GetDroppedFiles( std::vector<std::string>& dropped_files ); /** 最後の Drag&Drop で引き取られたファイル名のリストをクリアする。 */ void ClearDroppedFiles(); private: /** Drag&Drop が行なわれたタイミングで呼び出される仮想関数。 */ virtual void DragDropCB(); private: /** 管理データへのポインタ。 */ CALDragDropImpl* _pimpl; }; } /* namespace gui */ } /* namespace al */
例によって仮想関数がありますが、これはドロップされたタイミングを知るために使われるものです。派生クラスの DragDropCB() では GetDroppedFiles() を呼び出してドロップされたファイル名の配列を引き取ります。ちなみにタンゴレンでは先頭の一つしか使いません。
また、このクラスでは #include <WSCbase.h> みたいにWideStudio/MWTのヘッダを呼び出してはいません。必要なのは WSCbase* だけなので、class WSCbase を前方宣言してポインタだけ使えるようにしています。同様に 処理を実装している CALDragDropImpl クラスも、詳細はここでは隠されています。
CALDragDropImpl クラスは下記のように宣言されています。
class CALDragDropImpl : public CALWndProcHook { friend class CALDragDrop; protected: explicit CALDragDropImpl(CALDragDrop* pbody) : _pbody(pbody) {} virtual bool MyWndProc( LRESULT& lresult, WSCbase* win, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); private: /** 最後にDrag&Dropされたファイル名のリストを保持する。 */ std::vector<std::string> _dropped_files; /** このインスタンスを生成したCALDragDropクラスのインスタンス */ CALDragDrop* _pbody; };
このクラスは CALWndProcHookから派生されて、実質的には MyWndProc() が実装されているだけです。
MyWndProc は下記のようになります。
Windowsが渡してくるファイル名は文字コード変換しておかねばならないというのが普通より一仕事多くなるところで、後は取り立てて説明が必要なところは無いと思います。
bool CALDragDropImpl::MyWndProc( LRESULT& lresult, WSCbase*, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if( message != WM_DROPFILES ) return false; _dropped_files.clear(); HDROP hdrop = (HDROP)wParam; size_t n_files = DragQueryFileW(hdrop,~0u,NULL,0); for( size_t i_file = 0; i_file < n_files; ++i_file ) { std::wstring buff; size_t buffsize = DragQueryFileW(hdrop,i_file,NULL,0) + 1; buff.resize(buffsize); DragQueryFileW(hdrop,i_file,&buff[0],buffsize); std::string utf8_path; ::al::CSysCharSet::GetDefSysCharSet().ConvertFromSysWCS(buff.c_str(),utf8_path); _dropped_files.push_back(utf8_path); } DragFinish(hdrop); _pbody->DragDropCB(); lresult = 0; return true; }
… 実はこのコードはVersion1.1のものです。タンゴレンVersion1.0ではまずいことに ansi 系のAPIを使ってshift-jisでファイル名を受け取ってそこからutf-8に変換していました。実は開発初期にはshift-jis系の入出力関数を使っており、後からutf-16le系のapiを使うようにしたのですけれども、修正が必要なことに気が付かなかった次第です。いけませんね。
(この項終わり)