2024/06/03 電子書籍「電子書籍出版・技術文書作成を劇的に加速!秀丸エディタ + Markdown + Pandocの驚異」を出版

C++Builderで、ファイルのドロップ(Drop)に対応するには

C++Builder

Windowsのアプリケーションを作成していると、エクスプローラー (File Explorer) やAs/RのファイラーからファイルやフォルダをDrag&Dropして使用したい事があるのではないかと思います。C++Builderで、ファイルやフォルダのドロップ(Drop)処理の方法を説明します。

プロジェクトの準備

C++Builderで、初めてのプロジェクト作成でも説明しました、SDIアプリケーションをベースに説明します。メインフォームには、TMemoを配置して、確認用のメッセージを表示します。C++Builder 12で作成しています。

プロジェクトファイルは、以下からダウンロードできます。

ファイルのドロップ(Drop)を受付可能とする

アプリケーションのフォームで、ファイルのファイルのドロップ(Drop)を受付可能とするには、DragAcceptFiles関数を使用します。DragAcceptFiles関数を使用するには、shellapi.h をインクルードします。

DragAcceptFiles関数は、コンストラクタ、OnCreateイベント、OnShowイベントのどれかでコールします。それから、DragAcceptFiles関数を使用できるように、shellapi.h をインクルードする必要があります。

#include <shellapi.h>

__fastcall TSDIAppForm::TSDIAppForm(TComponent *AOwner)	: TForm(AOwner)
{
	// for Drop --------------------------
	DragAcceptFiles(Handle, true);
}

WM_DROPFILESメッセージの処理関数を追加する

DragAcceptFiles(Handle, true); をコールすると、フォームにファイルやフォルダをドロップすると、WM_DROPFILESメッセージが、発生します。

C++Builderで、WM_DROPFILESメッセージを処理するには、フォームのヘッダファイル(ここでは、SDIMAIN.h)に、以下のコードを追加します。これで、WM_DROPFILESメッセージが発生すると、WMDropFiles関数が実行されるようになります。

private:
	void __fastcall WMDropFiles(TWMDropFiles MSG);  // for Drop

BEGIN_MESSAGE_MAP
	VCL_MESSAGE_HANDLER(WM_DROPFILES, TWMDropFiles, WMDropFiles)    // for Drop
END_MESSAGE_MAP(TForm)

ファイル名を格納するTStringListを用意する

ファイル名を格納する為に、TStringListを使用してみます。

ヘッダファイルに定義を追加する

ヘッダファイルに、TStringList *DrpoFileInfo; を追加します。

private:

    TStringList *DrpoFileInfo;	// Drop

DrpoFileInfoの生成と、削除処理を追加する

今回は、コンストラクタとOnDEstroyイベントに記述します。

__fastcall TSDIAppForm::TSDIAppForm(TComponent *AOwner)	: TForm(AOwner)
{
	// for Drop --------------------------
	DrpoFileInfo	= new TStringList;
	DragAcceptFiles(Handle, true);
}

void __fastcall TSDIAppForm::FormDestroy(TObject *Sender)
{
    delete DrpoFileInfo;
}

ファイル名の取得

ファイル名の取得する処理を関数化しました。

int __fastcall TSDIAppForm::GetDropFiles(TWMDropFiles MSG, TStringList *FileList, bool clear)
{
	// Dropされたファイル情報の取得
	TCHAR FileName[MAX_PATH + 1];
	String fname	= "";
	
	try
	{
		if(clear) { FileList->Clear(); }	// リストのデータのクリア
		int DragNo	= DragQueryFile((HDROP)MSG.Drop, 0xFFFFFFFF, nullptr, 0);
		if( DragNo != 0 )
		{
			for(int i=0;i<DragNo;i++)
			{
				DragQueryFile((HDROP)MSG.Drop, i, FileName, sizeof(FileName)/sizeof(_TCHAR));
				fname    = FileName;
				if(TFile::Exists(fname))    			// ファイルが存在すれば、格納する
				{
					if(FileList->IndexOf(fname) == -1)	// 重複チェック
					{
						FileList->Add(fname);
					}
				}
			}
			// Dropされたファイル情報の取得
			DragFinish((HDROP)MSG.Drop);
		}
	} __finally {
		// 
	}

	return FileList->Count;
}
  • MAX_PATH を使用する為に、stdlib.h をインクルードします。
  • DragQueryFile((HDROP)MSG.Drop, 0xFFFFFFFF, nullptr, 0); で、ドロップされたファイルの数を取得します。
  • DragQueryFile((HDROP)MSG.Drop, i, FileName, sizeof(FileName)/sizeof(_TCHAR)); で、順番にFileNameに、ファイル名を取得します。
  • この関数では、フォルダと区別する為に、TFile::Exists()関数を使用しています。System.IOUtils.hppを使用できるように、System.IOUtils.hpp をインクルードしておきます。
  • 重複チェックを行い、新しいファイル名の時はFileListに追加します。
  • 処理が終わったら、DragFinish((HDROP)MSG.Drop) をコールします。
  • この関数は、取得したファイル名の数(FileList->Count)を返します。

フォルダ名の取得

フォルダ名の取得する処理も関数化しました。

int __fastcall TSDIAppForm::GetDropFolders(TWMDropFiles MSG, TStringList *FileList,  bool clear)
{
	// Dropされたファイル情報の取得
	TCHAR FileName[MAX_PATH + 1];
	String fname	= "";
	
	try
	{
		if(clear) { FileList->Clear(); }	// リストのデータのクリア
		int DragNo	= DragQueryFile((HDROP)MSG.Drop, 0xFFFFFFFF, nullptr, 0);
		if( DragNo != 0 )
		{
			for(int i=0;i<DragNo;i++)
			{
				DragQueryFile((HDROP)MSG.Drop, i, FileName, sizeof(FileName)/sizeof(_TCHAR));
				fname    = FileName;
				if( TDirectory::Exists(fname) )
				{
					if(FileList->IndexOf(fname) == -1)	// 重複チェック
					{
						FileList->Add(fname);
					}
				} else if( TFile::Exists(fname) ) {
					// ファイルがDropさっれたら、そのフォルダ名の取得し格納する
					fname			= TPath::GetDirectoryName(fname);
					if(FileList->IndexOf(fname) == -1)	// 重複チェック
					{
						FileList->Add(fname);
					}
				}
			}
			DragFinish((HDROP)MSG.Drop);
		}
	} __finally {
		// 
	}

	return FileList->Count;
}

基本的な処理は、GetDropFiles関数と同じです。相違点は、フォルダ名の処理を行うのに、TDirectory::Exists() 関数で、フォルダかどうかチェックし、フォルダ名ではない時は、TPath::GetDirectoryName()関数で、フォルダ名を取得してそのフォルダ名をFileListに格納しているところです。

WMDropFiles関数の処理

WMDropFiles関数内の処理は、先ほど作成した関数を呼び出します。

void __fastcall TSDIAppForm::WMDropFiles(TWMDropFiles MSG)
{
	int DragNo	= GetDropFiles(MSG, DrpoFileInfo);			// ファイル名を取得
	// int DragNo	= GetDropFolders(MSG, DrpoFileInfo);	// フォルダ名を取得
	if(DrpoFileInfo->Count > 0)
	{
		for(int i=0;i<DrpoFileInfo->Count;i++)
		{
			Memo1->Lines->Add(DrpoFileInfo->Strings[i]);	// 取得したファイル名をTMemoに表示
		}
	}
	// Memo1->Lines->Add("WMDropFiles");
}

参考URL

以下のHPも参考にしてください。

タイトルとURLをコピーしました