Create a Windows Clipboard Monitor in C# using SetClipboardViewer
Introduction
Applications such as
GetRight
and
ClipMate seem to magically know when something is copied to
the Clipboard. How is it done? This example shows you how to use
the Win32 API function SetClipboardViewer
to create a Clipboard Viewer application.
Using SetClipboardViewer
The Win32 API contains a function SetClipboardViewer
which allows an application to register itself as a viewer of
Clipboard events. When an item is copied to the Clipboard,
windows notifies the Clipboard Viewer by sending the
WM_DRAWCLIPBOARD message. The application can then use the
appropriate function to get the contents of the Clipboard and
perform processing.
Example Application
I had a great idea one day (after copying and pasting internet links a few hundred times) to create a small application that would read the Clipboard when something was copied to it and extract any Internet URLs, UNC paths or filenames that it found and put them on a menu in the system tray.
I spent a little time on it and it is in a working state but it
is not finished. One day I will get around to finishing it off
but for the moment it is a good sample to learn how
SetClipboardViewer works.
![]() |
C# Source Code: RAD_ClipboardMonitorExampleSrc.zip (~50 KB) |
![]() |
VB.NET Example: ClipboardMonitor_VB.txt |
The SetClipboardViewer Function
The SetClipboardViewer function adds the specified
window to the chain of Clipboard Viewers. Clipboard Viewer
windows receive a WM_DRAWCLIPBOARD
message whenever the content of the Clipboard changes.
The SetClipboardViewer API declaration has the
following syntax in C#
[DllImport("User32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
The hWndNewViewer parameter is the handle to the
window to be added to the Clipboard chain.
When SetClipboardViewer is successful, the return
value identifies the next window in the Clipboard Viewer chain.
This is important as a Clipboard Viewer is required to pass
Clipboard messages to the next window in the Clipboard chain.
Setting up the Clipboard Viewer
I need to override the WndProc
method of the underlying Form so that the main form can respond
to window messages from the operating system. Normally .NET
applications do not need to use WndProc
as the .NET Framework takes care of these events.
protected override void WndProc(ref Message m)
{
Next, call SetClipboardViewer to register
the application as a Clipboard Viewer. The window will now
receive the WM_DRAWCLIPBOARD
message. Applications do not normally receive the
WM_DRAWCLIPBOARD
message, only those that call SetClipboardViewer.
_ClipboardViewerNext = SetClipboardViewer(this.Handle);
Responding to the WM_DRAWCLIPBOARD message
After calling SetWindowLong the WindowProc method will
receive the WM_DRAWCLIPBOARD
message when something is copied to the Clipboard.
protected override void WndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
This is the place that I do my processing of the Clipboard data. The .NET Framework has the Clipboard, DataObject and DataFormats classes for working with the Clipboard. Clipboard.GetDataObject gets the current contents of the Clipboard as an IDataObject.
IDataObject iData = new DataObject(); iData = Clipboard.GetDataObject();
As the Clipboard can contain different types of data, use the DataObject.GetDataPresent method to check for a certain type of data. It returns true if the contents of the Clipboard can be converted to the specified type. The DataFormats class contains constants that define the names of some of the more common Clipboard formats.
if (iData.GetDataPresent(DataFormats.Rtf))
{
// ...
Once I have determined that the format is available then the DataObject.GetData method can be used to get the Clipboard content in the required format. The following example gets the contents in Rich Text Format (RTF).
ctlClipboardText.Rtf = (string)iData.GetData(DataFormats.Rtf);
After performing any processing that the application requires
the next window in the Clipboard Viewer chain must be notified
of the Clipboard change. This is done by calling the Windows API
function SendMessage. The same parameters
that were passed to the WndProc method are passed
to SendMessage to be used by the next Clipboard
Viewer in the chain.
SendMessage(_ClipboardViewerNext, m.Msg, m.WParam, m.LParam);
When overriding WndProc I must pass any unhandled
messages to the base class by calling base.WndProc
protected override void WndProc(ref Message m)
{
switch ((Win32.Msgs)m.Msg)
{
case Win32.Msgs.WM_DRAWCLIPBOARD:
// ... process Clipboard
default:
// unhandled window message
base.WndProc(ref m);
break;
The WM_CHANGECBCHAIN Message
The WM_CHANGECBCHAIN message is sent when a window
is being removed from the chain of Clipboard Viewers. Note that
the message is only sent to the first window in the Clipboard
Viewer chain. The first window and subsequent windows in the
chain must use SendMessage to pass the message
along the chain.
If wParam is equal to the handle of the next
Clipboard Viewer then the next Clipboard Viewer is being removed
and I need to update the pointer that refers to the next window
in the chain. m.LParam is a pointer to the
Clipboard Viewer in the chain after the one being removed.
if (m.WParam == _ClipboardViewerNext)
{
_ClipboardViewerNext = m.LParam;
}
else
{
SendMessage(_ClipboardViewerNext, m.Msg, m.WParam, m.LParam);
}
This message can be seen by starting multiple copies of the Clipboard Viewer and watching the Debug output.
Unregistering the Application as a Clipboard Viewer
When the application closes I make sure I call ChangeClipboardChain to remove the application from the Clipboard chain.
ChangeClipboardChain(this.Handle, _ClipboardViewerNext);
And Finally...
There are a few tricks to using the SetClipboardViewer
function but it allows your application to be notified of
Clipboard events.
If you have any comments or feedback please send them via email: Ross Donald (ross @ radsoftware.com.au)
