我想自定义个剪贴板格式 怎么做??(198分)

  • 主题发起人 主题发起人 999aaasss
  • 开始时间 开始时间
{ How to copy/paste custom items to/from the clipboard
}

// The TClipboard provides easy clipboard access. But what if you
// want to add (several) custom defined items to the clipboard?

// For all actions is the unit Clipboard required.
uses Clipboard;

// First you have to register your own clipboard format
// This can be done ny a Windows API function (unit ShellAPI)
const
MyClipboardFormatStr = 'MyData';

var
MyClpFormat : integer;

MyClpFormat:=RegisterClipboardFormat(MyClipboardFormatStr);

// The variable SLMClpFormat will contain a unique format handle for
// your own clipboard format.
// Then you can start copy items to the clipboard. You will need a
// function for pointer incrementation which is given here:
procedure IncPointer(var p : Pointer;increment : integer);
begin
p:=PChar(p)+Increment;
end;

// Say you have a data record defined as
type
PMyDataRec = ^TMyDataRec;
TMyDataRec = record
Name : string[50];
Value : integer;
end;
// This will work of course also with other types

// Furthermore let's say the data records are stored in a Listbox
// and shall be copied to a list box.

// Copy like this:
procedure TForm1.CopyItems;
var
i : integer;
dh : THandle;
ic : integer;
p : Pointer;
pi : pInteger;

begin
ic:=List1.SelCount
{ get number of items to be copied }
dh:=GlobalAlloc(GMEM_FIXED or GMEM_ZEROINIT,(SizeOf(TMyDataRec)*ic)+SizeOf(Integer));
{ alloc memory for all items plus for a integer variable giving you the number of
copied items }
p:=GlobalLock(dh)
{ Lock the allocated memory }
pi:=pInteger(p);
pi^:=ic
{ write number of items to allocated memory }
IncPointer(p,SizeOf(Integer))
{ increment the pointer behind the written data }
// You don't have to create an instance of clipboard, this is done automatically

for i:=1 to List1.Items.Count do { check all items if they are selected }
begin
if List1.Items[i-1].Selected then
begin
{ This one is selected -> copy it o the clipboard }
PMyDataRec(p)^:=PMyDataRec(List1.Items[i-1].Data)^;
{ of course data must point to a TMyDataRec }
IncPointer(p,SizeOf(TMyDataRec))
{ increment the pointer behind the written data }
end;
end;

// You have now filled the allocated memory with all items that shall be copied.
// Now you can put them to the clipboard
Clipboard.Open
{ Open the clipboard will prevent overwriting of so far copied items }
Clipboard.Clear
{ Clear the clipboard first }
Clipboard.SetAsHandle(MyClpFormat,Dh)
{ Copy to clipboard }
Clipboard.Close
{ finally close the clipboard }
GlobalUnlock(dh);
{ and unlock the allocate memory. But don't free it, it will be used by the clipboard }

if ic=0 then
GlobalFree(dh)
{ You can free it if you haven't copied anything }
end;

// Check first if your items are still available before pasting them from the clipbard

if Clipboard.HasFormat(MyClpFormat) then
begin
Form1.Paste1.Enabled:=true
{ Yes, they are still available }
end;

// And this is, how you paste them after Paste1 is clicked
procedure TMDIForm.Paste1Click(Sender: TObject);
var
dh : THandle;
pdr : PSLMDataRec;
i,ic : integer;
p : Pointer;
pi : pInteger;
li : TListItem;

begin
if Clipboard.HasFormat(MyClpFormat) then
// We have already checked, but maybe another application has overwritten the
// clipboard in between....
begin
ClipBoard.Open
{ First open the clipboard again }
dh:=Clipboard.GetAsHandle(MyClpFormat)
{ Catch the handle to the stored items }
p:=GlobalLock(dh)
{ and lock it }
pi:=pInteger(p)
{ The first item is an integer giving the number of items }
ic:=pi^
{ so get the number of items }
IncPointer(p,SizeOf(integer))
{ increment the pointer behind the read data }
for i:=1 to ic do { get all copied items one after another }
begin
li:=List1.Items.Add
{ first create a new listbox item }
pdr:=New(PMyDataRec)
{ Then create a new pointer to a TMyDataRec }
pdr^:=PMyDataRec(p)^
{ and fill it with data from the clipboard }
IncPointer(p,SizeOf(TSLMDataRec))
{ increment the pointer behind the written data }

li.Data:=pdr
{ Set the data pointer of the list item to the new record }
LI.Caption:=pdr^.Name
{ Let the item display the record field "Name" }

// You can of course add more record fields if the item has subitems:
LI.SubItems.Add(IntTostr(Value));

end
{ All data retrieved from clipboard }
Clipboard.Close
{ Close it }
GlobalUnlock(dh);
{ and unlock the pointer, but don't free it. This will be done by the clipboard itself,
if necessary }
end;
end;



--------------------------------------------------------------------------------


问题提出/摘要:

我想使用剪贴板来保存一定格式的数据,但是我只想单独写一套函数来从流(Stream)中输入或者输出数据。用TMemoryStream可能吗?





回答:

这不仅是可能的,而且也这正是Borland实现它的Clipboard.GetComponent和Clipboard.SetComponent的方法。基本上,你需要调用RegisterClipboardFormat()来注册你自己的剪贴板格式。



CF_MYFORMAT := RegisterClipboardFormat('My Format Description')




然后,你将要做以下步骤:

1. 创建一个MemoryStream并写入数据.

2. 创建一个全局内存区(global memory buffer)并将流复制到其中。

3. 调用Clipboard.SetAsHandle()将它放在剪贴板上。



示例:



var

hbuf : THandle


bufptr : Pointer


mstream : TMemoryStream


begin

mstream := TMemoryStream.Create


try

{-- 将你的数据写入流(Stream) --}

hbuf := GlobalAlloc(GMEM_MOVEABLE, mstream.size)


try

bufptr := GlobalLock(hbuf)


try

Move(mstream.Memory^, bufptr^, mstream.size)


Clipboard.SetAsHandle(CF_MYFORMAT, hbuf)


finally

GlobalUnlock(hbuf)


end


except

GlobalFree(hbuf)


raise


end


finally

mstream.Free


end


end




注意:不要释放你使用GlobalAlloc()分配的缓冲区!一旦你将它放到剪贴板上,就交由剪贴板来释放它。当你取回它时,同样,不要释放它,仅仅是复制它的内容。



要取回流(Stream)和它的数据,做以下代码:



var

hbuf : THandle


bufptr : Pointer


mstream : TMemoryStream


begin

hbuf := Clipboard.GetAsHandle(CF_MYFORMAT)


if hbuf <> 0 then begin

bufptr := GlobalLock(hbuf)


if bufptr <> nil then begin

try

mstream := TMemoryStream.Create


try

mstream.WriteBuffer(bufptr^, GlobalSize(hbuf))


mstream.Position := 0


{-- 从流中读取数据 --}

finally

mstream.Free


end


finally

GlobalUnlock(hbuf)


end


end


end


end

别人告诉我的 试试吧
 
接受答案了.
 
后退
顶部