Поделиться через


Использование буфера обмена

В этом разделе приведены примеры кода для следующих задач:

Реализация команд "Вырезать", "Копировать" и "Вставка"

В этом разделе описывается, как в приложении реализованы стандартные команды: Вырезать, Копироватьи Вставить. В этом разделе используются эти методы для размещения данных в буфере обмена с использованием зарегистрированного формата буфера обмена, формата 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, определяемая приложением, копирует текущий выбор в буфер обмена. Эта функция выполняет следующие действия:

  1. Открывает буфер обмена, вызвав функцию OpenClipboard .
  2. Очищает буфер обмена путем вызова функции EmptyClipboard .
  3. Вызывает функцию SetClipboardData один раз для каждого формата буфера обмена, который предоставляет приложение.
  4. Закрывает буфер обмена, вызвав функцию 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 вставляет содержимое буфера обмена. Эта функция выполняет следующие действия:

  1. Открывает буфер обмена, вызвав функцию OpenClipboard .
  2. Определяет, какой из доступных форматов буфера обмена следует получить.
  3. Извлекает дескриптор данных в выбранном формате с помощью вызова функции GetClipboardData.
  4. Вставляет копию данных в документ. Дескриптор, возвращаемый GetClipboardData, все еще принадлежит буферу обмена, поэтому приложение не должно освобождать его или оставлять его заблокированным.
  5. Закрывает буфер обмена вызовом функции 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_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 окно должно выполнять следующее:

  1. Определите, какие из доступных форматов буфера обмена нужно отобразить.
  2. Извлеките данные буфера обмена и отобразите их в окне. Или если формат буфера обмена задан CF_OWNERDISPLAY, отправьте WM_PAINTCLIPBOARD сообщение владельцу буфера обмена.
  3. Отправьте сообщение в следующее окно в цепочке окон просмотра буфера обмена.

Пример обработки сообщения 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; 
} 

ускорители клавиатуры