Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе приведены примеры кода для следующих задач:
-
Реализация команд "Вырезать", "Копировать" и "Вставка"
- Выбор данных
- Создание меню "Изменить"
- Обработка сообщения WM_INITMENUPOPUP
- Обработка сообщения WM_COMMAND
- Копирование сведений в буфер обмена
- Вставка сведений из буфера обмена
- Регистрация формата буфера обмена
- Обработка сообщений WM_RENDERFORMAT и WM_RENDERALLFORMATS
- Обработка сообщения WM_DESTROYCLIPBOARD
- Используйте формат буфера обмена "owner-display"
- Мониторинг содержимого буфера обмена
- Запросить номер последовательности буфера обмена
- Создание прослушивателя формата буфера обмена
- Создание окна просмотра буфера обмена
- Добавьте окно в цепочку просмотра буфера обмена
Реализация команд "Вырезать", "Копировать" и "Вставка"
В этом разделе описывается, как в приложении реализованы стандартные команды: Вырезать, Копироватьи Вставить. В этом разделе используются эти методы для размещения данных в буфере обмена с использованием зарегистрированного формата буфера обмена, формата CF_OWNERDISPLAY
и формата CF_TEXT
. Зарегистрированный формат используется для представления прямоугольных или эллиптических текстовых окон, называемых метками.
Выбор данных
Прежде чем данные можно скопировать в буфер обмена, пользователь должен выбрать конкретную информацию, которую нужно скопировать или вырезать. Приложение должно предоставить пользователю средства выбора информации в документе и какой-то визуальной обратной связи, чтобы указать выбранные данные.
Создание меню "Изменить"
Приложение должно загрузить таблицу ускорителей, содержащую стандартные клавиатурные ускорители для команд меню "Изменить". Функция TranslateAccelerator должна быть добавлена в цикл сообщений приложения, чтобы акселераторы вступили в силу. Дополнительные сведения об акселераторах клавиатуры см. в ускорителях клавиатуры.
Обработка сообщения WM_INITMENUPOPUP
Не все команды буфера обмена доступны пользователю в любое время. Приложение должно обработать сообщение WM_INITMENUPOPUP , чтобы включить элементы меню для доступных команд и отключить недоступные команды.
Ниже приведен вариант WM_INITMENUPOPUP для приложения с именем Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
Функция InitMenu
определена следующим образом.
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)
fuFlags = (id != IDM_DELETE && pbox->ichSel
== pbox->ichCaret) ?
MF_BYCOMMAND | MF_GRAYED :
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_COMMAND
Чтобы обработать команды меню, добавьте ветвь WM_COMMAND в процедуру главного окна вашего приложения. Ниже приведен вариант WM_COMMAND
для процедуры окна приложения Label.
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;
Чтобы выполнить команды Копировать и Вырезать, процедура окна вызывает функцию, определяемую приложением EditCopy
. Дополнительные сведения см. в разделе "Копирование сведений в буфер обмена". Чтобы выполнить команду вставки, процедура окна вызывает функцию EditPaste
, определяемую приложением. Дополнительные сведения о функции EditPaste
см. в разделе "Вставка сведений из буфера обмена".
Копирование информации в буфер обмена
В приложении Label функция EditCopy, определяемая приложением, копирует текущий выбор в буфер обмена. Эта функция выполняет следующие действия:
- Открывает буфер обмена, вызвав функцию OpenClipboard .
- Очищает буфер обмена путем вызова функции EmptyClipboard .
- Вызывает функцию SetClipboardData один раз для каждого формата буфера обмена, который предоставляет приложение.
- Закрывает буфер обмена, вызвав функцию CloseClipboard .
В зависимости от текущего выделения функция EditCopy копирует диапазон текста или копирует определяемую приложением структуру, представляющую всю метку. Структура, называемая LABELBOX
, определяется следующим образом:
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
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;
Ниже приведена EditCopy
функция:
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.
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;
}
Вставить информацию из буфера обмена
В приложении Label определяемая приложением функция EditPaste
вставляет содержимое буфера обмена. Эта функция выполняет следующие действия:
- Открывает буфер обмена, вызвав функцию OpenClipboard .
- Определяет, какой из доступных форматов буфера обмена следует получить.
- Извлекает дескриптор данных в выбранном формате с помощью вызова функции GetClipboardData.
- Вставляет копию данных в документ. Дескриптор, возвращаемый GetClipboardData, все еще принадлежит буферу обмена, поэтому приложение не должно освобождать его или оставлять его заблокированным.
- Закрывает буфер обмена вызовом функции CloseClipboard.
Если ярлык выбран и содержит точку вставки, команда EditPaste вставляет текст из буфера обмена в точку вставки. Если нет выбора или выбрана метка, функция создает новую метку, используя структуру LABELBOX
, определяемую приложением, в буфере обмена. Структура LABELBOX
помещается в буфер обмена с помощью зарегистрированного формата буфера обмена.
Структура, называемая LABELBOX
, определяется следующим образом:
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
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;
Ниже приведена EditPaste
функция:
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 the application is in edit mode,
// get the clipboard text.
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)
{
// Call the application-defined ReplaceSelection
// function to insert the text and repaint the
// window.
ReplaceSelection(hwndSelected, pbox, lptstr);
GlobalUnlock(hglb);
}
}
CloseClipboard();
return;
}
// If the application is not in edit mode,
// create a label window.
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();
}
Регистрация формата буфера обмена
Чтобы зарегистрировать формат буфера обмена, добавьте вызов функции RegisterClipboardFormat
в вашу инициализирующую функцию приложения следующим образом:
// Register a clipboard format.
// We assume that atchTemp can contain the format name and
// a null-terminator, otherwise it is truncated.
//
LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,
sizeof(atchTemp)/sizeof(TCHAR));
uLabelFormat = RegisterClipboardFormat(atchTemp);
if (uLabelFormat == 0)
return FALSE;
Обработка сообщений WM_RENDERFORMAT и WM_RENDERALLFORMATS
Если окно передает дескриптор NULL
функции SetClipboardData , оно должно обрабатывать WM_RENDERFORMAT и WM_RENDERALLFORMATS сообщения для отображения данных по запросу.
Если окно задерживает отображение определенного формата, а затем другое приложение запрашивает данные в этом формате, WM_RENDERFORMAT сообщение отправляется в окно. Кроме того, если окно задерживает отрисовку одного или нескольких форматов, а некоторые из этих форматов остаются невидимыми при уничтожении окна, то сообщение WM_RENDERALLFORMATS отправляется в окно до его уничтожения.
Чтобы отобразить формат буфера обмена, процедура окна должна поместить неуказанный дескриптор данных в буфер обмена с помощью функции SetClipboardData. Если процедура окна отображает формат в ответ на сообщение WM_RENDERFORMAT , он не должен открывать буфер обмена перед вызовом SetClipboardData
. Но если он отображает один или несколько форматов в ответ на сообщение WM_RENDERALLFORMATS , он должен открыть буфер обмена и проверить, что окно по-прежнему владеет буфером обмена перед вызовом SetClipboardData
, и оно должно закрыть буфер обмена перед возвратом.
Приложение Label обрабатывает сообщения WM_RENDERFORMAT и WM_RENDERALLFORMATS следующим образом:
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
В обоих случаях процедура окна вызывает определяемую RenderFormat
приложением функцию, которая определяется в следующем коде.
Структура, называемая LABELBOX
, определяется следующим образом:
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
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;
void WINAPI 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);
SetClipboardData(uLabelFormat, hglb);
}
}
Обработка сообщения WM_DESTROYCLIPBOARD
Окно может обработать сообщение WM_DESTROYCLIPBOARD, чтобы освободить все ресурсы, выделенные для поддержки отложенной отрисовки. Например, приложение Label при копировании метки в буфер обмена выделяет локальный объект памяти. Затем он освобождает этот объект в ответ на WM_DESTROYCLIPBOARD
сообщение следующим образом:
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Используйте формат буфера обмена для отображения владельца
Если окно помещает сведения в буфер обмена с помощью формата буфера обмена CF_OWNERDISPLAY
, необходимо выполнить следующее:
- Обработайте сообщение WM_PAINTCLIPBOARD . Это сообщение отправляется владельцу буфера обмена, когда необходимо перерисовать часть окна просмотра буфера обмена.
- Обработайте сообщение WM_SIZECLIPBOARD. Это сообщение отправляется владельцу буфера обмена при изменении размера окна просмотра буфера обмена или его содержимого. Как правило, окно реагирует на это сообщение, задав позиции прокрутки и диапазоны для окна просмотра буфера обмена. В ответ на это сообщение приложение Label также обновляет структуру SIZE для окна просмотра буфера обмена.
- Обработайте сообщения WM_HSCROLLCLIPBOARD и WM_VSCROLLCLIPBOARD . Эти сообщения отправляются владельцу буфера обмена при возникновении события полосы прокрутки в окне просмотра буфера обмена.
- Обработайте сообщение WM_ASKCBFORMATNAME . Окно просмотра буфера обмена отправляет это сообщение приложению, чтобы получить имя формата отображения владельца.
Процедура окна для приложения Label обрабатывает эти сообщения следующим образом:
LRESULT CALLBACK MainWindowProc(hwnd, msg, wParam, lParam)
HWND hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
static 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;
}
Отслеживать содержимое буфера обмена
Существует три способа мониторинга изменений в буфере обмена. Самый старый метод — создать окно просмотра буфера обмена. Windows 2000 добавила возможность запрашивать номер последовательности буфера обмена, а Windows Vista добавила поддержку прослушивателей форматов буфера обмена. Окна просмотра буфера обмена поддерживаются для обратной совместимости с более ранними версиями Windows. Новые программы должны использовать прослушиватели формата буфера обмена или номер последовательности буфера обмена.
Запросить номер последовательности буфера обмена
При каждом изменении содержимого буфера обмена 32-разрядное значение, известное как порядковый номер буфера обмена, увеличивается. Программа может получить текущий номер последовательности буфера обмена, вызвав функцию GetClipboardSequenceNumber . Сравнивая значение, возвращаемое с значением, возвращаемым предыдущим вызовом GetClipboardSequenceNumber
, программа может определить, изменилось ли содержимое буфера обмена. Этот метод более подходит для программ, которые кэшируют результаты на основе текущего содержимого буфера обмена и должны знать, допустимы ли вычисления перед использованием результатов из этого кэша. Обратите внимание, что это не метод уведомления и не следует использовать в цикле опроса. Чтобы получать уведомления при изменении содержимого буфера обмена, используйте прослушиватель формата буфера обмена или средство просмотра буфера обмена.
Создание прослушивателя формата буфера обмена
Прослушиватель формата буфера обмена — это окно, которое зарегистрировано для получения уведомлений при изменении содержимого буфера обмена. Этот метод рекомендуется вместо создания окна просмотра буфера обмена, так как его проще реализовать и чтобы избежать проблем, если программы не поддерживают цепочку просмотра буфера обмена должным образом или если окно в цепочке просмотра буфера обмена не отвечает на сообщения.
Окно регистрируется в качестве прослушивателя формата буфера обмена путем вызова функции AddClipboardFormatListener . При изменении содержимого буфера обмена окну отправляется сообщение WM_CLIPBOARDUPDATE. Регистрация остается допустимой до тех пор, пока окно не отменяет регистрацию, вызвав функцию RemoveClipboardFormatListener .
Создание окна просмотра буфера обмена
Окно просмотра буфера обмена отображает текущее содержимое буфера обмена и получает сообщения при изменении содержимого буфера обмена. Чтобы создать окно просмотра буфера обмена, приложение должно выполнить следующее:
- Добавьте окно в цепочку просмотра буфера обмена.
- Обработайте сообщение WM_CHANGECBCHAIN .
- Обработайте сообщение WM_DRAWCLIPBOARD .
- Удалите окно из цепочки просмотра буфера обмена, прежде чем оно будет уничтожено.
Добавьте окно в цепочку просмотра буфера обмена
Окно добавляет себя в цепочку просмотра буфера обмена, вызвав функцию SetClipboardViewer . Возвращаемое значение — это дескриптор следующего окна в цепочке. Окно должно отслеживать это значение, например, сохраняя его в статической переменной с именем hwndNextViewer
.
В следующем примере окно добавляется в цепочку просмотра буфера обмена в ответ на сообщение WM_CREATE.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Фрагменты кода показаны для следующих задач:
- Обработка сообщения WM_CHANGECBCHAIN
- Удалить окно из цепочки просмотра буфера обмена
- Обработка сообщения WM_DRAWCLIPBOARD
- Пример средства просмотра буфера обмена
Обработка сообщения WM_CHANGECBCHAIN
Окно просмотра буфера обмена получает сообщение WM_CHANGECBCHAIN , когда другое окно удаляется из цепочки просмотра буфера обмена. Если окно, которое удаляется, является следующим окном в цепочке, окно, получающее сообщение, должно отменить связь следующего окна из цепочки. В противном случае это сообщение должно быть передано следующему окну в цепочке.
В следующем примере показана обработка сообщения WM_CHANGECBCHAIN .
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
Удалите окно из цепочки просмотра буфера обмена
Чтобы удалить себя из цепочки просмотра буфера обмена, окно вызывает функцию ChangeClipboardChain . В следующем примере окно удаляется из цепочки просмотра буфера обмена в ответ на сообщение WM_DESTROY .
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Обработка сообщения WM_DRAWCLIPBOARD
Сообщение WM_DRAWCLIPBOARD уведомляет окно просмотра буфера обмена о том, что содержимое буфера обмена изменилось. При обработке сообщения WM_DRAWCLIPBOARD
окно должно выполнять следующее:
- Определите, какие из доступных форматов буфера обмена нужно отобразить.
- Извлеките данные буфера обмена и отобразите их в окне. Или если формат буфера обмена задан
CF_OWNERDISPLAY
, отправьте WM_PAINTCLIPBOARD сообщение владельцу буфера обмена. - Отправьте сообщение в следующее окно в цепочке окон просмотра буфера обмена.
Пример обработки сообщения WM_DRAWCLIPBOARD см. в примере, приведенном в следующем разделе.
Пример средства просмотра буфера обмена
В следующем примере показано полное простое приложение средства просмотра буфера обмена:
HINSTANCE hinst;
UINT uFormat = (UINT)(-1);
BOOL fAuto = TRUE;
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndNextViewer;
HDC hdc;
HDC hdcMem;
PAINTSTRUCT ps;
LPPAINTSTRUCT lpps;
RECT rc;
LPRECT lprc;
HGLOBAL hglb;
LPSTR lpstr;
HBITMAP hbm;
HENHMETAFILE hemf;
HWND hwndOwner;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Branch depending on the clipboard format.
switch (uFormat)
{
case CF_OWNERDISPLAY:
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE,
sizeof(PAINTSTRUCT));
lpps = GlobalLock(hglb);
memcpy(lpps, &ps, sizeof(PAINTSTRUCT));
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_PAINTCLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
break;
case CF_BITMAP:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (OpenClipboard(hwnd))
{
hbm = (HBITMAP)
GetClipboardData(uFormat);
SelectObject(hdcMem, hbm);
GetClientRect(hwnd, &rc);
BitBlt(hdc, 0, 0, rc.right, rc.bottom,
hdcMem, 0, 0, SRCCOPY);
CloseClipboard();
}
DeleteDC(hdcMem);
}
break;
case CF_TEXT:
if (OpenClipboard(hwnd))
{
hglb = GetClipboardData(uFormat);
lpstr = GlobalLock(hglb);
GetClientRect(hwnd, &rc);
DrawText(hdc, lpstr, -1, &rc, DT_LEFT);
GlobalUnlock(hglb);
CloseClipboard();
}
break;
case CF_ENHMETAFILE:
if (OpenClipboard(hwnd))
{
hemf = GetClipboardData(uFormat);
GetClientRect(hwnd, &rc);
PlayEnhMetaFile(hdc, hemf, &rc);
CloseClipboard();
}
break;
case 0:
GetClientRect(hwnd, &rc);
DrawText(hdc, "The clipboard is empty.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
break;
default:
GetClientRect(hwnd, &rc);
DrawText(hdc, "Unable to display format.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
}
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
if (uFormat == CF_OWNERDISPLAY)
{
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(RECT));
lprc = GlobalLock(hglb);
GetClientRect(hwnd, lprc);
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_SIZECLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
}
break;
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
case WM_DRAWCLIPBOARD: // clipboard contents changed.
// Update the window by using Auto clipboard format.
SetAutoView(hwnd);
// Pass the message to the next window in clipboard
// viewer chain.
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_INITMENUPOPUP:
if (!HIWORD(lParam))
InitMenu(hwnd, (HMENU) wParam);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
DestroyWindow(hwnd);
break;
case IDM_AUTO:
SetAutoView(hwnd);
break;
default:
fAuto = FALSE;
uFormat = LOWORD(wParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}
void WINAPI SetAutoView(HWND hwnd)
{
static UINT auPriorityList[] = {
CF_OWNERDISPLAY,
CF_TEXT,
CF_ENHMETAFILE,
CF_BITMAP
};
uFormat = GetPriorityClipboardFormat(auPriorityList, 4);
fAuto = TRUE;
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
void WINAPI InitMenu(HWND hwnd, HMENU hmenu)
{
UINT uFormat;
char szFormatName[80];
LPCSTR lpFormatName;
UINT fuFlags;
UINT idMenuItem;
// If a menu is not the display menu, no initialization is necessary.
if (GetMenuItemID(hmenu, 0) != IDM_AUTO)
return;
// Delete all menu items except the first.
while (GetMenuItemCount(hmenu) > 1)
DeleteMenu(hmenu, 1, MF_BYPOSITION);
// Check or uncheck the Auto menu item.
fuFlags = fAuto ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED;
CheckMenuItem(hmenu, IDM_AUTO, fuFlags);
// If there are no clipboard formats, return.
if (CountClipboardFormats() == 0)
return;
// Open the clipboard.
if (!OpenClipboard(hwnd))
return;
// Add a separator and then a menu item for each format.
AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
uFormat = EnumClipboardFormats(0);
while (uFormat)
{
// Call an application-defined function to get the name
// of the clipboard format.
lpFormatName = GetPredefinedClipboardFormatName(uFormat);
// For registered formats, get the registered name.
if (lpFormatName == NULL)
{
// Note that, if the format name is larger than the
// buffer, it is truncated.
if (GetClipboardFormatName(uFormat, szFormatName,
sizeof(szFormatName)))
lpFormatName = szFormatName;
else
lpFormatName = "(unknown)";
}
// Add a menu item for the format. For displayable
// formats, use the format ID for the menu ID.
if (IsDisplayableFormat(uFormat))
{
fuFlags = MF_STRING;
idMenuItem = uFormat;
}
else
{
fuFlags = MF_STRING | MF_GRAYED;
idMenuItem = 0;
}
AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName);
uFormat = EnumClipboardFormats(uFormat);
}
CloseClipboard();
}
BOOL WINAPI IsDisplayableFormat(UINT uFormat)
{
switch (uFormat)
{
case CF_OWNERDISPLAY:
case CF_TEXT:
case CF_ENHMETAFILE:
case CF_BITMAP:
return TRUE;
}
return FALSE;
}