[BlueLeaf1336]> PROBLEMS> OLE Drag Drop>
| history | TOP |
2006/09/22:作成
| はじめに | TOP |
以前に調べて速攻で断念した、エクスプローラへのドラッグアンドドロップです。ここに、もう一度挑戦しようと思います。
すでに関係ありそうな参考にできそうなパクれる情報の満載されたサイトをかき集めたりしたんですが、改めて調べてみると異常にたくさん出てきました。モロなやつもありました。
いつものことですが、このページではすでに書きつくされていようがどうしようが、完全にそのものがない限り、Delphi で書く、ことに重点を置いています。
今回調べたサイトです。○付きはかなり参考になる、あるいはモロのサイトです。これだけのヒントや答えが示されていてまだできない...
| 大雑把な説明 | TOP |
異なるアプリケーションの間でドラッグアンドドロップでデータを受け渡すのは、クリップボードを使ったやり取りと似ているらしいのですが、クリップボードを使ってそういうやり取りをやったことがないので、なるほど!! とはならずです。
まず、ドラッグ元になるために必要最低限必要なのは、IDropSource を完成させる、また、ドロップするデータを処理する IDataObject を完成させる、やることはこれだけです。どちらもクラスではなくてインターフェースなので、AddRef とかそのほかいろいろとあるんですが、ほとんどは Delphi で定義されている TInterfacedObject から派生したクラスを作ることでカバーできてしまいます。
それに、IDropSource を完成させるために 2つ、IDataObject を完成させるために 9つ のメソッドを実装するわけですが、よくわからないものは「実装してません」を宣言するだけで許してくれたりもします。
などかいていると簡単やん、と思えてきますが、これがまた...
| ベースになる何もしないクラスの宣言 | TOP |
まず、決まりきった手順で処理するだけだったり、実装せずに許してもらえるメソッドが結構あるので、これらを「何もしないクラス」として宣言してしまいます。その上で、そのクラスから派生した「実際に何かをするクラス」を作ることにします。
「何もしないクラス」の宣言です。
uses
Windows, SysUtils, Classes, ActiveX, ShlObj, ComObj, ShellApi;
type
//-------------------------------------------------------------------------
// ドラッグ元にするためのなにもしないクラス
TOleDragDropBase = class(TInterfacedObject, IDropSource, IDataObject)
// IDropSource
protected
function QueryContinueDrag(fEscapePressed: BOOL;
grfKeyState: Longint): HResult; virtual; stdcall;
function GiveFeedback(dwEffect: Longint): HResult; virtual; stdcall;
// IDataObject
protected
function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium):
HResult; virtual; stdcall;
function GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium):
HResult; virtual; stdcall;
function QueryGetData(const formatetc: TFormatEtc): HResult;
virtual; stdcall;
function GetCanonicalFormatEtc(const formatetc: TFormatEtc;
out formatetcOut: TFormatEtc): HResult; virtual; stdcall;
function SetData(const formatetc: TFormatEtc; var medium: TStgMedium;
fRelease: BOOL): HResult; virtual; stdcall;
function EnumFormatEtc(dwDirection: Longint; out enumFormatEtc:
IEnumFormatEtc): HResult; virtual; stdcall;
function DAdvise(const formatetc: TFormatEtc; advf: Longint;
const advSink: IAdviseSink; out dwConnection: Longint): HResult;
virtual; stdcall;
function DUnadvise(dwConnection: Longint): HResult; virtual; stdcall;
function EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
virtual; stdcall;
// データユーティリティ
protected
function CreateHText(const AText: String): HGLOBAL;
function CreateHDrop(AFileList: TStringList): HDROP;
procedure CreateMedium(const Data: HGLOBAL; var medium: TStgMedium);
end;
このクラスは、TInterfacedObject から派生し、IDropSource と IDataObject をなんと呼べばよいのかくっつけています。つまりこのクラスひとつで、ドラッグ元になることとドラッグするデータの両方を捌いてしまう作戦です。
インターフェースを使う上でのややこしい話は、TInterfacedObject がやってくれるので、必要なメソッドのみ実装すればよいことになっています。
IDropSource と IDataObject から引き継いだ全てのメソッドに「virtual」をつけているのは、この「何もしないクラス」の本体は、本当に何もしないので、このクラスから派生したクラスで処理を上書きしてもらうためです。
でも、実装を許してもらえるものが結構あるので、このクラスでは「全て実装を許してもらう」形で完成させておきます。こうすることで、本当の本当に必要な(もしくは理解できた箇所だけ)実装だけを派生クラスでやればよいことになって、良い感じかも。
| ベースになる何もしないクラスの本体 | TOP |
本当になんにもしていません。このクラスを作っておけば、派生クラスでは、許してもらえないメソッド、つまり「IDataObject.GetData」「IDataObject.QueryGetData」だけを書くだけでなんとかなるはずです。
現時点で理解できる範囲で説明すると
こんな感じです。多分すごく誤解しています。端折りすぎてます。
//=============================================================================
// TOleDragDropBase
//-----------------------------------------------------------------------------
// 文字列形式のデータを登録
function TOleDragDropBase.CreateHText(const AText: String): HGLOBAL;
var
Len: Integer;
P: PChar;
begin
Len := Length(AText) + 1;
Result := GlobalAlloc((GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT), Len);
if (Result <> 0) then
begin
P := GlobalLock(Result);
try
CopyMemory(P, PChar(AText), Len);
finally
GlobalUnlock(Result);
end;
end;
end;
//-----------------------------------------------------------------------------
// HDROP形式のデータを登録
// サンプル: "ソフトからショートカットを作る"
// http://forum.nifty.com/fdelphi/samples/00094.html
function TOleDragDropBase.CreateHDrop(AFileList: TStringList): HDROP;
var
i: Integer;
LDropFiles: TDropFiles;
LBuf: TMemoryStream;
LName: String;
//LNameLen: Integer;
//LNameBuf: PWChar;
LChar: Char;
P: Pointer;
begin
if (AFileList.Count = 0) then Result := 0
else
begin
// 構造体作成
FillChar(LDropFiles, SizeOf(LDropFiles), 0);
LDropFiles.pFiles := SizeOf(LDropFiles); // ファイルリストのオフセット
LDropFiles.pt := Point(0, 0);
LDropFiles.fNC := False;
//LDropFiles.fWide := True; // NT系らしいけどうまくいかない
LDropFiles.fWide := False;
// TDropFiles の後ろに ファイル名\0ファイル名\0ファイル名\0\0\0 と連結
LBuf := TMemoryStream.Create();
// 構造体書き込み
LBuf.Write(LDropFiles, SizeOf(LDropFiles));
// ファイル名を追加
for i := 0 to AFileList.Count - 1 do
begin
LName := AFileList.Strings[i];
//LNameLen := MultiByteToWideChar(CP_ACP, 0, PChar(LName), -1, nil, 0);
//GetMem(LNameBuf, LNameLen + 1);
//MultiByteToWideChar(CP_ACP, 0, PChar(LName), -1, LNameBuf, LNameLen);
//LBuf.Write(LNameBuf, LNameLen);
//FreeMem(LNameBuf);
LBuf.Write(LName[1], Length(LName));
LChar := #0;
LBuf.Write(LChar, 1);
end;
LChar := #0;
LBuf.Write(LChar, 1);
// ココまでの内容をメモリに書き込む
Result := GlobalAlloc((GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT), LBuf.Size);
P := GlobalLock(Result);
CopyMemory(P, LBuf.Memory, LBuf.Size);
LBuf.Free();
// ロック解放
GlobalUnlock(Result);
end;
end;
//-----------------------------------------------------------------------------
// ドラッグデータ作成
procedure TOleDragDropBase.CreateMedium(const Data: HGLOBAL; var medium: TStgMedium);
begin
medium.tymed := TYMED_HGLOBAL;
medium.unkForRelease := nil;
medium.hGlobal := Data;
end;
//=============================================================================
// TOleDragDropBase - IDropSource
//-----------------------------------------------------------------------------
// キーやマウスボタンの状態をみて、ドラッグを続けるかやめるかを決める関数
function TOleDragDropBase.QueryContinueDrag(fEscapePressed: BOOL;
grfKeyState: Longint): HResult;
begin
// ESCが押されたらキャンセル
if (fEscapePressed) then
begin
Result := DRAGDROP_S_CANCEL;
end
// マウスの両ボタンが押されたらキャンセル
else if ((grfKeyState and (MK_LBUTTON or MK_RBUTTON)) = (MK_LBUTTON or MK_RBUTTON)) then
begin
Result := DRAGDROP_S_CANCEL;
end
// マウスボタンが離されたらドロップ
else if ((grfKeyState and (MK_LBUTTON or MK_RBUTTON)) = 0) then
begin
Result := DRAGDROP_S_DROP;
end
// それ以外は継続
else
begin
Result := S_OK;
end;
end;
//-----------------------------------------------------------------------------
// マウスカーソルの形状を変えたり、特殊効果を出したりするための関数
function TOleDragDropBase.GiveFeedback(dwEffect: Longint): HResult;
begin
//標準のマウスカーソルを使う
Result := DRAGDROP_S_USEDEFAULTCURSORS;
end;
//=============================================================================
// TOleDragDropBase - IDataObject
//-----------------------------------------------------------------------------
// 指定された形式のデータがあればそれを返す関数
function TOleDragDropBase.GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium):
HResult;
begin
Result := E_NOTIMPL;
end;
//-----------------------------------------------------------------------------
// 指定された形式のデータが有るか無いかを返す関数
function TOleDragDropBase.QueryGetData(const formatetc: TFormatEtc): HResult;
begin
Result := E_NOTIMPL;
end;
//-----------------------------------------------------------------------------
// 気にしない。GetDataに似ているらしい
function TOleDragDropBase.GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium):
HResult;
begin
Result := E_NOTIMPL;
end;
//-----------------------------------------------------------------------------
// 気にしない
function TOleDragDropBase.GetCanonicalFormatEtc(const formatetc: TFormatEtc;
out formatetcOut: TFormatEtc): HResult;
begin
Result := E_NOTIMPL;
end;
//-----------------------------------------------------------------------------
// 気にしない。データを追加する関数
function TOleDragDropBase.SetData(const formatetc: TFormatEtc; var medium: TStgMedium;
fRelease: BOOL): HResult;
begin
Result := E_NOTIMPL;
end;
//-----------------------------------------------------------------------------
// 気にしない
function TOleDragDropBase.EnumFormatEtc(dwDirection: Longint; out enumFormatEtc:
IEnumFormatEtc): HResult;
begin
Result := E_NOTIMPL;
end;
//-----------------------------------------------------------------------------
// 気にしない
function TOleDragDropBase.DAdvise(const formatetc: TFormatEtc; advf: Longint;
const advSink: IAdviseSink; out dwConnection: Longint): HResult;
begin
Result := OLE_E_ADVISENOTSUPPORTED;
end;
//-----------------------------------------------------------------------------
// 気にしない
function TOleDragDropBase.DUnadvise(dwConnection: Longint): HResult;
begin
Result := OLE_E_ADVISENOTSUPPORTED;
end;
//-----------------------------------------------------------------------------
// 気にしない
function TOleDragDropBase.EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
begin
Result := OLE_E_ADVISENOTSUPPORTED;
end;
| 実際に何かをするクラスの宣言 | TOP |
多分こうなるはずです。この方針で進んだときにエクスプローラへのドラッグができなかったという事実については、また後で考えることにします。
type
//-------------------------------------------------------------------------
// ドラッグ元にするためのクラス
TOleDragDropObject = class(TOleDragDropBase)
protected
function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium):
HResult; override;
function QueryGetData(const formatetc: TFormatEtc): HResult;
override;
public
(etc.)
end;
| EOF | TOP |