Utilisation du presse-papiers
Cette section contient des exemples de code pour les tâches suivantes :
-
Implémentation des commandes Couper, Copier et Coller
- Sélection de données
- Création d’un menu Modifier
-
Traitement du
WM_INITMENUPOPUP
message -
Traitement du
WM_COMMAND
message - Copie d’informations dans le Presse-papiers
- Collage d’informations à partir du Presse-papiers
- Inscription d’un format de Presse-papiers
-
Traitement des
WM_RENDERFORMAT
messages etWM_RENDERALLFORMATS
-
Traitement du
WM_DESTROYCLIPBOARD
message - Utilisation du format de presse-papiers Owner-Display
- Surveillance du contenu du Presse-papiers
- Interrogation du numéro de séquence du Presse-papiers
- Création d’un écouteur de format du Presse-papiers
- Création d’une fenêtre visionneuse du Presse-papiers
- Ajout d’une fenêtre à la chaîne de visionneuse du Presse-papiers
Implémentation des commandes Couper, Copier et Coller
Cette section décrit comment les commandes Couper, Copier et Coller standard sont implémentées dans une application. L’exemple de cette section utilise ces méthodes pour placer des données dans le Presse-papiers à l’aide d’un format de Presse-papiers inscrit, du CF_OWNERDISPLAY
format et du CF_TEXT
format. Le format inscrit est utilisé pour représenter des fenêtres de texte rectangulaires ou elliptiques, appelées étiquettes.
Sélection de données
Pour que les informations puissent être copiées dans le Presse-papiers, l’utilisateur doit sélectionner des informations spécifiques à copier ou à couper. Une application doit fournir à l’utilisateur un moyen de sélectionner des informations dans un document et un type de commentaires visuels pour indiquer les données sélectionnées.
Création d’un menu Modifier
Une application doit charger une table d’accélérateurs contenant les accélérateurs clavier standard pour les commandes de menu Modifier . La TranslateAccelerator
fonction doit être ajoutée à la boucle de message de l’application pour que les accélérateurs prennent effet. Pour plus d’informations sur les raccourcis clavier, consultez Raccourcis clavier.
Traitement du WM_INITMENUPOPUP
message
Toutes les commandes du Presse-papiers ne sont pas disponibles pour l’utilisateur à un moment donné. Une application doit traiter le WM_INITMENUPOPUP
message pour activer les éléments de menu pour les commandes disponibles et désactiver les commandes non disponibles.
Voici le WM_INITMENUPOPUP
cas d’une application nommée Label.
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
La InitMenu
fonction est définie comme suit.
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
);
}
}
}
Traitement du WM_COMMAND
message
Pour traiter les commandes de menu, ajoutez le cas à la WM_COMMAND
procédure de fenêtre main de votre application. Voici le WM_COMMAND
cas de la procédure de fenêtre de l’application 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;
Pour exécuter les commandes Copier et Couper , la procédure de fenêtre appelle la fonction définie par EditCopy
l’application. Pour plus d’informations, consultez Copie d’informations dans le Presse-papiers. Pour exécuter la commande Coller , la procédure de fenêtre appelle la fonction définie par EditPaste
l’application. Pour plus d’informations sur la EditPaste
fonction, consultez Collage d’informations à partir du Presse-papiers.
Copie d’informations dans le Presse-papiers
Dans l’application Label, la fonction EditCopy définie par l’application copie la sélection actuelle dans le Presse-papiers. Cette fonction effectue les opérations suivantes :
- Ouvre le Presse-papiers en appelant la
OpenClipboard
fonction . - Vide le Presse-papiers en appelant la
EmptyClipboard
fonction . - Appelle la
SetClipboardData
fonction une fois pour chaque format de Presse-papiers fourni par l’application. - Ferme le Presse-papiers en appelant la
CloseClipboard
fonction .
Selon la sélection actuelle, la fonction EditCopy copie une plage de texte ou copie une structure définie par l’application représentant une étiquette entière. La structure, appelée LABELBOX
, est définie comme suit.
#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;
Voici la EditCopy
fonction .
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;
}
Collage d’informations à partir du Presse-papiers
Dans l’application Label, la fonction définie par EditPaste
l’application colle le contenu du Presse-papiers. Cette fonction effectue les opérations suivantes :
- Ouvre le Presse-papiers en appelant la
OpenClipboard
fonction . - Détermine les formats du Presse-papiers disponibles à récupérer.
- Récupère le handle dans les données au format sélectionné en appelant la
GetClipboardData
fonction . - Insère une copie des données dans le document. Le handle retourné par
GetClipboardData
appartient toujours au Presse-papiers. Par conséquent, une application ne doit pas le libérer ou le laisser verrouillé. - Ferme le Presse-papiers en appelant la
CloseClipboard
fonction .
Si une étiquette est sélectionnée et contient un point d’insertion, la fonction EditPaste insère le texte du Presse-papiers au point d’insertion. En l’absence de sélection ou si une étiquette est sélectionnée, la fonction crée une nouvelle étiquette à l’aide de la structure définie par LABELBOX
l’application dans le Presse-papiers. La LABELBOX
structure est placée dans le Presse-papiers à l’aide d’un format de Presse-papiers inscrit.
La structure, appelée LABELBOX
, est définie comme suit.
#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;
Voici la EditPaste
fonction .
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();
}
Inscription d’un format de Presse-papiers
Pour inscrire un format de Presse-papiers, ajoutez un appel à la fonction à la RegisterClipboardFormat
fonction d’initialisation instance de votre application, comme suit.
// 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;
Traitement des WM_RENDERFORMAT
messages et WM_RENDERALLFORMATS
Si une fenêtre transmet un NULL
handle à la SetClipboardData
fonction, elle doit traiter les messages et WM_RENDERALLFORMATS
pour afficher les WM_RENDERFORMAT
données sur demande.
Si une fenêtre retarde le rendu d’un format spécifique et qu’une autre application demande des données dans ce format, un WM_RENDERFORMAT
message est envoyé à la fenêtre. En outre, si une fenêtre retarde le rendu d’un ou plusieurs formats et que certains de ces formats restent non rendus lorsque la fenêtre est sur le point d’être détruite, un WM_RENDERALLFORMATS
message est envoyé à la fenêtre avant sa destruction.
Pour afficher un format de Presse-papiers, la procédure de fenêtre doit placer un handle de non-données dans le Presse-papiersNULL
à l’aide de la SetClipboardData
fonction . Si la procédure de fenêtre affiche un format en réponse au WM_RENDERFORMAT
message, elle ne doit pas ouvrir le Presse-papiers avant d’appeler SetClipboardData
. Toutefois, s’il affiche un ou plusieurs formats en réponse au WM_RENDERALLFORMATS
message, il doit ouvrir le Presse-papiers et case activée que la fenêtre est toujours propriétaire du Presse-papiers avant d’appeler SetClipboardData
, et elle doit fermer le Presse-papiers avant de revenir.
L’application Label traite les WM_RENDERFORMAT
messages et WM_RENDERALLFORMATS
comme suit.
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
Dans les deux cas, la procédure de fenêtre appelle la fonction définie par RenderFormat
l’application, définie comme suit.
La structure, appelée LABELBOX
, est définie comme suit.
#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);
}
}
Traitement du WM_DESTROYCLIPBOARD
message
Une fenêtre peut traiter le WM_DESTROYCLIPBOARD
message afin de libérer toutes les ressources qu’elle met de côté pour prendre en charge le rendu différé. Par exemple, l’application Label, lors de la copie d’une étiquette dans le Presse-papiers, alloue un objet mémoire local. Il libère ensuite cet objet en réponse au WM_DESTROYCLIPBOARD
message, comme suit.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Utilisation du format de presse-papiers Owner-Display
Si une fenêtre place des informations dans le Presse-papiers à l’aide du format du CF_OWNERDISPLAY
Presse-papiers, elle doit effectuer les opérations suivantes :
- Traitez le
WM_PAINTCLIPBOARD
message. Ce message est envoyé au propriétaire du Presse-papiers lorsqu’une partie de la fenêtre de visionneuse du Presse-papiers doit être repeinte. - Traitez le
WM_SIZECLIPBOARD
message. Ce message est envoyé au propriétaire du Presse-papiers lorsque la fenêtre de visionneuse du Presse-papiers a été redimensionnée ou que son contenu a changé. En règle générale, une fenêtre répond à ce message en définissant les positions de défilement et les plages de la fenêtre de visionneuse du Presse-papiers. En réponse à ce message, l’application Label met également à jour uneSIZE
structure pour la fenêtre de visionneuse du Presse-papiers. - Traitez les
WM_HSCROLLCLIPBOARD
messages etWM_VSCROLLCLIPBOARD
. Ces messages sont envoyés au propriétaire du Presse-papiers lorsqu’un événement de barre de défilement se produit dans la fenêtre de visionneuse du Presse-papiers. - Traitez le
WM_ASKCBFORMATNAME
message. La fenêtre de visionneuse du Presse-papiers envoie ce message à une application pour récupérer le nom du format d’affichage propriétaire.
La procédure de fenêtre de l’application Label traite ces messages, comme suit.
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;
}
Surveillance du contenu du Presse-papiers
Il existe trois façons de surveiller les modifications apportées au Presse-papiers. La méthode la plus ancienne consiste à créer une fenêtre de visionneuse du Presse-papiers. Windows 2000 a ajouté la possibilité d’interroger le numéro séquentiel du Presse-papiers, et Windows Vista a ajouté la prise en charge des écouteurs de format de Presse-papiers. Les fenêtres de visionneuse du Presse-papiers sont prises en charge à des fins de compatibilité descendante avec les versions antérieures de Windows. Les nouveaux programmes doivent utiliser des écouteurs au format Presse-papiers ou le numéro de séquence du Presse-papiers.
Interrogation du numéro de séquence du Presse-papiers
Chaque fois que le contenu du Presse-papiers change, une valeur de 32 bits appelée numéro séquentiel du Presse-papiers est incrémentée. Un programme peut récupérer le numéro séquentiel du Presse-papiers actuel en appelant la GetClipboardSequenceNumber
fonction . En comparant la valeur retournée à une valeur retournée par un appel précédent à GetClipboardSequenceNumber
, un programme peut déterminer si le contenu du Presse-papiers a changé. Cette méthode convient mieux aux programmes qui utilisent les résultats en cache en fonction du contenu du Presse-papiers actuel et qui doivent savoir si les calculs sont toujours valides avant d’utiliser les résultats de ce cache. Notez qu’il ne s’agit pas d’une méthode de notification et ne doit pas être utilisée dans une boucle d’interrogation. Pour être averti lorsque le contenu du Presse-papiers change, utilisez un écouteur de format de Presse-papiers ou une visionneuse du Presse-papiers.
Création d’un écouteur de format du Presse-papiers
Un écouteur de format presse-papiers est une fenêtre qui s’est inscrite pour être avertie lorsque le contenu du Presse-papiers a changé. Cette méthode est recommandée plutôt que de créer une fenêtre de visionneuse du Presse-papiers, car elle est plus simple à implémenter et évite les problèmes si les programmes ne parviennent pas à gérer correctement la chaîne de visionneuse du Presse-papiers ou si une fenêtre de la chaîne de visionneuse du Presse-papiers cesse de répondre aux messages.
Une fenêtre s’inscrit en tant qu’écouteur de format presse-papiers en appelant la AddClipboardFormatListener
fonction . Lorsque le contenu du Presse-papiers change, un message est publié dans WM_CLIPBOARDUPDATE
la fenêtre. L’inscription reste valide jusqu’à ce que la fenêtre se désinscrit elle-même en appelant la RemoveClipboardFormatListener
fonction .
Création d’une fenêtre visionneuse du Presse-papiers
Une fenêtre de visionneuse du Presse-papiers affiche le contenu actuel du Presse-papiers et reçoit des messages lorsque le contenu du Presse-papiers change. Pour créer une fenêtre de visionneuse du Presse-papiers, votre application doit effectuer les opérations suivantes :
- Ajoutez la fenêtre à la chaîne de visionneuse du Presse-papiers.
- Traitez le
WM_CHANGECBCHAIN
message. - Traitez le
WM_DRAWCLIPBOARD
message. - Supprimez la fenêtre de la chaîne de visionneuse du Presse-papiers avant qu’elle ne soit détruite.
Ajout d’une fenêtre à la chaîne de visionneuse du Presse-papiers
Une fenêtre s’ajoute à la chaîne de visionneuse du Presse-papiers en appelant la SetClipboardViewer
fonction . La valeur de retour est le handle de la fenêtre suivante dans la chaîne. Une fenêtre doit effectuer le suivi de cette valeur, par exemple en l’enregistrant dans une variable statique nommée hwndNextViewer
.
L’exemple suivant ajoute une fenêtre à la chaîne de visionneuse du Presse-papiers en réponse au WM_CREATE
message.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Les extraits de code sont affichés pour les tâches suivantes :
-
Traitement du
WM_CHANGECBCHAIN
message - Suppression d’une fenêtre de la chaîne de visionneuse du Presse-papiers
-
Traitement du
WM_DRAWCLIPBOARD
message - Exemple de visionneuse du Presse-papiers
Traitement du WM_CHANGECBCHAIN
message
Une fenêtre de visionneuse du Presse-papiers reçoit le WM_CHANGECBCHAIN
message lorsqu’une autre fenêtre se supprime de la chaîne de visionneuse du Presse-papiers. Si la fenêtre supprimée est la fenêtre suivante de la chaîne, la fenêtre qui reçoit le message doit dissocier la fenêtre suivante de la chaîne. Sinon, ce message doit être passé à la fenêtre suivante de la chaîne.
L’exemple suivant montre le traitement du WM_CHANGECBCHAIN
message.
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;
Suppression d’une fenêtre de la chaîne de visionneuse du Presse-papiers
Pour se supprimer de la chaîne de visionneuse du Presse-papiers, une fenêtre appelle la ChangeClipboardChain
fonction . L’exemple suivant supprime une fenêtre de la chaîne de visionneuse du Presse-papiers en réponse au WM_DESTROY
message.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Traitement du WM_DRAWCLIPBOARD
message
Le WM_DRAWCLIPBOARD
message avertit une fenêtre de visionneuse du Presse-papiers que le contenu du Presse-papiers a changé. Une fenêtre doit effectuer les opérations suivantes lors du traitement du WM_DRAWCLIPBOARD
message :
- Déterminez les formats de Presse-papiers disponibles à afficher.
- Récupérez les données du Presse-papiers et affichez-les dans la fenêtre. Ou si le format du Presse-papiers est
CF_OWNERDISPLAY
, envoyez unWM_PAINTCLIPBOARD
message au propriétaire du Presse-papiers. - Envoyez le message à la fenêtre suivante dans la chaîne de visionneuse du Presse-papiers.
Pour obtenir un exemple de traitement du WM_DRAWCLIPBOARD
message, consultez l’exemple de liste dans Exemple de visionneuse du Presse-papiers.
Exemple de visionneuse du Presse-papiers
L’exemple suivant montre une application de visionneuse du Presse-papiers simple.
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;
}