This section contains code samples for the following tasks:

Implementing Cut, Copy, and Paste Commands

CF_OWNERDISPLAYCF_TEXT

Selecting Data

Before information can be copied to the clipboard, the user must select specific information to be copied or cut. An application should provide a means for the user to select information within a document and some kind of visual feedback to indicate selected data.

Creating an Edit Menu

TranslateAccelerator

WM_INITMENUPOPUP

WM_INITMENUPOPUP

WM_INITMENUPOPUP

case WM_INITMENUPOPUP: InitMenu((HMENU) wParam); break;

InitMenu

```cpp

void WINAPI InitMenu(HMENU hmenu)

{

int cMenuItems = GetMenuItemCount(hmenu);

int nPos;

UINT id;

UINT fuFlags;

PLABELBOX pbox = (hwndSelected == NULL) ? NULL : (PLABELBOX)GetWindowLong(hwndSelected, 0);

for (nPos = 0; nPos < cMenuItems; nPos++)

{

id = GetMenuItemID(hmenu, nPos);

switch (id)

{

case IDM_CUT:

case IDM_COPY:

case IDM_DELETE:

if (pbox == NULL || !pbox->fSelected)

{

fuFlags = MF_BYCOMMAND | MF_GRAYED;

}

else if (pbox->fEdit)

{

if ((id != IDM_DELETE) && (pbox->ichSel == pbox->ichCaret))

{

fuFlags = MF_BYCOMMAND | MF_GRAYED;

}

else

{

fuFlags = MF_BYCOMMAND | MF_ENABLED;

}

}

else

{

fuFlags = MF_BYCOMMAND | MF_ENABLED;

}

EnableMenuItem(hmenu, id, fuFlags);

break;

case IDM_PASTE:

if (pbox != NULL && pbox->fEdit)

{

EnableMenuItem(hmenu, id, IsClipboardFormatAvailable(CF_TEXT) ? MF_BYCOMMAND | MF_ENABLED : MF_BYCOMMAND | MF_GRAYED);

}

else

{

EnableMenuItem(hmenu, id, IsClipboardFormatAvailable(uLabelFormat) ? MF_BYCOMMAND | MF_ENABLED : MF_BYCOMMAND | MF_GRAYED);

}

}

}

}

```

WM_COMMANDWM_COMMANDWM_COMMAND

case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_CUT: if (EditCopy()) EditDelete(); break; case IDM_COPY: EditCopy(); break; case IDM_PASTE: EditPaste(); break; case IDM_DELETE: EditDelete(); break; case IDM_EXIT: DestroyWindow(hwnd); } break;

EditCopyEditPasteEditPaste

Copying Information to the Clipboard

In the Label application, the application-defined EditCopy function copies the current selection to the clipboard. This function is responsible for performing the following actions:

The structure of a label box is defined as follows:

$#define BOX_ELLIPSE 0 #define BOX_RECT 1 #define CCH_MAXLABEL 80 #define CX_MARGIN 12$

