Utilisation du presse-papiers

Cette section contient des exemples de code pour les tâches suivantes :

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 :

  1. Ouvre le Presse-papiers en appelant la OpenClipboard fonction .
  2. Vide le Presse-papiers en appelant la EmptyClipboard fonction .
  3. Appelle la SetClipboardData fonction une fois pour chaque format de Presse-papiers fourni par l’application.
  4. 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 :

  1. Ouvre le Presse-papiers en appelant la OpenClipboard fonction .
  2. Détermine les formats du Presse-papiers disponibles à récupérer.
  3. Récupère le handle dans les données au format sélectionné en appelant la GetClipboardData fonction .
  4. 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é.
  5. 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 une SIZE structure pour la fenêtre de visionneuse du Presse-papiers.
  • Traitez les WM_HSCROLLCLIPBOARD messages et WM_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

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 :

  1. Déterminez les formats de Presse-papiers disponibles à afficher.
  2. 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 un WM_PAINTCLIPBOARD message au propriétaire du Presse-papiers.
  3. 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; 
}