With just a few lines of code, you can easily add Clipboard functionality to your application.
Introduction
In this first instalment of a four-part series of articles on programmatically transferring data to and from the Windows Clipboard, I'll explain the basic steps of using the Clipboard using the Clipboard
API. After I've gone over the basics steps, which are used no matter what data format is on the Clipboard, I'll present a demo application that illustrates how to programmatically transfer simple (ANSI) text to and from the Clipboard.
Using the Windows Clipboard API
There are actually two distinct mechanisms for interfacing to the Clipboard. The first involves using the Windows Clipboard
API and the second uses OLE. Since the Clipboard
API is by far the most common method used, most of the demos in this little series will use this technique. If you're familiar with the Windows API - especially the means by which memory is allocated via the GlobalAlloc
and GlobalLock
functions - the steps needed to use the Clipboard will be all the easier to learn. Another thing to realize at this point is that regardless of the type of data being transferred to the clipboard, the same basic programmatic steps are taken any time you are transferring data to or from the Clipboard. Figure 1 shows these steps in a standard UML Sequence Diagram with the subsequent sections going into more detail about each step.
Figure 1: These are the standard steps used to transfer data to the Clipboard using the Windows Clipboard API.
Allocating Memory for Your Data
The verbiage "placing data on the Clipboard" is really a misnomer as the Clipboard is not some sort of global data buffer. Actually, the Clipboard is little more than a handle to a data buffer that is created and maintained by the application that is making this data available for other applications. Since the data must be accessible from all processes, the Windows API functions, GlobalAlloc
and GlobalLock
, are used.
As a quick refresher, here is the syntax for these two functions and how they work:
The GlobalAlloc
function is used to allocate a global block of memory that will hold the entirety of the data being made available via the Clipboard.
HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes)
The uFlags
parameter is used to specify to Windows how the memory is to be allocated. If you specify a value of 0 (or NULL
), then a value of GMEM_FIXED
is assumed. The valid values for this parameter are in the Table 1 and can be combined with the logical or operator.
Table 1 - GlobalAlloc flags
uFlag Value | Description |
GHND | Same as GMEM_MOVEABLE | GMEM_ZEROINT |
GMEM_FIXED | The default value, this allocated a block of fixed (i.e., non-moveable) memory. The return value from GlobalAlloc when specifying GMEM_FIXED is a pointer. |
GMEM_MOVEABLE | In Windows, memory blocks are never moved in physical memory. However, the can be moved within the default heap. Therefore, the return value when specifying GMEM_MOVEABLE is a handle to the memory. |
GPTR | This value combines GMEM_FIXED and GMEM_ZEROINT , the later simply initializing the allocated memory to all zeroes. |
Note - Since specifying a value of GMEM_FIXED
for the uFlag
parameter to the GlobalAlloc
call will return a pointer while specifying GMEM_MOVEABLE
will return a handle, these two values are mutually exclusive.
The parameter dwBytes
is a double word that enables you to specify how large a buffer you wish to allocate.
The second function that you need to call to allocate the memory for your data is the Windows API function, GlobalLock
.
LPVOID GlobalLock(HGLOBAL hMem)
This function is very straight-forward and takes as its only parameter a handle returned from the GlobalAlloc
function.
Copy the Data into the Global Memory
Once you've allocated the global memory and have obtained a pointer to it, you can copy the desired data into that global memory buffer. How you do this will depend on what type of data you're transferring so I'll go into more detail about this step in the three demos that follow this article.
Unlock the Global Memory
This is a very important, yet often overlooked step in the process. According to the Windows documentation, once you've allocated the memory and inserted the data to be made public to other processes, you are not to touch this memory again. The reason is simple. Once you've made the Clipboard mechanism aware of the new data (next step), Windows will now have control over that memory and any further tampering with it on your end might very well invalidate the integrity of the data.
To unlock the global memory block, make a call to the GlobalUnlock
API function. Here's the very simple syntax for this function where the hMem
parameter is the handle returned from the GlobalAlloc
function.
BOOL GlobalUnlock(HGLOBAL hMem)
Open the Clipboard
The OpenClipboard
function enables you to lock the Clipboard so that no other processes can modify its contents as you are attempting to access that data. This function's syntax is as follows:
BOOL OpenClipboard(HWND hWndNewOwner)
The hWndNewOwner
simply allows you to associate the open Clipboard with a given window. If you don't need to do this (most don't), simply leave specify a value of NULL
for this parameter, in which case, Windows will associate the open Clipboard with the current task. All of the demos in this book will use this latter technique as I've personally never seen too many situations where an association with a specific window was needed.
Empty the Clipboard
This step is needed to initialized the Clipboard and is accomplished via the EmptyClipboard
function. When you call this function, Windows releases the global memory associated with the Clipboard. If you'll remember, earlier I said that Windows would be responsible for this task. Therefore, it is the responsibility of any application using the Clipboard to call this function in order to prevent memory leaks.
BOOL EmptyClipboard()
Note that you do not need to call a function to determine if any data exists prior to calling this function as this function will only return a nonzero value if the function fails. If it succeeds or if the Clipboard doesn't contain any data, the function returns zero (representing success).
Set the Clipboard Data
Finally, at this step, you set the Clipboard data. As mentioned earlier, the Clipboard really doesn't contain data, but simply maintains a global handle obtained (and populated) by your application. The function to perform this step is the SetClipboardData
function. This is also the function that enables you to specify the format of the data being transferred (notice syntax below).
HANDLE SetClipboardData(UINT uFormat, HANDLE hMem)
The value you specify for the uFormat
parameter must be either a standard Clipboard format (Table 2) or a registered format. Registered formats are user-defined formats that enable you to specify formats for application-specific data. We'll be getting into this format later on.
Table 2 - Standard Clipboard Formats
uFormat value | hMem value |
CF_BITMAP | Handle to a bitmap (you'll see how to use this format in this article series' second demo) |
CF_DIB | Handle to a BITMAPINFO structure followed by the bits that constitute the bitmap |
CF_DIBV5 | Used in Windows 2000 only, this is also a handle to a BITMAPINFO structure where the subsequent bits represent the bitmap color information and the bitmap image. |
CF_DIF | Handle to a Software Arts' Data Interchange Format (SADIF) buffer |
CF_DSPBITMAP | Handle to a bitmap display format associated with an application-specific format. The data being pointed to must be data that can be displayed in bitmap format. |
CF_DSPENHMETAFILE | Handle to an enhanced metafile display format associated with a private format. In this case, the data must be displayable in enhanced metafile format in lieu of the privately formatted data. |
CF_DSPMETAFILEPICT | Handle to a metafile-picture display format associated with a private format. The data being pointed to must be displayable in metafile-picture format in lieu of the privately formatted data. |
CF_DSPTEXT | Handle to text display format associated with a private format. The data must be displayable in text format in lieu of the privately formatted data. |
CF_ENHMETAFILE | Handle to an enhanced metafile (HENHMETAFILE ) |
CF_GDIOBJFIRST through CF_GDIOBJLAST | This range of integers represents application-defined GDI object clipboard formats. Note that the handles represented by these values are not automatically deleted using the GlobalFree function when the clipboard is emptied. Additionally, the hMem parameter is not a handle to a GDI object, but is a handle allocated by the GlobalAlloc function with the GMEM_MOVEABLE flag. |
CF_HDROP | Handle to type HDROP that identifies a list of files being transferred via the Clipboard - typically used in drag & drop operations. The DragQueryFiles function is used to retrieve information about these files. |
CF_LOCALE | Handle to the locale identifier associated with the text in the clipboard |
CF_METAFILEPICT | Handle to a metafile picture format (METAFILEPICT ) structure |
CF_OEMTEXT | Handle to a text format containing characters in the OEM character set. In this format, each line must be terminated with a carriage return/linefeed (CR/LF) combination. A null character represents end of data (EOD) using this format. |
CF_OWNERDISPLAY | If an application specifies a uFormat value of CF_OWNERDISPLAY , the hMem value must be NULL . In addition, the application is then responsible for displaying and updating the Clipboard viewer window and handling the following messages: WM_ASKCBFORMATNAME , WM_HSCROLLCLIPBOARD , WM_PAINTCLIPBOARD , WM_SIZECLIPBOARD , WM_VSCROLLCLIPBOARD . |
CF_PALETTE | Handle to a color palette. |
CF_PENDATA | Handle to data representing pen extensions for the Microsoft Windows for Pen Computing. |
CF_PRIVATEFIRST through CF_PRIVATELAST | Much like using the CF_GDIOBJFIRST -CF_GDIOBJLAST range, these integers represent a series of value for private Clipboard formats. Note once again that the data associated with these handles is not freed automatically and as such it is the application's responsibility to do such. |
CF_RIFF | Handle to an audio data format that is more complex than can be represented with the standard (WAV) format where the uFormat parameter is set to CF_WAVE . |
CF_SYLK | Handle to Microsoft Symbolic Link (SYLK) formatted data. |
CF_TEXT | Handle to standard text. Using this format, each line must be terminated with a carriage return/linefeed (CR-LF) combination. A null character denotes end of data (EOD). This is used for ANSI text whereas CF_UNICODETEXT is used for UNICODE text. |
CF_WAVE | Handle to the audio data in one of the standard wave formats: such as 11 kHz or 22 kHz pulse code modulation (PCM). |
CF_TIFF | Handle to tagged-image file (TIFF) formatted data |
CF_UNICODETEXT | For use with Windows NT/2000 or later, this format is used for UNICODE data as opposed to ANSI text (which is represented by the CF_TEXT value). |
Close the Clipboard
When an application has finished examining or modifying the Clipboard data, the CloseClipboard
function is called. This has the effect of unlocking the Clipboard so that other applications can have access to it.
BOOL CloseClipboard()
Demo to Transfer Simple Text
The first demo we'll start with will illustrate how to transfer text to and from the Clipboard. While this is not needed as much as it once was due to the Windows 2000/Me context menu (right-clicking over any edit control will result in a menu containing the standard Clipboard functions) there are still some very valid uses for this capability. One example is if you want to pass simple text-based data to another application. However, in order to keep the example as clean as possible, this demo will consist of a dialog with two edit controls. One edit control will enable you to type text into it and copy that text to the Clipboard while the second edit control will be a read-only control. A button will enable you to paste text from the Clipboard into this second control. By keeping the demo relatively simple, it's much easier for you to focus on the specifics of working with the Clipboard and it's also more convenient when you want to copy and paste the salient code from this demo into your own project files.
Creating the SimpleTextTransfer Demo Project
At this time, create a dialog-based application called SimpleTextTransfer
. Once you've done that, modify the default dialog so that it looks like the one shown in Figure 2.
Figure 2: Simple dialog to test using the Clipboard to transfer standard ANSI text.
After you've modified the dialog, you'll need to make the following changes to the dialog's controls.
- Set the
Multiline
property for both edit controls to True
. - Set the
AutoVScroll
property for both edit controls to True
. - Set the Vertical Scroll property for both edit controls to
True
. - Set the
Readonly
property for "from Clipboard" edit control to True
. - Create a control member variable for each of the two edit controls. Name them
m_edtToClipboard
and m_edtFromClipboard
, respectively.
Copying Text to the Clipboard
Once you've finished with the dialog controls' properties, add an event handler for the Copy button's BN_CLICKED
message and modify it so that it looks like the following. I've placed comments throughout the code to make the code easy to understand.
void CSimpleTextTransferDlg::OnBnClickedBtncopy()
{
if (UpdateData())
{
CString strData;
m_edtToClipboard.GetWindowText(strData);
if (OpenClipboard())
{
EmptyClipboard();
HGLOBAL hClipboardData;
hClipboardData = GlobalAlloc(GMEM_DDESHARE,
strData.GetLength()+1);
char * pchData;
pchData = (char*)GlobalLock(hClipboardData);
strcpy(pchData, LPCSTR(strData));
GlobalUnlock(hClipboardData);
SetClipboardData(CF_TEXT,hClipboardData);
CloseClipboard();
}
}
}
Cutting Text to the Clipboard
As you might have guessed, the only difference between copying data to the Clipboard and cutting it to the Clipboard is that in the latter case, the data is removed after the transfer takes place. Therefore, at this point, add an event handler for the Cut button's BN_CLICKED
message. Once you've done that, modify that handler so that it looks as follows:
void CSimpleTextTransferDlg::OnBnClickedBtncut()
{
OnBnClickedBtncopy();
m_edtToClipboard.SetWindowText("");
}
Pasting Text from the Clipboard
At this point, the application can place data on the clipboard, but it doesn't yet allow for the pasting of that data to the dialog. Therefore, we'll take care of that now.
As with the Copy and Cut buttons, add an event handler for the Paste button's BN_CLICKED
message.
void CSimpleTextTransferDlg::OnBnClickedBtnpaste()
{
if (OpenClipboard())
{
HANDLE hClipboardData = GetClipboardData(CF_TEXT);
char *pchData = (char*)GlobalLock(hClipboardData);
CString strFromClipboard = pchData;
m_edtFromClipboard.SetWindowText(strFromClipboard);
GlobalUnlock(hClipboardData);
CloseClipboard();
}
}
Testing the SimpleTextTransfer Demo
At this point, compile and test the application. Your results should be similar to what you see in Figure 3.
Figure 3: With just a few lines of code, you can easily add Clipboard functionality to your application.
However, there is just one problem with our little test. If you click the <Alt><Print Screen> key combination, this copies a bitmap of the current window to the Clipboard. You can test this by opening the PaintBrush
application and pasting the image into a new bitmap image. However, if you copy an image to the Clipboard and then click the Paste button on the demo application, the text in the "to clipboard" edit control is wiped out and nothing appears! This is because the demo is attempting to use the data on the Clipboard without first determining if the data is valid for this application.
In order to check as to the format of the data on the Clipboard, simply use the IsClipboardAvailable
function.
BOOL IsClipboardFormatAvailable(UINT format)
The format
parameter can be any value listed in Table 1. Here's the modified BN_CLICKED
handler for the dialog's Paste button. I've bolded the lines that have changed from the previous incarnation of this function.
void CSimpleTextTransferDlg::OnBnClickedBtnpaste()
{
if (OpenClipboard())
{
if (::IsClipboardFormatAvailable(CF_TEXT)
|| ::IsClipboardFormatAvailable(CF_OEMTEXT))
{
HANDLE hClipboardData = GetClipboardData(CF_TEXT);
char *pchData = (char*)GlobalLock(hClipboardData);
CString strFromClipboard = pchData;
m_edtFromClipboard.SetWindowText(strFromClipboard);
GlobalUnlock(hClipboardData);
}
else
{
AfxMessageBox("There is no text data (ANSI) on the Clipboard.");
}
CloseClipboard();
}
}
Now, if you run this application, copy a bitmap (or any data that is not defined as CF_TEXT
or CF_OEMTEXT
) and press the demo's Paste button, you will see the message shown in Figure 4.
Figure 4: You should always check the format of the data on the Clipboard before attempting to use it.
Summary
In this first of a four-part series on programming the Windows Clipboard, you learned the basics of using the Windows API to transfer data to and from the Clipboard. You then put that new-found knowledge to work with a demo application using simple ANSI text. In the next instalment of this series, I'll go into sending and receiving bitmap images from and to the Clipboard.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.