ファイルリストのコピー・ペーストコンポーネント
エクスプローラとのコピー・貼付け処理を行なうためのコンポーネントです。CF_HDROPフォーマットを使用して、クリップボードとファイルリストをやりとりします。
またRenderCopyメソッドと、OnRenderFormatイベントを組合わせることで、遅延レンダリング処理を行なうことができます(遅延レンダリングとは、クリップボードへコピーする時点では、クリップボードフォーマットだけを渡しておき、実際に貼付けを行なう時点でWM_RENDERFORMATメッセージを介して、あらためてコピーする実体を渡すという処理です)。拙作LhaMiの最新バージョンでは、この機能を用いて実際にエクスプローラへ貼付けする時点で、圧縮ファイルを解凍するといった処理を実現しています。
以下をコピーしてファイル名を'CP.pas'として保存し、コンポーネントの登録で登録して下さい。
注)ファイルコピーは実際には「移動」として処理されます。
{*
エクスプローラとのコピー・貼付け処理コンポーネント
実際はコピーしたファイルをエクスプローラ上で貼付けすると、それらの
ファイルは「移動」されます。クリップボードへのCF_HDROPフォーマットを
用いたファイルリストコピーでは、コピーと移動を制御する方法はないよう
です。
プロパティ
CopyFiles: TStringList; クリップボードへコピーするファイルのリスト
PasteFiles: TStringList; クリップボードから張付けしたファイルリスト
メソッド
Copy; CopyFiles内のファイルリストをクリップボードへコピーする
RenderCopy; クリップボードへ空のファイルリストをコピーする
エクスプローラ上で貼付けを実行した時点でOnRenderFormat
イベントが発生する
Paste; クリップボード内にCF_HDROPフォーマットのデータがあれば、
ファイルリストを取出してPasteFilesにセットする
イベント
OnRenderFormat;
RenderCopyを実行後、エクスプローラ上で貼付けを実行した
時点で呼び出される。必要な処理を行なった後にCopyメソッド
でファイルリストコピーを実行すれば、実際の貼付けが行わ
れる。
*}
unit CP;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ShlObj;
type
TOnRenderFormatEvent = procedure(Sender: TObject) of Object;
TCP = class(TComponent)
private
{ Private 宣言 }
FCopyFiles,
FPasteFiles: TStrings;
FOnRenderFormat: TOnRenderFormatEvent;
WHandle: THandle;
FOldWinProc: Pointer;
FMyProcInstance: Pointer;
FRenderCount: integer;
procedure CopyProc;
procedure RenderCopyProc;
procedure PasteProc;
procedure WndProc(var Mess: TMessage);
procedure SetCopyFiles(const Value: TStrings);
protected
{ Protected 宣言 }
public
{ Public 宣言 }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Copy;
procedure RenderCopy;
procedure Paste;
published
{ Published 宣言 }
property CopyFiles: TStrings read FCopyFiles write SetCopyFiles;
property PasteFiles: TStrings read FPasteFiles;
property OnRenderFormat: TOnRenderFormatEvent read FOnRenderFormat write FOnRenderFormat;
end;
procedure Register;
implementation
constructor TCP.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
// サブクラス化でWndProcを横取り
if not(csDesigning in ComponentState) then
if Owner is TCustomForm then
with TCustomForm(Owner) do
begin
WHandle:=Handle;
FMyProcInstance := MakeObjectInstance(WndProc);
FOldWinProc :=Pointer(GetWindowLong(WHandle, GWL_WNDPROC));
SetWindowLong(WHandle, GWL_WNDPROC, Integer(FMyProcInstance));
end;
FCopyFiles := TStringList.Create;
FPasteFiles := TStringList.Create;
end;
destructor TCP.Destroy;
begin
FCopyFiles.Free;
FPasteFiles.Free;
OpenClipboard(WHandle);
EmptyClipboard;
CloseClipboard;
inherited Destroy;
end;
// メッセージ処理
procedure TCP.WndProc(var Mess: TMessage);
begin
case Mess.Msg of
// 遅延レンダリング
WM_RENDERFORMAT:
begin
// 最初はエクスプローラのコンテキストメニュー表示前にコールされる
// ため何回か処理をスキップする
if FRenderCount < 3 then
begin
Inc(FRenderCount);
//SetClipboardData(CF_HDROP, 0);これはなくても良いみたい
end else begin
if Assigned(FOnRenderFormat) then
FOnRenderFormat(nil);
end;
end;
end;
// 元のWndProcをコールする
Mess.Result := CallWindowProc(FOldWinProc, WHandle,
Mess.Msg,Mess.wParam,Mess.lParam);
end;
// ペースト処理
procedure TCP.PasteProc;
var
srcbuf : PChar;
bufhndl : HGLOBAL;
FileList : PDROPFILES;
c, gs: integer;
fname: string;
begin
OpenClipBoard(WHandle);
// CF_HDROPフォーマット?
bufhndl := GetClipBoardData(CF_HDROP);
if bufhndl = 0 then
begin
CloseClipBoard;
Exit;
end;
FPasteFiles.Clear;
srcbuf:=GlobalLock(bufhndl);
if srcbuf <> nil then
begin
gs := GlobalSize(bufhndl);
// TDROPFILES構造体の構築
FileList:=PDROPFILES(srcbuf);
// WideCharフォーマットで格納時
if FileList^.fWide then
begin
// ヘッダ情報のスキップ
Inc(srcbuf, SizeOF(TDROPFILES));
fname := '';
c := 0;
// このコードがうごくがどうかは未確認(^^;
while True do
begin
while srcbuf^ <> #0 do
begin
fname := fname + srcbuf^;
Inc(srcbuf);
Inc(c);
end;
FPasteFiles.Add(WideCharToString(PWideChar(fname)));
Inc(srcbuf, 2);
Inc(c, 2);
if (srcbuf^ = #0) or (c >= gs) then
Break;
fname := '';
end;
// Ansiコード
end else begin
//ヘッダ情報のスキップ
Inc(srcbuf, SizeOF(TDROPFILES));
fname := '';
c := 0;
while True do
begin
//#0でファイルを切り出す
while srcbuf^ <> #0 do
begin
fname := fname + srcbuf^;
Inc(srcbuf);
Inc(c);
end;
FPasteFiles.Add(fname);
Inc(srcbuf);
Inc(c);
//#0が二つ続けば終り。またメモリサイズを越えた場合も終了する
if (srcbuf^ = #0) or (c >= gs) then
Break;
fname := '';
end;
end;
end;
GlobalUnlock(bufhndl);
EmptyClipBoard;
CloseClipBoard;
end;
// コピー処理
procedure TCP.CopyProc;
var
srcbuf : PChar;
bufhndl : HGLOBAL;
FileList : PDROPFILES;
i: integer;
fname: string;
begin
OpenClipboard(WHandle);
try
// グローバルメモリのハンドルを取得する
bufhndl:=GlobalAlloc(GMEM_MOVEABLE, $8000);
if bufhndl<>0 then
begin
// メモリーを固定して、ポインタを得る
srcbuf:=GlobalLock(bufhndl);
if srcbuf<>NIL then
begin
FillChar(srcbuf^,$8000,0); // データ内容を0でクリア
// TDROPFILES構造体の構築
FileList:=PDROPFILES(srcbuf);
with FileList^ do
begin
pFiles:=SizeOF(TDROPFILES);
pt.x:=0;
pt.y:=0;
fNC:=FALSE;
fWide:=FALSE; // ANSI文字列(ShiftJIS文字列)を使用する
end;
// srcbufをTDROPFILESポインタの後ろへ移動
Inc(srcbuf, SizeOF(TDROPFILES));
// コピーファイル名を入れる
for i := 0 to FCopyFiles.Count - 1 do
begin
fname := FCopyFiles.Strings[i];
StrPCopy(srcbuf, fname);
Inc(srcbuf, Length(fname) + 1);
end;
// 成功したらアンロックして、メモリハンドルをクリップボードに渡す
GlobalUnlock(bufhndl);
SetClipBoardData(CF_HDROP,bufhndl);
end else
GlobalFree(bufhndl); // 失敗したらメモリーを解放
end;
finally
CloseClipBoard;
end;
end;
// 遅延レンダリングコピー
procedure TCP.RenderCopyProc;
begin
OpenClipBoard(WHandle);
EmptyClipBoard;
// 何のことはない、データに0をセットしてコピーするだけ
SetClipBoardData(CF_HDROP, 0);
CloseClipBoard;
end;
// CopyFilesのセット
procedure TCP.SetCopyFiles(const Value: TStrings);
begin
FCopyFiles.Assign(Value);
end;
// コピーメソッド
procedure TCP.Copy;
begin
if FCopyFiles.Count > 0 then
CopyProc;
end;
// ペーストメソッド
procedure TCP.Paste;
begin
PasteProc;
end;
// 遅延レンダリングコピーメソッド
procedure TCP.RenderCopy;
begin
FRenderCount := 0;
RenderCopyProc;
end;
procedure Register;
begin
RegisterComponents('User6', [TCP]);
end;
end.
このユニットの使用は改変や一部流用も含めて自由です。
戻る