1. Technology

Listening to the Clipboard: Clipboard Delphi Spy with Custom Clipboard Formats

Receiving Clipboard Change Notifications


  1. «TClipboard Has Limitations
  2. »Working with Custom Clipboard Formats

Clipboard Spy

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.

Delphi Clipboard Spy

As a demo project we'll create a simple clipboard viewer application that displays the text in the Windows clipboard in a Memo component. The content of a memo is updated every time when the text in clipboard changes (format: cf_text).

To attach our application to the clipboard viewer notification chain we call the SetClipboardViewer function in the OnCreate event handler procedure of the main form.

 procedure TForm1.FormCreate(Sender: TObject) ;
  NextInChain := SetClipboardViewer(Handle) ;
The NextInChain variable is of THandle type, declared as global in the form1's unit. NextInChain represents our program as the next window in the clipboard viewer chain.
   Form1: TForm1;
   NextInChain : THandle;
 uses ClipBrd;
To perform a certain task whenever the contents of the clipboard changes we need to receive (and respond) to a WM_DrawClipboard message. The following procedure checks whether there is a "new" text stored in a clipboard and if so pastes this text to a memo component on a form. In any case after responding to WM_DrawClipboard message application must pass it to the next clipboard viewer.
 procedure TForm1.WMDrawClipboard(var Msg:TMessage) ;
  if Clipboard.HasFormat(cf_text) then
   //do something with other clipboard formats
  //pass the message on to the next window
  if NextInChain <> 0 then
   SendMessage(NextInChain, WM_DrawClipboard, 0, 0)
It may seem that this is all we have to code, but... The WM_ChangeCBChain messages (chain fixing functions) needs to be handled every time any other clipboard viewer chain is changed. The message brings with it the information about the window being removed and the next window in the clipboard viewer chain.
 procedure TForm1.WMChangeCBChain(var Msg: TMessage) ;
   Remove, Next: THandle;
   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)
Finally, when we are ready to terminate our application we have to call the ChangeClipboardChain API function to remove our window from the chain of clipboard viewers. Naturally in the OnDestroy event handler:
 procedure TForm1.FormDestroy(Sender: TObject) ;
   ChangeClipboardChain(Handle, NextInChain) ;
I have merely forgot this one: we, of course, have to declare all those message handling procedure in the, let's say, private part of the forms interface section:
 procedure WMDrawClipboard(var Msg: TMessage) ; message WM_DRAWCLIPBOARD;
 procedure WMChangeCBChain(var Msg: TMessage) ; message WM_CHANGECBCHAIN; 
To see this code in real action, compile and run this project. Try cutting and copying some text to the clipboard...whoa, the text is displayed in our application window! We have produced a real clipboard textual spy.
  1. «TClipboard Has Limitations
  2. »Working with Custom Clipboard Formats

©2014 About.com. All rights reserved.