Not too long ago, the Windows Clipboard represented the only means for applications to exchange data with each other. Before the glorious days of OLE embedding and Drag and Drop, users had to use clipboard operations such as cutting, copying, and pasting to transfer data from one application to another, or even to move data within the same application.
These days, the clipboard is often forgotten; yet just because it is overshadowed by OLE, it does not mean that applications can stop supporting clipboard operations. Furthermore, various clipboard-related concepts survive even when applications exchange data using more advanced methods.
What exactly is the clipboard? Perhaps it is best defined as a Win32 facility where applications can place data. Such data becomes accessible to all applications. Data can be retrieved in a variety of formats, some of which are supported by the operating system, some by applications.
Applications place data on the clipboard using the SetClipboardData function. In addition to providing a handle to the data object, this function also accepts a parameter specifying the format of the data. Applications are encouraged to provide data in a variety of formats; for example, a word processor program may place data on the clipboard using both a private format and a plain text format that is usable by other applications such as the Notepad.
The three types of clipboard formats available to applications include standard formats, registered formats, and private formats.
A multitude of standard clipboard formats exists, identified by symbolic constants. These formats are summarized in Table 15.1. In the cases when the application is supposed to provide a handle of a specific type when calling SetClipboardData, the handle type is indicated. In other cases, the handle passed to SetClipboardData is typically a handle to a block of memory allocated via the GlobalAlloc function.
Format Type |
Description |
Text Formats | |
CF_OEMTEXT |
Text containing characters from the OEM character set |
CF_TEXT |
Text containing characters from the ANSI character set |
CF_UNICODETEXT |
Text containing Unicode characters |
Bitmap formats | |
CF_BITMAP |
Device-dependent bitmap (HBITMAP) |
CF_DIB |
Device independent bitmap (HBITMAPINFO) |
CF_TIFF |
Tagged Image File Format |
Metafile formats | |
CF_ENHMETAFILE |
Enhanced metafile (HENHMETAFILE) |
CF_METAFILEPICT |
Windows Metafile (METAFILEPICT) |
Substitute formats for private formats | |
CF_DSPBITMAP |
Bitmap representation of private data |
CF_DSPENHMETAFILE |
Enhanced metafile representation of private data |
CF_DSPMETAFILEPICT |
Metafile representation of private data |
CF_DSPTEXT |
Text representation of private data |
Sound formats | |
CF_RIFF |
Resource Interchange File Format |
CF_WAVE |
Standard wave file format audio data |
Special formats | |
CF_DIF |
Data Interchange Format from Software Arts |
CF_OWNERDISPLAY |
Data displayed by the owner of the clipboard data |
CF_PALETTE |
Color palette (HPALETTE) |
CF_PENDATA |
Microsoft Pen Extensions data |
CF_PRIVATEFIRST through CF_PRIVATELAST |
Private data |
CF_SYLK |
Microsoft Symbolic Link format |
Windows 95 only formats | |
CF_GDIOBJFIRST through CF_GDIOBJLAST |
Application-defined GDI objects |
CF_HDROP |
List of files (HDROP) |
CF_LOCALE |
Locale information for CF_TEXT data |
Under certain circumstances, Windows is capable of synthesizing data in formats not explicitly provided by an application. For example, if the application provides data in CF_TEXT format, Windows can render that data in the CF_OEMTEXT format at the request of another application. Windows can perform this conversion of formats between the text formats CF_TEXT, CF_OEMTEXT, and (under Windows NT) CF_UNICODETEXT; the bitmap formats CF_BITMAP and CF_DIB; and the metafile formats CF_ENHMETAFILE and CF_METAFILEPICT. Finally, Windows can also synthesize a CF_PALETTE format from the CF_DIB format.
Applications that need to place data on the clipboard in a format other than any of the standard formats can register a new clipboard format using the RegisterClipboardFormat function. For example, an application that wishes to place RTF text on the clipboard may make the following call to register this format:
cfRTF = RegisterClipboardFormat("Rich Text Format");
If several applications call RegisterClipboardFormat with the same format name, the format is only registered once.
There are many clipboard formats registered by Windows. For example, some registered formats are related to OLE, some others to the Windows 95 shell. The name of a registered format can be obtained by calling the GetClipboardFormatName function.
Sometimes it is not necessary for an application to register a new clipboard format. This is the case when the clipboard is used, for example, to transfer data internally within the application and the data is not expected to be used by other applications. For such application-defined private formats, an application can use the CF_PRIVATEFIRST through CF_PRIVATELAST range of values.
In order to enable clipboard viewers to display data stored in a private format, the clipboard owner must provide data in any of the display formats CF_DSPBITMAP, CF_DSPTEXT, CF_DSPMETAFILEPICT, or CF_DSPENHMETAFILE. These formats are identical to their standard counterparts (CF_BITMAP, CF_TEXT, CF_METAFILEPICT, and CF_ENHMETAFILE) except that they are used solely for display purposes and not for pasting.
In order to utilize the clipboard, an application has to perform a variety of operations. These include setting up the clipboard data, obtaining ownership of the clipboard, transferring the data, and responding the clipboard-related events. The application should also provide, as part of its user interface, clipboard-specific user commands (such as commands under its Edit menu).
Before data can be transferred to the clipboard, an application has to do two things. First, the data object must be allocated; second, ownership of the clipboard must be obtained.
The data object must be a handle. This handle can refer to a block of memory allocated using GlobalAlloc with the GMEM_MOVEABLE and GMEM_DDESHARE flags (note that the presence of the GMEM_DDESHARE flag does not indicate that the block of memory is shared between applications); or it can be a handle to a GDI object such as a bitmap. It is important to note that once the handle is passed to the clipboard, the application transfers the object's ownership; it should no longer lock the object and should definitely not make an attempt to delete it.
The application obtains ownership of the clipboard by opening the clipboard using OpenClipboard and then emptying the clipboard by calling the EmptyClipboard function. All handles to data that was previously transferred to the clipboard will be freed. Next, the application transfers data to the clipboard using SetClipboardData and closes it by calling CloseClipboard.
The application can call SetClipboardData multiple times if data is available in several formats. For example, an application may call SetClipboardData using the CF_DIB and CF_ENHMETAFILE formats to provide a graphic image in both bitmap and metafile forms.
Delayed rendering is a performance-enhancing technique that are most useful for applications that routinely place large blocks of data on the clipboard.
An application can specify delayed rendering by passing NULL as the second parameter of SetClipboardData. The system informs the application that data in a specific format must be rendered by sending the application a WM_RENDERFORMAT message. In response to this message, the application must call SetClipboardData and place the requested data on the clipboard.
An application that placed data on the clipboard using delayed rendering may also receive a WM_RENDERALLFORMATS message. This message is sent to the clipboard owner before it is destroyed to ensure that the data on the clipboard remains available to other applications.
When processing a WM_RENDERFORMAT or WM_RENDERALLFORMATS message, the application must not open the clipboard before calling SetClipboardData or close it afterwards.
An application can use the IsClipboardFormatAvailable function to determine if data in a specific format is available on the clipboard. If it wishes to obtain a copy of the data on the clipboard, the application can call OpenClipboard, followed by a call to GetClipboardData. The handle obtained by calling GetClipboardData will not be assumed to remain persistent; applications should immediately copy any data associated with that handle, preferably before calling CloseClipboard. After the call to CloseClipboard, it is possible for other applications to empty the clipboard, rendering the handle obtained through GetClipboardData useless.
The IsClipboardFormatAvailable function can also be used to update the application's Edit menu items. For example, if IsClipboardFormatAvailable indicates that no clipboard data is available in a format that the application understands, the application should disable its Paste command.
Applications can also obtain information about the data formats available in the clipboard by calling CountClipboardFormats or EnumClipboardFormats.
Edit controls have built-in clipboard support (also supported by the edit control in combo boxes). Edit controls respond to a series of messages by performing clipboard operations. When receiving a WM_COPY message, edit controls copy the current selection to the clipboard using the CF_TEXT format. When receiving a WM_CUT message, edit controls transfer the current selection to the clipboard using the CF_TEXT format and erase the selection from the control. In response to a WM_PASTE message, edit controls take the clipboard contents (if anything is available in the CF_TEXT format) and use it to replace the current selection. Finally, edit controls also process the WM_CLEAR message (erasing the current selection).
There are several Windows messages associated with the clipboard.
Applications that use delayed rendering must process the WM_RENDERFORMAT and WM_RENDERALLFORMATS messages.
The WM_DESTROYCLIPBOARD message is sent to the clipboard owner when the contents of the clipboard are destroyed. In response to this message, an application may free up any resources associated with rendering or drawing clipboard items.
A series of messages is sent to applications that place data on the clipboard using the CF_OWNERDISPLAY format. These include WM_ASKCBFORMATNAME, WM_DRAWCLIPBOARD, WM_HSCROLLCLIPBOARD, WM_VSCROLLCLIPBOARD, and WM_PAINTCLIPBOARD.
Another set of messages is sent to or used by clipboard viewer applications.
A clipboard viewer is an application that displays the current contents of the clipboard. An example of a clipboard viewer is the Windows Clipboard Viewer application.
A clipboard viewer merely exists for the user's convenience and does not disrupt or alter clipboard operations.
There can be several clipboard viewers in operation. An application inserts a window in the chain of clipboard viewers by calling the SetClipboardViewer function with the handle of the window. Once added to the chain, the clipboard viewer will receive WM_CHANGECBCHAIN and WM_DRAWCLIPBOARD messages. The clipboard viewer can remove itself from the chain by calling the ChangeClipboardChain function.
The program shown in Listing 15.1 (yes; yet another Hello, World program) puts all this nice theory into practice. This very simple application provides an implementation for the four basic clipboard commands: Cut, Copy, Paste, and Delete. The program's resource file is shown in Listing 15.2, and its header file in Listing 15.3. To compile this application from the command line, you need to enter the following two commands in a DOS window (still not complex enough to warrant a make file):
rc hellocf.rc cl hellocf.c hellocf.res user32.lib gdi32.lib
#include <windows.h> #include "hellocf.h" HINSTANCE hInstance; char *pszData; void DrawHello(HWND hwnd) { HDC hDC; PAINTSTRUCT paintStruct; RECT clientRect; if (pszData != NULL) { hDC = BeginPaint(hwnd, &paintStruct); if (hDC != NULL) { GetClientRect(hwnd, &clientRect); DPtoLP(hDC, (LPPOINT)&clientRect, 2); DrawText(hDC, pszData, -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); EndPaint(hwnd, &paintStruct); } } } void CopyData(HWND hwnd) { HGLOBAL hData; LPVOID pData; OpenClipboard(hwnd); EmptyClipboard(); hData = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, strlen(pszData) + 1); pData = GlobalLock(hData); strcpy((LPSTR)pData, pszData); GlobalUnlock(hData); SetClipboardData(CF_TEXT, hData); CloseClipboard(); } void DeleteData(HWND hwnd) { free(pszData); pszData = NULL; InvalidateRect(hwnd, NULL, TRUE); } void PasteData(HWND hwnd) { HANDLE hData; LPVOID pData; if (!IsClipboardFormatAvailable(CF_TEXT)) return; OpenClipboard(hwnd); hData = GetClipboardData(CF_TEXT); pData = GlobalLock(hData); if (pszData) DeleteData(hwnd); pszData = malloc(strlen(pData) + 1); strcpy(pszData, (LPSTR)pData); GlobalUnlock(hData); CloseClipboard(); InvalidateRect(hwnd, NULL, TRUE); } void SetMenus(HWND hwnd) { EnableMenuItem(GetMenu(hwnd), ID_EDIT_CUT, pszData ? MF_ENABLED : MF_GRAYED); EnableMenuItem(GetMenu(hwnd), ID_EDIT_COPY, pszData ? MF_ENABLED : MF_GRAYED); EnableMenuItem(GetMenu(hwnd), ID_EDIT_PASTE, IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(GetMenu(hwnd), ID_EDIT_DELETE, pszData ? MF_ENABLED : MF_GRAYED); } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_COMMAND: switch (LOWORD(wParam)) { case ID_FILE_EXIT: DestroyWindow(hwnd); break; case ID_EDIT_CUT: CopyData(hwnd); DeleteData(hwnd); break; case ID_EDIT_COPY: CopyData(hwnd); break; case ID_EDIT_PASTE: PasteData(hwnd); break; case ID_EDIT_DELETE: DeleteData(hwnd); break; } break; case WM_PAINT: DrawHello(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_INITMENUPOPUP: if (LOWORD(lParam) == 1) { SetMenus(hwnd); break; } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow) { HWND hwnd; MSG msg; WNDCLASS wndClass; HANDLE hAccTbl; pszData = malloc(14); strcpy(pszData, "Hello, World!"); hInstance = hThisInstance; if (hPrevInstance == NULL) { memset(&wndClass, 0, sizeof(wndClass)); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszMenuName = "HelloMenu"; wndClass.lpszClassName = "Hello"; if (!RegisterClass(&wndClass)) return FALSE; } hwnd = CreateWindow("Hello", "Hello", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); hAccTbl = LoadAccelerators(hInstance, "HelloMenu"); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(hwnd, hAccTbl, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; }
#include "windows.h" #include "hellocf.h" HelloMenu MENU BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Edit" BEGIN MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT, GRAYED MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY, GRAYED MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE, GRAYED MENUITEM "&Delete\tDel", ID_EDIT_DELETE, GRAYED END END HelloMenu ACCELERATORS BEGIN "X", ID_EDIT_CUT, VIRTKEY, CONTROL "C", ID_EDIT_COPY, VIRTKEY, CONTROL "V", ID_EDIT_PASTE, VIRTKEY, CONTROL VK_DELETE, ID_EDIT_DELETE, VIRTKEY END
#define ID_FILE_EXIT 1000 #define ID_EDIT_CUT 1001 #define ID_EDIT_COPY 1002 #define ID_EDIT_PASTE 1003 #define ID_EDIT_DELETE 1004
To see how this application works, try using its clipboard functions. You can use the Cut or Copy functions to copy the text it displays to the clipboard; you can also use another application (for example, the Windows Notepad) to create a block of text, copy it to the clipboard, and then paste it into this application.
This program has a simple data object; a pointer that is initially set to point to the character string "Hello, World!" The application also has a simple set of menus containing an Edit menu with the clipboard functions Cut, Copy, Paste, and Delete.
Clipboard operations are performed in response to the user selecting these Edit menu commands. The function CopyData copies the current string to the clipboard by first gaining ownership of it through EmptyClipboard and then performing a SetClipboardData call. The function PasteData copies data from the clipboard; it does so by first freeing any current data, and then obtaining clipboard data by calling GetClipboardData.
The function SetMenus updates the enabled state of menu items in the Edit menu based on the availability of data in the CF_TEXT format on the clipboard. If no such data is available, the Paste command is disabled. The state of the Cut, Copy, and Delete menu items is also updated to reflect whether the application has any data that can be placed on the clipboard.
To ensure that the application's window is properly updated when the data changes, both the DeleteData and the PasteData functions call InvalidateRect.
Note that the data handle allocated in CopyData is never freed by the application. After this handle has been passed to the clipboard, freeing it (when the clipboard is emptied) is no longer the responsibility of the application. Similarly, the application never frees the handle obtained through GetClipboardData (in PasteData); after the clipboard data has been obtained and the clipboard is closed, this handle is simply discarded.
The Windows clipboard represents one of the oldest mechanisms for transferring data between applications. The clipboard is a Windows facility where applications can place data in a variety of formats, to be retrieved later by other applications.
Windows defines several standard clipboard formats. Applications can also register additional clipboard formats or use private clipboard formats.
An application transfers data to the clipboard by first gaining ownership of it calling the function EmptyClipboard. It may transfer data immediately or choose to use delayed rendering by passing a NULL handle to the SetClipboardData function. When using delayed rendering, applications must process the WM_RENDERFORMAT and WM_RENDERALLFORMATS messages.
Edit controls (and the edit control parts of combo boxes) have built-in clipboard support. They respond the WM_CUT, WM_COPY, WM_PASTE, and WM_CLEAR messages by performing a cut, copy, paste, or delete operation using data in the CF_TEXT format.
Clipboard viewers are programs that show the current contents of the clipboard. These programs merely serve the user's convenience and do not alter the clipboard's contents or affect clipboard operations.