C++ Builder CE tips

FMXでファイルをフォームへDrag and Dropする。 FMX版 その2 普通のやり方

FMXの場合は、VCLでやったようなMESSAGE_MAPを書くやり方が使えないので、自前のWndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)を割り込ませて実現します。

Unit1.hから

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Forms.hpp>
#include <FMX.Controls.Presentation.hpp>
#include <FMX.Memo.hpp>
#include <FMX.Memo.Types.hpp>
#include <FMX.ScrollBox.hpp>
#include <FMX.Types.hpp>
#include <FMX.Platform.Win.hpp>
#include <FMX.StdCtrls.hpp>
#include <FMX.Objects.hpp>
#include <FMX.Edit.hpp>

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TMemo *Memo1;

	void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:	// ユーザー宣言


public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

コンストラクタ以外は、FormCloseくらいが目立つ、というかそれしかない。

Unit1.cppは、

//---------------------------------------------------------------------------

#include <fmx.h>
#pragma hdrstop
#include <FMX.Platform.Win.hpp>
#include <ShellAPI.h>
#include <cstdlib>
#include <memory>
#include <vector>
#include <iostream>


#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"

#if _WIN64
#define Parm GWLP_WNDPROC
#elif _WIN32
#define Parm GWL_WNDPROC
#elif __APPLE__
  // macOS
#else
  #error Not a supported platform
#endif

std::vector <AnsiString>filenames;

TForm1 *Form1;

HWND m_Hwnd;
WNDPROC m_WndOldProc;
long m_OldWndProc;



LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
	int count;
	int i;
	char fname[256];
	TRect f;


	if(Msg == WM_DROPFILES){

		const unsigned int count = DragQueryFile((HDROP)wParam,-1,NULL,0);
		for( unsigned int i = 0 ; i < count ; ++i ){
				const unsigned int length = DragQueryFile((HDROP)wParam, i, NULL, 0);
				std::unique_ptr<wchar_t[]> filename(new wchar_t[length + 1]);

				DragQueryFile((HDROP)wParam, i, filename.get(), length + 1);
				UnicodeString temp;
				temp = filename.get();
				Form1->Memo1->Lines->Add(temp);
		}

	}


	return CallWindowProc(m_WndOldProc,hWnd,Msg,wParam,lParam);

}

__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{


	m_Hwnd = Fmx::Platform::Win::FormToHWND(Form1);
	DragAcceptFiles(m_Hwnd,true);

	m_OldWndProc = GetWindowLongPtr(m_Hwnd, Parm);
	m_WndOldProc = (WNDPROC)SetWindowLongPtr(m_Hwnd, Parm,(LONG)WndProc);

}

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
	SetWindowLongPtr(m_Hwnd,Parm,m_OldWndProc);
}

一応プリプロセッサマクロで32bit版64bit版両対応しました。前の二つと同様に、フォームにTMemo一個だけ貼り付けて、上記のUnit1.hUnit1.cppをプロジェクトに入れてbuildしてください。FormCloseをhookするのを忘れずに。

現在のWndProcを保存しておいて、自前のWndProcに差し替えて。所望の動作をさせ、FormCloseのタイミングで保存しておいたWndProcに戻すというロジックなんですが、GetWindowLongPtrとかSetWindowLongPtrとかいかにもなWin32ぽい関数を使っているところが、全然FMXらしくないですかね。そういう意味では2番目に示したやり方の方がスッキリしてますね。

コメント