Como usar a área de transferência

Esta seção tem exemplos de código para as seguintes tarefas:

Implementando os comandos Recortar, Copiar e Colar

Esta seção descreve como os comandos padrão Recortar, Copiar e Colar são implementados em um aplicativo. O exemplo nesta seção usa esses métodos para colocar dados na área de transferência usando um formato de área de transferência registrado, o formato e o CF_OWNERDISPLAYCF_TEXT formato. O formato registrado é usado para representar janelas de texto retangulares ou elípticas, chamadas rótulos.

Selecionando dados

Antes que as informações possam ser copiadas para a área de transferência, o usuário deve selecionar informações específicas a serem copiadas ou cortadas. Um aplicativo deve fornecer um meio para o usuário selecionar informações dentro de um documento e algum tipo de feedback visual para indicar dados selecionados.

Criando um menu Editar

Um aplicativo deve carregar uma tabela de aceleradores contendo os aceleradores de teclado padrão para os comandos do menu Editar . A TranslateAccelerator função deve ser adicionada ao loop de mensagem do aplicativo para que os aceleradores entrem em vigor. Para obter mais informações sobre aceleradores de teclado, consulte Aceleradores de teclado.

Processando a WM_INITMENUPOPUP mensagem

Nem todos os comandos da área de transferência estão disponíveis para o usuário a qualquer momento. Um aplicativo deve processar a WM_INITMENUPOPUP mensagem para habilitar os itens de menu para comandos disponíveis e desabilitar comandos indisponíveis.

A seguir está o caso de WM_INITMENUPOPUP um aplicativo chamado Label.

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

A InitMenu função é definida da seguinte forma.

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 
                    ); 
 
        } 
    } 
}

Processando a WM_COMMAND mensagem

Para processar comandos de menu, adicione o WM_COMMAND caso ao procedimento da janela principal do aplicativo. Veja a seguir o WM_COMMAND caso do procedimento de janela do aplicativo 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; 

Para executar os comandos Copiar e Recortar , o procedimento de janela chama a função definida EditCopy pelo aplicativo. Para obter mais informações, consulte Copiando informações para a área de transferência. Para executar o comando Colar, o procedimento de janela chama a função definida EditPaste pelo aplicativo. Para obter mais informações sobre a EditPaste função, consulte Colando informações da área de transferência.

Copiando informações para a área de transferência

No aplicativo Label, a função EditCopy definida pelo aplicativo copia a seleção atual para a área de transferência. Essa função faz o seguinte:

  1. Abre a área de transferência chamando a OpenClipboard função.
  2. Esvazia a área de transferência chamando a EmptyClipboard função.
  3. Chama a SetClipboardData função uma vez para cada formato de área de transferência fornecido pelo aplicativo.
  4. Fecha a área de transferência chamando a CloseClipboard função.

Dependendo da seleção atual, a função EditCopy copia um intervalo de texto ou copia uma estrutura definida pelo aplicativo que representa um rótulo inteiro. A estrutura, chamada LABELBOX, é definida da seguinte forma.

#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;

A seguir está a EditCopy função.

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; 
}

Colando informações da área de transferência

No aplicativo Label, a função definida EditPaste pelo aplicativo cola o conteúdo da área de transferência. Essa função faz o seguinte:

  1. Abre a área de transferência chamando a OpenClipboard função.
  2. Determina quais dos formatos de área de transferência disponíveis devem ser recuperados.
  3. Recupera o identificador para os dados no formato selecionado chamando a GetClipboardData função.
  4. Insere uma cópia dos dados no documento. O identificador retornado por GetClipboardData ainda é de propriedade da área de transferência, portanto, um aplicativo não deve liberá-lo ou deixá-lo bloqueado.
  5. Fecha a área de transferência chamando a CloseClipboard função.

Se um rótulo estiver selecionado e contiver um ponto de inserção, a função EditPaste insere o texto da área de transferência no ponto de inserção. Se não houver seleção ou se um rótulo for selecionado, a função criará um novo rótulo, usando a estrutura definida LABELBOX pelo aplicativo na área de transferência. A LABELBOX estrutura é colocada na área de transferência usando um formato de área de transferência registrado.

A estrutura, chamada LABELBOX, é definida da seguinte forma.

#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;

A seguir está a EditPaste função.

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(); 
}

Registrando um formato da área de transferência

Para registrar um formato de área de transferência, adicione uma chamada à RegisterClipboardFormat função para a função de inicialização de instância do seu aplicativo, da seguinte maneira.

// 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;

Processando o WM_RENDERFORMAT e WM_RENDERALLFORMATS Mensagens

