Listening to the Clipboard

Post date: Mar 16, 2010 3:33:37 PM

An application can be notified of changes in the data stored in the Windows clipboard by registering itself as a Clipboard Viewer.

Clipboard viewers use two API calls and several messages to communicate with the Clipboard viewer chain. SetClipboardViewer adds a window to the beginning of the chain and returns a handle to the next viewer in the chain. ChangeClipboardChain removes a window from the chain.

When a clipboard change occurs, the first window in the clipboard viewer chain is notified via the WM_DrawClipboard message and must pass the message on to the next window. To do this, our application must store the next window along in the chain to forward messages to and also respond to the WM_ChangeCBChain message which is sent whenever any other clipboard viewer on the system is added or removed to ensure the next window along is valid.

Note: Add Memo1 to the Form1.

Read more: http://delphi.about.com/od/windowsshellapi/a/clipboard_spy_2.htm

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TMember = record
    Name : string;
    eMail : string;
    Posts : Cardinal;

end;

type
  TForm1 = class(TForm)
    Memo1: TMemo;

procedure FormCreate(Sender: TObject);

procedure FormDestroy(Sender: TObject);

private

procedure WMDrawClipboard(var Msg: TMessage); message WM_DRAWCLIPBOARD;

procedure WMChangeCBChain(var Msg: TMessage); message WM_CHANGECBCHAIN;

public

end;

var
  Form1        : TForm1;
  NextInChain  : THandle;
  DelphiGuide  : TMember;
  OurFormat    : Integer;
  MemberHandle : THandle;
implementation
{$R *.DFM}
uses ClipBrd;
procedure TForm1.WMDrawClipboard(var Msg:TMessage);
var MemberPointer : ^TMember;
    MemberInClip  : THandle;
    AMember       : TMember;
begin

if Clipboard.HasFormat(cf_text) then begin

  Memo1.Lines.Clear;
  Memo1.PasteFromClipboard;

end else

if Clipboard.HasFormat(OurFormat) then begin

if OpenClipboard(Handle) then begin

     MemberInClip:=GetClipboardData(OurFormat);
     MemberPointer := GlobalLock(MemberInClip);

with AMember do begin

       Name  := MemberPointer^.Name;
       eMail := MemberPointer^.eMail;
       Posts := MemberPointer^.Posts;

end;

     GlobalUnLock(MemberInClip);
     CloseClipboard();

with Memo1.Lines do begin

      Clear;
      Add('Clipboard has TMember data:');
      Add(AMember.Name);
      Add(AMember.email);
      Add(IntToStr(AMember.Posts));

end;

end;

end;

if NextInChain <> 0 then

  SendMessage(NextInChain, WM_DrawClipboard, 0, 0)
end;
procedure TForm1.WMChangeCBChain(var Msg: TMessage);
var
  Remove, Next: THandle;
begin
  Remove := Msg.WParam;
  Next := Msg.LParam;

with Msg do

if NextInChain = Remove then

   NextInChain := Next

else if NextInChain <> 0 then

   SendMessage(NextInChain, WM_ChangeCBChain, Remove, Next)
end;
procedure TForm1.FormCreate(Sender: TObject);
var MemberPointer  : ^TMember;
begin
 NextInChain:=SetClipboardViewer(Handle);
 DelphiGuide.Name  := 'Zarko Gajic';
 DelphiGuide.eMail := 'delphi.guide@about.com';
 DelphiGuide.Posts := 15;
 OurFormat:=RegisterClipboardFormat('CF_TMember');

if OpenClipboard(Handle) then begin

   EmptyClipboard;
   MemberHandle := GlobalAlloc(GMEM_DDESHARE or GMEM_MOVEABLE, SizeOf(TMember));
   MemberPointer := GlobalLock(MemberHandle);
   MemberPointer^.Name  := DelphiGuide.Name;
   MemberPointer^.eMail := DelphiGuide.eMail;
   MemberPointer^.Posts := DelphiGuide.Posts;
   GlobalUnLock(MemberHandle);
   SetClipboardData(OurFormat,MemberHandle);
  CloseClipboard();

end;

end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  GlobalFree(MemberHandle);
  ChangeClipboardChain(Handle, NextInChain);
end;
end.