Free VC++ Tutorial

Web based School

Previous Page Main Page Next Page


15 — The Windows Clipboard

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.

Clipboard Formats

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.

Standard Clipboard 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.

    Table 15.1. Standard clipboard formats.
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.

Registered Formats

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.

Private Formats

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.

Clipboard Operations

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).

Transferring Data to the Clipboard

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

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.

Pasting Data from the Clipboard

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.

Controls and the Clipboard

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).

Clipboard Messages

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.

Clipboard Viewers

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.

A Simple Implementation

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
    Listing 15.1. A simple clipboard-aware application.
#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;

}
    Listing 15.2. Resource file for the sample clipboard-aware application.
#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
    Listing 15.3. Header file for the sample clipboard-aware application.
#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.

Summary

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.

Previous Page Main Page Next Page