Se uma janela passa um NULL identificador para a SetClipboardData função, ela deve processar as WM_RENDERFORMAT mensagens e WM_RENDERALLFORMATS para renderizar dados mediante solicitação.

Se uma janela atrasar a renderização de um formato específico e, em seguida, outro aplicativo solicitar dados nesse formato, uma WM_RENDERFORMAT mensagem será enviada para a janela. Além disso, se uma janela atrasar a renderização de um ou mais formatos, e se alguns desses formatos permanecerem não renderizados quando a janela estiver prestes a ser destruída, uma WM_RENDERALLFORMATS mensagem será enviada para a janela antes de sua destruição.

Para renderizar um formato de área de transferência, o procedimento de janela deve colocar um identificador que nãoNULL seja de dados na área de transferência usando a SetClipboardData função. Se o procedimento de janela estiver renderizando um formato em resposta à WM_RENDERFORMAT mensagem, ele não deve abrir a área de transferência antes de chamar SetClipboardData. Mas se ele estiver renderizando um ou mais formatos em resposta à WM_RENDERALLFORMATS mensagem, ele deve abrir a área de transferência e verificar se a janela ainda possui a área de transferência antes de chamar SetClipboardData, e deve fechar a área de transferência antes de retornar.

O aplicativo Label processa as WM_RENDERFORMAT mensagens e WM_RENDERALLFORMATS da seguinte maneira.

case WM_RENDERFORMAT: 
    RenderFormat((UINT) wParam); 
    break; 
 
case WM_RENDERALLFORMATS:
    if (OpenClipboard(hwnd))
    {
        if (GetClipboardOwner() == hwnd)
        {
            RenderFormat(uLabelFormat);
            RenderFormat(CF_TEXT);
        }
        CloseClipboard();
    }
    break;

Em ambos os casos, o procedimento de janela chama a função definida pelo aplicativo, definida RenderFormat a seguir.

A estrutura, chamada LABELBOX, é definida da seguinte forma.

#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); 
    } 
}

Processando a WM_DESTROYCLIPBOARD mensagem

Uma janela pode processar a WM_DESTROYCLIPBOARD mensagem para liberar quaisquer recursos que ela reservou para dar suporte à renderização atrasada. Por exemplo, o aplicativo Label, ao copiar um rótulo para a área de transferência, aloca um objeto de memória local. Em seguida, ele libera esse objeto em resposta à WM_DESTROYCLIPBOARD mensagem, da seguinte maneira.

case WM_DESTROYCLIPBOARD: 
    if (pboxLocalClip != NULL) 
    { 
        LocalFree(pboxLocalClip); 
        pboxLocalClip = NULL; 
    } 
    break;

Usando o formato da área de transferência de exibição do proprietário

Se uma janela colocar informações na área de transferência usando o formato da área de transferência, ela deverá fazer o CF_OWNERDISPLAY seguinte:

  • Processe a WM_PAINTCLIPBOARD mensagem. Essa mensagem é enviada ao proprietário da área de transferência quando uma parte da janela do visualizador da área de transferência deve ser pintada novamente.
  • Processe a WM_SIZECLIPBOARD mensagem. Essa mensagem é enviada ao proprietário da área de transferência quando a janela do visualizador da área de transferência foi redimensionada ou seu conteúdo foi alterado. Normalmente, uma janela responde a essa mensagem definindo as posições de rolagem e os intervalos para a janela do visualizador da área de transferência. Em resposta a essa mensagem, o aplicativo Label também atualiza uma SIZE estrutura para a janela do visualizador da área de transferência.
  • Processe as WM_HSCROLLCLIPBOARDWM_VSCROLLCLIPBOARD e mensagens. Essas mensagens são enviadas ao proprietário da área de transferência quando ocorre um evento de barra de rolagem na janela do visualizador da área de transferência.
  • Processe a WM_ASKCBFORMATNAME mensagem. A janela do visualizador da área de transferência envia essa mensagem a um aplicativo para recuperar o nome do formato de exibição do proprietário.

O procedimento de janela para o aplicativo Label processa essas mensagens, da seguinte maneira.

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; 
}

Monitorando o conteúdo da área de transferência

Há três maneiras de monitorar as alterações na área de transferência. O método mais antigo é criar uma janela de visualizador da área de transferência. O Windows 2000 adicionou a capacidade de consultar o número de sequência da área de transferência e o Windows Vista adicionou suporte para ouvintes de formato da área de transferência. As janelas do visualizador da área de transferência são suportadas para compatibilidade com versões anteriores do Windows. Novos programas devem usar ouvintes de formato de área de transferência ou o número de sequência da área de transferência.

Consultando o número de sequência da área de transferência

