如何正确在剪贴板存入自定义格式的内容﹖

Frank6600 2010-01-13 10:55:38
var
PRec: ^TMyRec;
Data: THandle;
begin
Data := GlobalAlloc(GMEM_DDESHARE, SizeOf(TMyRec));
PRec := GlobalLock(Data);

PRec.name := '张三';
PRec.age := 99;

GlobalUnlock(Data);
Clipboard.SetAsHandle(CF_MY, Data);

网上找到上列例子,例子中 TMyRec 是一个记录结构:

TMyRec = record
name: string[8];
age : Byte;
end;

这个例子中,我不明白的是 SizeOf 的部份。
一个记录结构,要算 Size 是容易的,尤其上例中 string 后还加了一个[8]限定长度。

但,如果要存入的是长度不定的呢﹖

例如存入一个 TStringList 的内容。
难道也用 sizeof(TStringList) 吗﹖

我要实现的比 TStringList 还要难点:基本上是 Linked List 结构。
每一个Linked List的节点内容,保存的是HTML,长度不一定。
我写一个示意代码:

TNode = class
public
HTML: string;
procedure AddChild(Node: TNode);
procedure AddBefore(Node: TNode);
Procedure AddAfter(Node: TNode);
end;

TMyTree = class
public
RootNode: TNode;
procedure AddNode(ParentNode: TNode; str: string);
end;

类似这样。
最终我要把这整个 Tree 都存入剪贴板,怎么做啊﹖

(注:Tree的形状、Node数直到黏贴前才确定。Node.HTML 的长度也是直到黏贴前才确定)



...全文
208 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
Frank6600 2010-01-14
  • 打赏
  • 举报
回复
好了,感谢各位,初步的思路有了,我按这个思路去解决吧!
Frank6600 2010-01-13
  • 打赏
  • 举报
回复
> sizeof只不过是取结构占的内存大小,这不是主要问题,主要的问题是你知道如何用上面那几个函数分配内> 存并加入剪贴板,这个自己变通一下就行了,比如你的Html是String,那保存个String不就行了:

听起来像是包的概念。
那复杂结构的Copy与Paste就必须用封包与解包的概念来实现了﹖
wzwcn 2010-01-13
  • 打赏
  • 举报
回复
sizeof只不过是取结构占的内存大小,这不是主要问题,主要的问题是你知道如何用上面那几个函数分配内存并加入剪贴板,这个自己变通一下就行了,比如你的Html是String,那保存个String不就行了:
var
P: Pchar;
H: THandle;
Html:string;//你的string
begin
//..你的html 长度不定
H:= GlobalAlloc(GMEM_DDESHARE, length(Html)); //分配一块内存
P:= GlobalLock(H);//取得指向内存块的指针
move(pchar(Html)^,p^,length(Html));
GlobalUnlock(H);
Clipboard.SetAsHandle(CF_MY,H);

大概就这样,上面代码随手写的没测试
dd_zhouqian 2010-01-13
  • 打赏
  • 举报
回复
没看懂,帮顶
hjkto 2010-01-13
  • 打赏
  • 举报
回复
我有一个监视剪切板的例子,不知你需要不?
需要的话 ,留下联系方式
我打包给你
iseekcode 2010-01-13
  • 打赏
  • 举报
回复
你可以找一下这本书的随书源码,除了这个类,作者还给出了应用实例。

move方法作者的解释在上面。
DELPHI7帮助文档的:

Description
Move copies Count bytes from Source to Dest. No range checking is performed. Move compensates for overlaps between the source and destination blocks.

Whenever possible, use the global SizeOf function (Delphi) or the sizeof operator (C++) to determine the count.
Frank6600 2010-01-13
  • 打赏
  • 举报
回复
iseekcode 提供的“D5开发人员指南英文版里的一个自定义剪贴板操作类”,
如果我理解得没错,其本质就是封包,是吧﹖

它的封包原理用的是最最简单的,即用CRLF把它们接起来。
不过没看懂它怎么解包的。

// Copy the data to the TDataRec field
Move(DataPtr^, Rec, Size)

这方法不太理解,怎么没看到以 CRLF 分解的过程。


金卯刀 2010-01-13
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 iseekcode 的回复:]
如果是deep copy且跨进程,可不可以先将对象序列化或者叫串行化,通过进程间通信解决?
[/Quote]
若都是delphi程序,那好說 ^_^
Frank6600 2010-01-13
  • 打赏
  • 举报
回复


uses Clipbrd;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
Clipboard.SetComponent(Tree);
Tree.Name:='MyTree';
Clipboard.GetComponent(Self,panel1);//Owner-->self; Parent--->panel
end;
...
initialization
RegisterClasse([TTree]);//若有其他類型,也要注冊...
...



这方法挺酷的,虽然不太相信这方法能成,但很好奇它的实现逻辑是怎么样的。
iseekcode 2010-01-13
  • 打赏
  • 举报
回复
如果是deep copy且跨进程,可不可以先将对象序列化或者叫串行化,通过进程间通信解决?
金卯刀 2010-01-13
  • 打赏
  • 举报
