Using the Clipboard, Part I : Transferring Simple Text

In this first article of a four-part series on learning everything you ever wanted to know about programming the Windows Clipboard, you'll discover the basic steps of using the Clipboard API to transfer simple (ANSI) text to and from the Clipboard.

With just a few lines of code, you can easily add Clipboard functionality to your application.

<!-- Add the rest of your HTML here -->

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 associated 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

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 a 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);
// test to see if we can open the clipboard first before
// wasting any cycles with the memory allocation
if (OpenClipboard())
{
// Empty the Clipboard. This also has the effect
// of allowing Windows to free the memory associated
// with any data that is in the Clipboard
EmptyClipboard();
// Ok. We have the Clipboard locked and it's empty.
// Now let's allocate the global memory for our data.
// Here I'm simply using the GlobalAlloc function to
// allocate a block of data equal to the text in the
// "to clipboard" edit control plus one character for the
// terminating null character required when sending
// ANSI text to the Clipboard.
HGLOBAL hClipboardData;
hClipboardData = GlobalAlloc(GMEM_DDESHARE,
strData.GetLength()+1);
// Calling GlobalLock returns to me a pointer to the
// data associated with the handle returned from
// GlobalAlloc
char * pchData;
pchData = (char*)GlobalLock(hClipboardData);
// At this point, all I need to do is use the standard
// C/C++ strcpy function to copy the data from the local
// variable to the global memory.
strcpy(pchData, LPCSTR(strData));
// Once done, I unlock the memory - remember you
// don't call GlobalFree because Windows will free the
// memory automatically when EmptyClipboard is next
// called.
GlobalUnlock(hClipboardData);
// Now, set the Clipboard data by specifying that
// ANSI text is being used and passing the handle to
// the global memory.
SetClipboardData(CF_TEXT,hClipboardData);
// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
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:

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()
{
// Test to see if we can open the clipboard first.
if (OpenClipboard())
{
// Retrieve the Clipboard data (specifying that
// we want ANSI text (via the CF_TEXT value).
HANDLE hClipboardData = GetClipboardData(CF_TEXT);
// Call GlobalLock so that to retrieve a pointer
// to the data associated with the handle returned
// from GetClipboardData.
char *pchData = (char*)GlobalLock(hClipboardData);
// Set a local CString variable to the data
// and then update the dialog with the Clipboard data
CString strFromClipboard = pchData;
m_edtFromClipboard.SetWindowText(strFromClipboard);
// Unlock the global memory.
GlobalUnlock(hClipboardData);
// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
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()
{
// Test to see if we can open the clipboard first.
if (OpenClipboard())
{
if (::IsClipboardFormatAvailable(CF_TEXT)
|| ::IsClipboardFormatAvailable(CF_OEMTEXT))
{// Retrieve the Clipboard data (specifying that
// we want ANSI text (via the CF_TEXT value).
HANDLE hClipboardData = GetClipboardData(CF_TEXT);
// Call GlobalLock so that to retrieve a pointer
// to the data associated with the handle returned
// from GetClipboardData.
char *pchData = (char*)GlobalLock(hClipboardData);
// Set a local CString variable to the data
// and then update the dialog with the Clipboard data
CString strFromClipboard = pchData;
m_edtFromClipboard.SetWindowText(strFromClipboard);
// Unlock the global memory.
GlobalUnlock(hClipboardData);
}
else
{
AfxMessageBox("There is no text data (ANSI) on the Clipboard.");
}// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
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 the 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.

Comments and Discussions

I need to use CF_DIBV5 format from clipboard. However I can't include wingdi.h because it is not allowed within SDK I am using (Adobe). Is there any other way i can access CF_DIBV5 data from clipboard in this format?

Visual Basic 6, Visual C++ 6
We have a custom clipboard format registered using RegisterClipboardFormat. This has been used between several applications written in C++ with MFC for many years.
Recently, we have identified a need for these applications to interoperate with Visual Basic applications. We have created a Visual Basic application which is able to receive drops from C++ applications or other VB applications; however when we attempt to drag data from a VB application to an MFC application the C++ application detects that the custom data format is present but is unable to access the actual data as GetGlobalData returns NULL.
I would be most greatful if anyone has any ideas to resolve this problem. ? Not using VB would be nice but not an option unfortunately.

The above code allows multiple VB applications to interoperate as either source or destination of drag and drop operations, including dropping of objects from C++ applications. When we attempt to drop to C++ application with code as follows the application is unable to lock the global data.

hello Sir,
I am writing a program which need to get the highlighted
words from Windows's applications( say, IE, Netscape etc.), but
clipboard only save words that been "copied",or "cut". i suppose
the clipboard should have pointer, or something to locate the address
of the highlighted words, but i couldn't find any methods to get the
highlighted words, if you know how to do so, could you pls greatly help
in my works?~~ or anyone who noticed this message could help?

The clipboard only "knows" about information that has been explicitly copied to it. In the case of the user simply selecting text in an edit window of an app, that's not going to take place. I would imagine that you could do this, but it would take some work on your part. You might look into things like determining the active app or window, enumerating its controls for edit windows that have selected text. I'm fairly sure that would work, but it would take a couple of hours to figure it out as I've never had the need for this before. Hopefully this will at least point you in the right direction.

I am looking at a problem that you may help me on.
I was looking at approaches for finding the screen position [ x, y ] for the object in the clipboard. So, if there is an text object(say through selection from a notepad) that was copied to the clipboard, I would like to determine its absolute position w.r.t to the window screen.

Is this even possible ? Can you help me out with some approaches to attack this problem ?

I wrote a little clipboard application and I want that every time
a CTRL+V is pressed to perform some action in my application that will
affect the data in the clipboard, repost it to the clipboard and display
it on the screen.

lets say, that I did CTRL+C on "hello" and now pressed CTRL+V.
my application will convert to upercase "HELLO" and I want it pasted
on screen (of course without the smallercase "hello")

I have talked to people who said maybe catching an interrupt of CTRL+V
and creating one after manipulation - but, can I make sure my CTRL+V
interrupt is done before it is pasted by the OS on the screen ?

Sir:
This time I am just writting a program about simple manipulating Clipboard. On my disk MSDN , I looked up some samples . some of them are complex ,or some are invoving in other wider aspect . so your code is simple . It is what I need for my this project !!! Thank you very much !!!

I'm trying to write a program that uses the standard calendar from VC6 when i compile i get this message
CI'm trying to write a program that uses the standard
calendar from VC++6 when I compile I get this message
C:\prefab\ENGINERRING\dpf\dpfDlg.cpp(101) : error
C2065: 'IDC_MONTHCALENDAR1' : undeclared identifier
MICROSOFT WEB SITE SAID-

CAUSE
The database dialog data exchange cover routine generated
by microsoft visual c++6.0 AppWizar for exchange data
between the Date Time Picker control and the recordset
member variable is DDX_FieldDateTimeCtrl(). This function
is omitted from the Visual C++ 6.0 release code.

I understand that ms left the code out so it doesn't
Compile. However, how do I fix it?

Please if you can be, of any help let me know.
Also, if you know of any sites or books etc that can help
me better understand windows programming do not hesitate
to let me know. Thank you all in advance.

Sorry about never responding. You wrote this last year when my wife was in the hospital and so I was a bit preoccupied. Now that she's home and better I'm finally cleaning up outstanding questions and posts.

Having said that, I only have VS.NET on all my machines. However, you can easily create a VC6 solution with the same name and simply copy my source files (there's only about 2 or 3 that have changed from the appwiz generated versions) over yours. That would get you up and running in less than 5 minutes.

Many more people will see and derive benefits from my articles on CodeProject than on my own site.

I am dramatically reconstructing my site. A year ago I had thought about doing a post-CodeGuru site, but I just don't have the time to do that. Therefore, instead of articles being the focus, my site will become more of a place to support my books and advertise my consulting and programming practice.

Having said that, I'll be removing the articles and posting them here; about once a week or every two weeks as I literally have a few dozen I can contribute to my fellow CPians.

Tom Archer wrote:Many more people will see and derive benefits from my articles on CodeProject than on my own site.

Quite on the target, Tom.

Tom Archer wrote:A year ago I had thought about doing a post-CodeGuru site, but I just don't have the time to do that

Yeah, I figured this a time ago, because, you only updated the book section.

Tom Archer wrote:my site will become more of a place to support my books and advertise my consulting and programming practice.

IMHO, a wise choice. You already have some reputation on programming besides being a avid pool player(playing for days at 10-12 hours a day , ouch ), so It's better to capitalize on this , and of course posting here your articles will definitely reach a major audience , improving your reputation of pool addict ( a computer geek is nothing new and probaly all the CPians already know this about you )