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
- Selecionando dados
- Criando um menu Editar
- Processando a
WM_INITMENUPOPUP
mensagem - Processando a
WM_COMMAND
mensagem - Copiando informações para a área de transferência
- Colando informações da área de transferência
- Registrando um formato da área de transferência
- Processando o
WM_RENDERFORMAT
eWM_RENDERALLFORMATS
Mensagens - Processando a
WM_DESTROYCLIPBOARD
mensagem - Usando o formato da área de transferência de exibição do proprietário
- Monitorando o conteúdo da área de transferência
- Consultando o número de sequência da área de transferência
- Criando um ouvinte de formato da área de transferência
- Criando uma janela do Visualizador da Área de Transferência
- Adicionando uma janela à cadeia do visualizador da área de transferência
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_OWNERDISPLAY
CF_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:
- Abre a área de transferência chamando a
OpenClipboard
função. - Esvazia a área de transferência chamando a
EmptyClipboard
função. - Chama a
SetClipboardData
função uma vez para cada formato de área de transferência fornecido pelo aplicativo. - 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:
- Abre a área de transferência chamando a
OpenClipboard
função. - Determina quais dos formatos de área de transferência disponíveis devem ser recuperados.
- Recupera o identificador para os dados no formato selecionado chamando a
GetClipboardData
função. - 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. - 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 umaSIZE
estrutura para a janela do visualizador da área de transferência. - Processe as
WM_HSCROLLCLIPBOARD
WM_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 - Removendo uma janela da cadeia do visualizador da área de transferência
- Processando a
WM_DRAWCLIPBOARD
mensagem - Exemplo de um Visualizador da Área de Transferência
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:
- Determine qual dos formatos de área de transferência disponíveis exibir.
- 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 umaWM_PAINTCLIPBOARD
mensagem para o proprietário da área de transferência. - 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;
}