回复
另外,只要是TCompent類型的,也可以用delphi提供的TClipBoard中的方法,如下:

uses Clipbrd;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
Clipboard.SetComponent(Tree);
Tree.Name:='MyTree';
Clipboard.GetComponent(Self,panel1);//Owner-->self; Parent--->panel
end;
...
initialization
RegisterClasse([TTreeView]);//若有其他類型,也要注冊...
...


在同一進程中,若知道拷貝的對象是什么具體類型,則可用assign;若不知道,則可用此法...
金卯刀 2010-01-13
  • 打赏
  • 举报
回复
但你要用剪切板,不知道是否有跨進程這個因素?若是這樣,問題就不那么簡單了....
金卯刀 2010-01-13
  • 打赏
  • 举报
回复
你的需求看起來是對象拷貝中的 Deep Copy。
了解一下你所使用的對象中的assign方法
iseekcode 2010-01-13
  • 打赏
  • 举报
回复
下面是D5开发人员指南英文版里的一个自定义剪贴板操作类:

A Unit That Defines Custom Clipboard

unit cbdata;
interface

uses
SysUtils, Windows, clipbrd;

const
DDGData = ‘CF_DDG’; // constant for registerin

type
// Record data to be stored to the clipboard
TDataRec = packed record
LName: string[10];
FName: string[10];
MI: string[2];
Age: Integer;
BirthDate: TDateTime;
end;

{ Define an object around the TDataRec that contains the methods
for copying and pasting the data to and from the clipboard }

TData = class
public
Rec: TDataRec;
procedure CopyToClipBoard;
procedure GetFromClipBoard;
end;

var
CF_DDGDATA: word; // Receives the return value of RegisterClipboardFormat().
implementation

procedure TData.CopyToClipBoard;
{ This function copies the contents of the TDataRec field, Rec, to the
clipboard as both binary data, as text. Both formats will be
available from the clipboard }
const
CRLF = #13#10;
var
Data: THandle;
DataPtr: Pointer;
TempStr: String[50];
begin
// Allocate SizeOf(TDataRec) bytes from the heap
Data := GlobalAlloc(GMEM_MOVEABLE, SizeOf(TDataRec));
try
// Obtain a pointer to the first byte of the allocated memory
DataPtr := GlobalLock(Data);
try
// Move the data in Rec to the memory block
Move(Rec, DataPtr^, SizeOf(TDataRec));
{ Clipboard.Open must be called if multiple clipboard formats are
being copied to the clipboard at once. Otherwise, if only one
format is being copied the call isn’t necessary }
ClipBoard.Open;
// First copy the data as its custom format
ClipBoard.SetAsHandle(CF_DDGDATA, Data);
// Now copy the data as text format
with Rec do
TempStr := FName+CRLF+LName+CRLF+MI+CRLF+IntToStr(Age)+CRLF+
DateTimeToStr(BirthDate);
ClipBoard.AsText := TempStr;
{ If a call to Clipboard.Open is made you must match it
with a call to Clipboard.Close }
Clipboard.Close
finally
// Unlock the globally allocated memory
GlobalUnlock(Data);
end;
except
{ A call to GlobalFree is required only if an exception occurs.
Otherwise, the clipboard takes over managing any allocated
memory to it.}
GlobalFree(Data);
raise;
end;
end;

procedure TData.GetFromClipBoard;
{ This method pastes memory saved in the clipboard if it is of the
format CF_DDGDATA. This data is stored in the TDataRec field of
this object. }
var
Data: THandle;
DataPtr: Pointer;
Size: Integer;
begin
// Obtain a handle to the clipboard
Data := ClipBoard.GetAsHandle(CF_DDGDATA);
if Data = 0 then Exit;
// Obtain a pointer to the memory block referred to by Data
DataPtr := GlobalLock(Data);
try
// Obtain the size of the data to retrieve
if SizeOf(TDataRec) > GlobalSize(Data) then
Size := GlobalSize(Data)
else
Size := SizeOf(TDataRec);
// Copy the data to the TDataRec field
Move(DataPtr^, Rec, Size)
finally
// Free the pointer to the memory block.
GlobalUnlock(Data);
end;
end;

initialization
// Register the custom clipboard format
CF_DDGDATA := RegisterClipBoardFormat(DDGData);
end.

This unit performs several tasks. First, it registers the new format with the Win32 Clipboard by calling the RegisterClipboardFormat() function. This function returns a value that identifies this new format. Any application that registers this same format, as specified by the string para-meter, will obtain the same value when calling this function. The new format is also available on the ClipBoard’s list of formats, which can be accessed by the Clipboard.Formats property.

The unit also defines the record containing the data to be placed onto the Clipboard and the object that encapsulates this record. The record, TDataRec, has string fields to hold a person’s name, an integer field to hold the person’s age, and a TDataTime field to hold the person’s birth date.

The object encapsulating TDataRec, TData, defines the methods CopyToClipboard() and
GetFromClipboard().

16,748

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 语言基础/算法/系统设计
社区管理员
  • 语言基础/算法/系统设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