Cada vez que o conteúdo da área de transferência é alterado, um valor de 32 bits conhecido como número de sequência da área de transferência é incrementado. Um programa pode recuperar o número de sequência atual da área de transferência chamando a GetClipboardSequenceNumber função. Ao comparar o valor retornado com um valor retornado por uma chamada anterior para GetClipboardSequenceNumber, um programa pode determinar se o conteúdo da área de transferência foi alterado. Esse método é mais adequado para programas que armazenam em cache os resultados com base no conteúdo atual da área de transferência e precisam saber se os cálculos ainda são válidos antes de usar os resultados desse cache. Observe que esse não é um método de notificação e não deve ser usado em um loop de sondagem. Para ser notificado quando o conteúdo da área de transferência for alterado, use um ouvinte de formato de área de transferência ou um visualizador de área de transferência.

Criando um ouvinte de formato da área de transferência

Um ouvinte de formato de área de transferência é uma janela que se registrou para ser notificada quando o conteúdo da área de transferência foi alterado. Esse método é recomendado em vez de criar uma janela de visualizador da área de transferência porque é mais simples de implementar e evita problemas se os programas não conseguirem manter a cadeia de visualizadores da área de transferência corretamente ou se uma janela na cadeia de visualizadores da área de transferência parar de responder às mensagens.

Uma janela é registrada como um ouvinte de formato da área de transferência chamando a AddClipboardFormatListener função. Quando o conteúdo da área de transferência é alterado, a janela é postada uma WM_CLIPBOARDUPDATE mensagem. O registro permanece válido até que a janela cancele o registro chamando a RemoveClipboardFormatListener função.

Criando uma janela do Visualizador da Área de Transferência

Uma janela do visualizador da área de transferência exibe o conteúdo atual da área de transferência e recebe mensagens quando o conteúdo da área de transferência é alterado. Para criar uma janela do visualizador da área de transferência, seu aplicativo deve fazer o seguinte:

  • Adicione a janela à cadeia de visualizadores da área de transferência.
  • Processe a WM_CHANGECBCHAIN mensagem.
  • Processe a WM_DRAWCLIPBOARD mensagem.
  • Remova a janela da cadeia de visualização da área de transferência antes que ela seja destruída.

Adicionando uma janela à cadeia do visualizador da área de transferência

Uma janela se adiciona à cadeia de visualizadores da área de transferência chamando a SetClipboardViewer função. O valor de retorno é o identificador para a próxima janela na cadeia. Uma janela deve controlar esse valor — por exemplo, salvando-o em uma variável estática chamada hwndNextViewer.

O exemplo a seguir adiciona uma janela à cadeia de visualizadores da área de transferência em resposta à WM_CREATE mensagem.

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

Trechos de código são mostrados para as seguintes tarefas:

Processando a WM_CHANGECBCHAIN mensagem

Uma janela do visualizador da área de transferência recebe a WM_CHANGECBCHAIN mensagem quando outra janela está se removendo da cadeia de visualizadores da área de transferência. Se a janela que está sendo removida for a próxima janela da cadeia, a janela que recebe a mensagem deve desvincular a próxima janela da cadeia. Caso contrário, essa mensagem deve ser passada para a próxima janela na cadeia.

O exemplo a seguir mostra o processamento da WM_CHANGECBCHAIN mensagem.

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;

Removendo uma janela da cadeia do visualizador da área de transferência

Para se remover da cadeia de visualizadores da área de transferência, uma janela chama a ChangeClipboardChain função. O exemplo a seguir remove uma janela da cadeia de visualizadores da área de transferência em resposta à WM_DESTROY mensagem.

case WM_DESTROY: 
    ChangeClipboardChain(hwnd, hwndNextViewer); 
    PostQuitMessage(0); 
    break;

Processando a WM_DRAWCLIPBOARD mensagem

A WM_DRAWCLIPBOARD mensagem notifica uma janela do visualizador da área de transferência de que o conteúdo da área de transferência foi alterado. Uma janela deve fazer o seguinte ao processar a WM_DRAWCLIPBOARD mensagem:

  1. Determine qual dos formatos de área de transferência disponíveis exibir.
  2. Recupere os dados da área de transferência e exiba-os na janela. Ou, se o formato da área de transferência for CF_OWNERDISPLAY, envie uma WM_PAINTCLIPBOARD mensagem para o proprietário da área de transferência.
  3. Envie a mensagem para a próxima janela na cadeia de visualizadores da área de transferência.

Para obter um exemplo de processamento da WM_DRAWCLIPBOARD mensagem, consulte a listagem de exemplo em Exemplo de um Visualizador da Área de Transferência.

Exemplo de um Visualizador da Área de Transferência

O exemplo a seguir mostra um aplicativo visualizador da área de transferência simples.

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; 
}