$\typedef \begin{pmatrix} struct tag\labelbox \end{pmatrix} { \begin{pmatrix} RECTANGLE rcText; \\ BOOL fSelected; \\ BOOL fEdit; \\ int nType; \\ int ichCaret; \\ int ichSel; \\ int nXCaret; \\ int nXSel; \\ int cchLabel; \\ TCHAR atchLabel[CCH_MAXLABEL]; \end{pmatrix} \labelbox, *P\labelbox; $

以下是重构后的代码:

```cpp

BOOL WINAPI EditCopy(VOID) {

PLABELBOX pbox;

LPTSTR lptstrCopy;

HGLOBAL hglbCopy;

int ich1, ich2, cch;

if (hwndSelected == NULL)

return FALSE;

// Open the clipboard and empty it.

if (!OpenClipboard(hwndMain))

return FALSE;

EmptyClipboard();

// Get a pointer to the structure for the selected label.

pbox = (PLABELBOX)GetWindowLong(hwndSelected, 0);

// If text is selected, copy it using the CF_TEXT format.

if (pbox->fEdit) {

if (pbox->ichSel == pbox->ichCaret) // zero length

{

CloseClipboard(); // selection

return FALSE;

}

if (pbox->ichSel < pbox->ichCaret)

{

ich1 = pbox->ichSel;

ich2 = pbox->ichCaret;

}

else

{

ich1 = pbox->ichCaret;

ich2 = pbox->ichSel;

}

cch = ich2 - ich1;

// Allocate a global memory object for the text.

hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (cch + 1) * sizeof(TCHAR));

if (hglbCopy == NULL)

{

CloseClipboard();

return FALSE;

}

// Lock the handle and copy the text to the buffer.

lptstrCopy = GlobalLock(hglbCopy);

memcpy(lptstrCopy, &pbox->atchLabel[ich1], cch * sizeof(TCHAR));

lptstrCopy[cch] = (TCHAR)0; // null character

GlobalUnlock(hglbCopy);

// Place the handle on the clipboard.

SetClipboardData(CF_TEXT, hglbCopy);

}

// If no text is selected, the label as a whole is copied.

else

{

// Save a copy of the selected label as a local memory object. This copy is used to render data on request. It is freed in response to the WM_DESTROYCLIPBOARD message.

PLABELBOX pboxLocalClip = (PLABELBOX)LocalAlloc(LMEM_FIXED, sizeof(LABELBOX));

if (pboxLocalClip == NULL)

{

CloseClipboard();

return FALSE;

}

memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));

pboxLocalClip->fSelected = FALSE;

pboxLocalClip->fEdit = FALSE;

// Place a registered clipboard format, the owner-display format, and the CF_TEXT format on the clipboard using delayed rendering.

SetClipboardData(uLabelFormat, NULL);

SetClipboardData(CF_OWNERDISPLAY, NULL);

SetClipboardData(CF_TEXT, NULL);

}

// Close the clipboard.

CloseClipboard();

return TRUE;

}

```

```

#include

// Define box types

#define BOX_ELLIPSE 0

#define BOX_RECT 1

#define CCH_MAXLABEL 80

#define CX_MARGIN 12

typedef struct tagLABELBOX {

RECT rcText; // coordinates of rectangle containing text

BOOL fSelected; // TRUE if the label is selected

BOOL fEdit; // TRUE if text is selected

int nType; // rectangular or elliptical

int ichCaret; // caret position

int ichSel; // with ichCaret, delimits selection

int nXCaret; // window position corresponding to ichCaret

int nXSel; // window position corresponding to ichSel

int cchLabel; // length of text in atchLabel

TCHAR atchLabel[CCH_MAXLABEL];

} LABELBOX, *PLABELBOX;

// Edit paste function

void EditPaste() {

OPENCLIPBOARD();

HGLOBAL hMem = GETCLIPBOARDDATA();

LPVOID pMem = GlobalLock(hMem);

CLOSECLIPBOARD();

PLABELBOX pLB = (PLABELBOX)pMem;

RECT rcClient;

RECT rcBox = pLB->rcText;

LONG lLeft = rcBox.left + CX_MARGIN;

LONG lTop = (rcBox.top + (rcBox.bottom - rcBox.top)/2) + CX_MARGIN;

LONG lRight = (rcBox.right - CX_MARGIN);

LONG lBottom = (rcBox.bottom + (rcBox.bottom - rcBox.top)/2) + CX_MARGIN;

WINDOWINFO wndInfo = GetWindowInfoByHandle((HWND)pLB->nXCaret);

LONG lClientWidth = wndInfo.szRectl.right - wndInfo.szRectl.left;

LONG lClientHeight = wndInfo.szRectl.bottom - wndInfo.szRectl.top;

BOOL bRepaint = TRUE;

if ((rcBox.left == pLB->nXCaret && pLB->nXCaret <= lLeft + lClientWidth) || (rcBox.right == pLB->nXCaret && pLB->nXCaret >= lRight)) {

if (lTop > lClientHeight) bRepaint = false;

else if (lBottom < lClientHeight) bRepaint = false;

else if ((pLB->fSelected && IsWindowVisible((HWND)pLB->nXSel)) || (!pLB->fSelected && IsWindowVisible((HWND)pLB->nXCaret))) bRepaint = false;

else if ((!pLB->fEdit && pLB->nType == BOX_ELLIPSE && (rcBox.left != pLB->nXCaret || rcBox.right != pLB->nXCaret)) || (pLB->fEdit && pLB->nType == BOX_RECT)) bRepaint = false;

} else if ((rcBox.top == pLB->nXCaret && pLB->nXCaret <= lTop + lClientHeight) || (rcBox.bottom == pLB->nXCaret && pLB->nXCaret >= lBottom)) {

if (lLeft > lClientWidth) bRepaint = false;

else if (lRight < lClientWidth) bRepaint = false;

else if ((pLB->fSelected && IsWindowVisible((HWND)pLB->nXSel)) || (!pLB->fSelected && IsWindowVisible((HWND)pLB->nXCaret))) bRepaint = false;

else if ((!pLB->fEdit && pLB->nType == BOX_ELLIPSE && (rcBox.top != pLB->nXCaret || rcBox.bottom != pLB->nXCaret)) || (pLB->fEdit && pLB->nType == BOX_RECT)) bRepaint = false;

} else bRepaint = false;

if (bRepaint) InvalidateRect((HWND)pLB->nXCaret, NULL, TRUE);

}

```

以下是重构后的代码:

```cpp

VOID WINAPI EditPaste(VOID) {

PLABELBOX pbox;

HGLOBAL hglb;

LPTSTR lptstr;

PLABELBOX pboxCopy;

int cx, cy;

HWND hwnd;

pbox = hwndSelected == NULL ? NULL : (PLABELBOX)GetWindowLong(hwndSelected, 0);

// 如果应用程序处于编辑模式,获取剪贴板文本。

if (pbox != NULL && pbox->fEdit) {

if (!IsClipboardFormatAvailable(CF_TEXT))

return;

if (!OpenClipboard(hwndMain))

return;

hglb = GetClipboardData(CF_TEXT);

if (hglb != NULL) {

lptstr = GlobalLock(hglb);

if (lptstr != NULL) {

// 调用应用程序定义的ReplaceSelection函数插入文本并重绘窗口。

ReplaceSelection(hwndSelected, pbox, lptstr);

GlobalUnlock(hglb);

}

}

CloseClipboard();

return;

}

// 如果应用程序不处于编辑模式,创建一个标签窗口。

if (!IsClipboardFormatAvailable(uLabelFormat))

return;

if (!OpenClipboard(hwndMain))

return;

hglb = GetClipboardData(uLabelFormat);

if (hglb != NULL) {

pboxCopy = GlobalLock(hglb);

if (pboxCopy != NULL) {

cx = pboxCopy->rcText.right + CX_MARGIN;

cy = pboxCopy->rcText.top * 2 + cyText;

hwnd = CreateWindowEx(WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT, atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy, hwndMain, NULL, hinst, NULL);

if (hwnd != NULL) {

pbox = (PLABELBOX)GetWindowLong(hwnd, 0);

memcpy(pbox, pboxCopy, sizeof(LABELBOX));

ShowWindow(hwnd, SW_SHOWNORMAL);

SetFocus(hwnd);

}

GlobalUnlock(hglb);

}

}

CloseClipboard();

}

```

以下是根据提供的内容重构的代码段:

```cpp

// Register a clipboard format.

// LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp, sizeof(atchTemp)/sizeof(TCHAR));

// uLabelFormat = RegisterClipboardFormat(atchTemp);

// if (uLabelFormat == 0) return FALSE;

void SetClipboardData(UINT format)

{

if (OpenClipboard(hwnd))

{

if (GetClipboardOwner() == hwnd)

{

// Set the clipboard data to the specified format.

// If the format is not supported, the function fails.

// The function returns TRUE if successful or FALSE otherwise.

NULLSetClipboardDataW(format);

}

CloseClipboard();

}

}

void RenderFormat(UINT format)

{

switch (format)

{

case CF_TEXT:

// Code for rendering text format

break;

}

}

case WM_RENDERFORMAT:

RenderFormat((UINT) wParam);

break;

case WM_RENDERALLFORMATS:

if (OpenClipboard(hwnd))

{

if (GetClipboardOwner() == hwnd)

{

RenderFormat(uLabelFormat);

RenderFormat(CF_TEXT);

}

CloseClipboard();

}

break;

```

```c++

typedef struct tagLABELBOX {

RECT rcText; // coordinates of rectangle containing text

BOOL fSelected; // TRUE if the label is selected

BOOL fEdit; // TRUE if text is selected

int nType; // rectangular or elliptical

int ichCaret; // caret position

int ichSel; // with ichCaret, delimits selection

int nXCaret; // window position corresponding to ichCaret

int nXSel; // window position corresponding to ichSel

int cchLabel; // length of text in atchLabel

TCHAR atchLabel[CCH_MAXLABEL];

} LABELBOX, *PLABELBOX;

```

```cpp

void RenderFormat(UINT uFormat)

{

HGLOBAL hglb;

PLABELBOX pbox;

LPTSTR lptstr;

int cch;

if (pboxLocalClip == NULL)

return;

if (uFormat == CF_TEXT)

{

// Allocate a buffer for the text.

cch = pboxLocalClip->cchLabel;

hglb = GlobalAlloc(GMEM_MOVEABLE, (cch + 1) * sizeof(TCHAR));

if (hglb == NULL)

return;

// Copy the text from pboxLocalClip.

lptstr = GlobalLock(hglb);

memcpy(lptstr, pboxLocalClip->atchLabel, cch * sizeof(TCHAR));

lptstr[cch] = (TCHAR)0;

GlobalUnlock(hglb);

// Place the handle on the clipboard.

SetClipboardData(CF_TEXT, hglb);

}

else if (uFormat == uLabelFormat)

{

hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LABELBOX));

if (hglb == NULL)

return;

pbox = GlobalLock(hglb);

memcpy(pbox, pboxLocalClip, sizeof(LABELBOX));

GlobalUnlock(hglb);

// Place the handle on the clipboard.

SetClipboardData(uLabelFormat, hglb);

}

}

```

WM_DESTROYCLIPBOARD is a message that is sent when the clipboard owner window is destroyed. When this message is received, the window procedure checks if pboxLocalClip is not NULL. If it is not NULL, LocalFree() is called to free the memory allocated for pboxLocalClip, and then pboxLocalClip is set to NULL. This step ensures that any resources associated with the clipboard owner window are properly cleaned up when the window is destroyed.

Using the Owner-Display Clipboard Format (CF_OWNERDISPLAY) requires that applications use the CF_OWNERDISPLAY format to store data on the clipboard. This format allows the application that owns the clipboard to modify the content of the clipboard before it is displayed to other windows. To use this format, an application must call SetClipboardData() with the CF_OWNERDISPLAY flag.

The window procedure for the Label application processes these messages by checking if pboxLocalClip is not NULL. If it is not NULL, LocalFree() is called to free the memory allocated for pboxLocalClip, and then pboxLocalClip is set to NULL. This step ensures that any resources associated with the clipboard owner window are properly cleaned up when the window is destroyed.

以下是重构后的代码:

```cpp

LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

RECT rcViewer;

RECT rc;

LPRECT lprc;

LPPAINTSTRUCT lpps;

switch (msg)

{

// Handle other messages.

case WM_PAINTCLIPBOARD:

// Determine the dimensions of the label.

SetRect(&rc, 0, 0, pboxLocalClip->rcText.right + CX_MARGIN, pboxLocalClip->rcText.top * 2 + cyText);

// Center the image in the clipboard viewer window.

if (rc.right < rcViewer.right)

{

rc.left = (rcViewer.right - rc.right) / 2;

rc.right += rc.left;

}

if (rc.bottom < rcViewer.bottom)

{

rc.top = (rcViewer.bottom - rc.bottom) / 2;

rc.bottom += rc.top;

}

// Paint the image, using the specified PAINTSTRUCT structure, by calling the application-defined PaintLabel function.

lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);

PaintLabel(lpps, pboxLocalClip, &rc);

GlobalUnlock((HGLOBAL) lParam);

break;

case WM_SIZECLIPBOARD:

// Save the dimensions of the window in a static RECT structure.

lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);

memcpy(&rcViewer, lprc, sizeof(RECT));

GlobalUnlock((HGLOBAL) lParam);

// Set the scroll ranges to zero (thus eliminating the need to process the WM_HSCROLLCLIPBOARD and WM_VSCROLLCLIPBOARD messages).

SetScrollRange((HWND) wParam, SB_HORZ, 0, 0, TRUE);

SetScrollRange((HWND) wParam, SB_VERT, 0, 0, TRUE);

break;

case WM_ASKCBFORMATNAME:

LoadString(hinst, IDS_OWNERDISPLAY, (LPSTR) lParam, wParam);

break;

default:

return DefWindowProc(hwnd, msg, wParam, lParam);

}

return 0;

}

```

There are three ways to monitor changes to the clipboard: creating a clipboard viewer window, querying the clipboard sequence number, and creating a clipboard format listener. The oldest method is creating a clipboard viewer window, which was first introduced in Windows 2000. However, this approach has been kept for backward compatibility with earlier versions of Windows. On the other hand, Windows Vista added support for clipboard format listeners and the ability to query the clipboard sequence number. It is recommended to use either of these methods instead of creating a clipboard viewer window, especially if new programs are being developed.

Querying the Clipboard Sequence Number

The GetClipboardSequenceNumber function can be used to retrieve the current clipboard sequence number. This function takes no parameters and returns an integer value representing the current clipboard sequence number. To obtain the next sequence number after the current one, simply increment the return value by 1. This method allows for monitoring changes to the clipboard without actually creating a separate window or listening for specific events.

Creating a Clipboard Format Listener

A clipboard format listener is a type of window that has registered to receive notifications when the contents of the clipboard change. This method is simpler to implement and avoids potential problems that may arise if programs fail to properly maintain the clipboard viewer chain or if a window within the chain stops responding to messages. To create a clipboard format listener, you need to register it using the RegisterClipboardFormatNotification function. Once registered, the listener will automatically receive notifications whenever the clipboard content changes.

Adding and Removing Clipboard Format ListenersTo create a Clipboard Viewer Window, you must first add the application as a clipboard format listener using AddClipboardFormatListener. This allows the application to receive messages when the clipboard content changes. Once added, you can remove the listener using RemoveClipboardFormatListener at any time.

Example:

To add an application as a clipboard format listener, use the following code snippet:

AddClipboardFormatListener(hwnd, CF_UNICODETEXT);

To remove an application as a clipboard format listener, use the following code snippet:

RemoveClipboardFormatListener(hwnd);

Creating a Clipboard Viewer Window

A clipboard viewer window is used to display the current content of the clipboard and receive messages when the clipboard content changes. To create a clipboard viewer window, your application should perform the following tasks:

SetClipboardViewer

SetClipboardViewerhwndNextViewer

WM_CHANGECBCHAIN

WM_DRAWCLIPBOARD

Adding a Window to the Clipboard Viewer Chain

In order for the clipboard viewer window to receive messages when clipboard content changes, it must be added to the clipboard viewer chain. This is done by calling SetClipboardViewer with a handle to the new viewer window. The previous viewer window is then set as the next viewer in the chain using SetClipboardViewerhwndNextViewer. Finally, the WM_CREATE message is processed to complete the initialization of the new viewer window.

Example:

To add an application as a viewer in the clipboard viewer chain, use the following code snippet:

case WM_CREATE: // Add the window to the clipboard viewer chain. hwndNextViewer = SetClipboardViewer(hwnd); break;

The following code is an example of a clipboard viewer.

When the next window in the chain is closing, it repairs the chain by checking if the next window is being closed (WM_CHANGECBCHAIN message) and updating hwndNextViewer accordingly. If not, it sends the message to the next link in the chain using SendMessage().

In case of WM_DESTROY message, it calls ChangeClipboardChain() with hwnd and hwndNextViewer as parameters in order to remove the viewer from the clipboard viewer chain. Finally, it posts a QuitMessage() to stop the application.

WM_DRAWCLIPBOARD is handled when the clipboard content needs to be displayed on the screen. The CF_OWNERDISPLAY window class attribute is set to allow owner-drawn content to be displayed on the same window as the window that owns the clipboard view.

```cpp

#include

#include

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

PAINTSTRUCT ps;

HDC hdc;

HBITMAP hbm;

HENHMETAFILE hemf;

RECT rc;

LPRECT lprc;

HGLOBAL hglb;

LPSTR lpstr;

int iFormatCount = CountClipboardFormats();

BOOL fAuto = TRUE;

uFormat = (UINT)(-1);

switch (uMsg)

{

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

if (iFormatCount > 0)

{

uFormat = GetPriorityClipboardFormat(NULL, iFormatCount);

fAuto = TRUE;

InvalidateRect(hwnd, NULL, TRUE);

UpdateWindow(hwnd);

}

EndPaint(hwnd, &ps);

break;

// ... 其他消息处理 ...

}

return (LRESULT)NULL;

}

```