Delen via


Het Klembord gebruiken

Deze sectie bevat codevoorbeelden voor de volgende taken:

De opdrachten knippen, kopiëren en plakken implementeren

In deze sectie wordt beschreven hoe standaard Knippen, Kopiërenen opdrachten in een toepassing worden geïmplementeerd. In het voorbeeld in deze sectie worden deze methoden gebruikt om gegevens op het klembord te plaatsen met behulp van een geregistreerde klembordindeling, de CF_OWNERDISPLAY-indeling en de CF_TEXT-indeling. De geregistreerde indeling wordt gebruikt om rechthoekige of elliptische tekstvensters weer te geven, ook wel labels genoemd.

Gegevens selecteren

Voordat informatie naar het klembord kan worden gekopieerd, moet de gebruiker specifieke informatie selecteren die moet worden gekopieerd of geknipt. Een toepassing moet een middel bieden voor de gebruiker om informatie in een document te selecteren en een soort visuele feedback om geselecteerde gegevens aan te geven.

Een menu Bewerken maken

Een toepassing moet een acceleratortabel laden met de standaardtoetsencombinaties voor de Bewerken menuopdrachten. De functie TranslateAccelerator moet worden toegevoegd aan de berichtlus van de toepassing om de accelerators van kracht te laten worden. Zie Toetsenbordversnellersvoor meer informatie over toetsenbordversnellers.

Het WM_INITMENUPOPUP-bericht verwerken

Niet alle klembordopdrachten zijn op elk gewenst moment beschikbaar voor de gebruiker. Een toepassing moet het WM_INITMENUPOPUP bericht verwerken om de menu-items voor beschikbare opdrachten in te schakelen en niet-beschikbare opdrachten uit te schakelen.

Hier volgt het WM_INITMENUPOPUP geval voor een toepassing met de naam Label.

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

De functie InitMenu wordt als volgt gedefinieerd.

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

Het WM_COMMAND-bericht verwerken

Als u menuopdrachten wilt verwerken, voegt u de WM_COMMAND case toe aan de hoofdvensterprocedure van uw toepassing. Hier volgt het WM_COMMAND-geval voor de vensterprocedure van de Label-applicatie.

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; 

Als u de opdrachten Copy en Cut wilt uitvoeren, roept de vensterprocedure de door de toepassing gedefinieerde EditCopy-functie aan. Zie Gegevens kopiëren naar het Klembordvoor meer informatie. Als u de opdracht Plakken wilt uitvoeren, roept de vensterprocedure de door de toepassing gedefinieerde EditPaste-functie aan. Voor meer informatie over de functie EditPaste, zie informatie plakken van het klembord.

Gegevens kopiëren naar het Klembord

In de labeltoepassing kopieert de toepassingsgedefinieerde EditCopy-functie de huidige selectie naar het klembord. Deze functie doet het volgende:

  1. Opent het klembord door de functie OpenClipboard aan te roepen.
  2. Hiermee wordt het klembord leeg gemaakt door de functie EmptyClipboard aan te roepen.
  3. Roept de SetClipboardData functie eenmaal aan voor elke klembordindeling die de toepassing biedt.
  4. Sluit het klembord door de functie CloseClipboard aan te roepen.

Afhankelijk van de huidige selectie kopieert de functie EditCopy een tekstbereik of kopieert u een door de toepassing gedefinieerde structuur die een heel label vertegenwoordigt. De structuur, LABELBOXgenoemd, wordt als volgt gedefinieerd.

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

Hieronder ziet u de functie EditCopy.

BOOL WINAPI EditCopy(VOID) 
{ 
    PLABELBOX pbox; 
    LPTSTR  lptstrCopy; 
    HGLOBAL hglbCopy; 
    int ich1, ich2, cch; 
 
    if (hwndSelected == NULL) 
        return FALSE; 
 
    // Open the clipboard, and empty it. 
 
    if (!OpenClipboard(hwndMain)) 
        return FALSE; 
    EmptyClipboard(); 
 
    // Get a pointer to the structure for the selected label. 
 
    pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0); 
 
    // If text is selected, copy it using the CF_TEXT format. 
 
    if (pbox->fEdit) 
    { 
        if (pbox->ichSel == pbox->ichCaret)     // zero length
        {   
            CloseClipboard();                   // selection 
            return FALSE; 
        } 
 
        if (pbox->ichSel < pbox->ichCaret) 
        { 
            ich1 = pbox->ichSel; 
            ich2 = pbox->ichCaret; 
        } 
        else 
        { 
            ich1 = pbox->ichCaret; 
            ich2 = pbox->ichSel; 
        } 
        cch = ich2 - ich1; 
 
        // Allocate a global memory object for the text. 
 
        hglbCopy = GlobalAlloc(GMEM_MOVEABLE, 
            (cch + 1) * sizeof(TCHAR)); 
        if (hglbCopy == NULL) 
        { 
            CloseClipboard(); 
            return FALSE; 
        } 
 
        // Lock the handle and copy the text to the buffer. 
 
        lptstrCopy = GlobalLock(hglbCopy); 
        memcpy(lptstrCopy, &pbox->atchLabel[ich1], 
            cch * sizeof(TCHAR)); 
        lptstrCopy[cch] = (TCHAR) 0;    // null character 
        GlobalUnlock(hglbCopy); 
 
        // Place the handle on the clipboard. 
 
        SetClipboardData(CF_TEXT, hglbCopy); 
    } 
 
    // If no text is selected, the label as a whole is copied. 
 
    else 
    { 
        // Save a copy of the selected label as a local memory 
        // object. This copy is used to render data on request. 
        // It is freed in response to the WM_DESTROYCLIPBOARD 
        // message. 
 
        pboxLocalClip = (PLABELBOX) LocalAlloc( 
            LMEM_FIXED, 
            sizeof(LABELBOX) 
        ); 
        if (pboxLocalClip == NULL) 
        { 
            CloseClipboard(); 
            return FALSE; 
        } 
        memcpy(pboxLocalClip, pbox, sizeof(LABELBOX)); 
        pboxLocalClip->fSelected = FALSE; 
        pboxLocalClip->fEdit = FALSE; 
 
        // Place a registered clipboard format, the owner-display 
        // format, and the CF_TEXT format on the clipboard using 
        // delayed rendering. 
 
        SetClipboardData(uLabelFormat, NULL); 
        SetClipboardData(CF_OWNERDISPLAY, NULL); 
        SetClipboardData(CF_TEXT, NULL); 
    } 
 
    // Close the clipboard. 
 
    CloseClipboard(); 
 
    return TRUE; 
}

Gegevens van het Klembord plakken

In de labeltoepassing plakt de door de toepassing gedefinieerde EditPaste functie de inhoud van het klembord. Deze functie doet het volgende:

  1. U opent het klembord door de functie OpenClipboard aan te roepen.
  2. Bepaalt welke van de beschikbare klembordformaten kunnen worden opgehaald.
  3. Haalt de handle op naar de gegevens in de geselecteerde indeling door de GetClipboardData-functie aan te roepen.
  4. Hiermee voegt u een kopie van de gegevens in het document in. Het handvat dat door GetClipboardData wordt geretourneerd, is nog steeds eigendom van het klembord, dus een applicatie mag het niet vrijgeven of ontgrendeld laten.
  5. Sluit het klembord door functie CloseClipboard aan te roepen.

Als een label is geselecteerd en een invoegpositie bevat, wordt met de functie EditPaste de tekst vanaf het klembord op de invoegpositie ingevoegd. Als er geen selectie is of als er een label is geselecteerd, maakt de functie een nieuw label met behulp van de door de toepassing gedefinieerde LABELBOX structuur op het klembord. De LABELBOX structuur wordt op het klembord geplaatst met behulp van een geregistreerde klembordindeling.

De structuur, LABELBOXgenoemd, wordt als volgt gedefinieerd.

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

Hieronder ziet u de functie EditPaste.

VOID WINAPI EditPaste(VOID) 
{ 
    PLABELBOX pbox; 
    HGLOBAL   hglb; 
    LPTSTR    lptstr; 
    PLABELBOX pboxCopy; 
    int cx, cy; 
    HWND hwnd; 
 
    pbox = hwndSelected == NULL ? NULL : 
        (PLABELBOX) GetWindowLong(hwndSelected, 0); 
 
    // If the application is in edit mode, 
    // get the clipboard text. 
 
    if (pbox != NULL && pbox->fEdit) 
    { 
        if (!IsClipboardFormatAvailable(CF_TEXT)) 
            return; 
        if (!OpenClipboard(hwndMain)) 
            return; 
 
        hglb = GetClipboardData(CF_TEXT); 
        if (hglb != NULL) 
        { 
            lptstr = GlobalLock(hglb); 
            if (lptstr != NULL) 
            { 
                // Call the application-defined ReplaceSelection 
                // function to insert the text and repaint the 
                // window. 
 
                ReplaceSelection(hwndSelected, pbox, lptstr); 
                GlobalUnlock(hglb); 
            } 
        } 
        CloseClipboard(); 
 
        return; 
    } 
 
    // If the application is not in edit mode, 
    // create a label window. 
 
    if (!IsClipboardFormatAvailable(uLabelFormat)) 
        return; 
    if (!OpenClipboard(hwndMain)) 
        return; 
 
    hglb = GetClipboardData(uLabelFormat); 
    if (hglb != NULL) 
    { 
        pboxCopy = GlobalLock(hglb); 
        if (pboxCopy != NULL) 
        { 
            cx = pboxCopy->rcText.right + CX_MARGIN; 
            cy = pboxCopy->rcText.top * 2 + cyText; 
 
            hwnd = CreateWindowEx( 
                WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT, 
                atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy, 
                hwndMain, NULL, hinst, NULL 
            ); 
            if (hwnd != NULL) 
            { 
                pbox = (PLABELBOX) GetWindowLong(hwnd, 0); 
                memcpy(pbox, pboxCopy, sizeof(LABELBOX)); 
                ShowWindow(hwnd, SW_SHOWNORMAL); 
                SetFocus(hwnd); 
            } 
            GlobalUnlock(hglb); 
        } 
    } 
    CloseClipboard(); 
}

Een klembordindeling registreren

Als u een klembordformaat wilt registreren, voegt u een aanroep van de RegisterClipboardFormat-functie toe aan de initialisatiefunctie van uw toepassing, als volgt.

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

De WM_RENDERFORMAT- en WM_RENDERALLFORMATS-berichten verwerken

Als een venster een NULL handle doorgeeft aan de functie SetClipboardData, moet het de WM_RENDERFORMAT en WM_RENDERALLFORMATS berichten verwerken om gegevens op aanvraag weer te geven.

Als een venster het weergeven van een specifieke indeling vertraagt en vervolgens een andere toepassing gegevens in die indeling aanvraagt, wordt er een WM_RENDERFORMAT bericht naar het venster verzonden. Bovendien, als een venster het weergeven van een of meer indelingen vertraagt en als sommige van deze indelingen onrendeerd blijven wanneer het venster op het punt staat te worden vernietigd, wordt er vóór de vernietiging een WM_RENDERALLFORMATS bericht naar het venster verzonden.

Als u een klembordindeling wilt weergeven, moet de vensterprocedure een niet-NULL gegevensgreep op het klembord plaatsen met behulp van de functie SetClipboardData. Als de vensterprocedure een indeling weergeeft als reactie op het WM_RENDERFORMAT bericht, mag het klembord niet worden geopend voordat SetClipboardDatawordt aangeroepen. Echter, als er een of meer formaten worden weergegeven als reactie op het WM_RENDERALLFORMATS-bericht, moet het klembord worden geopend en moet worden gecontroleerd of het venster nog steeds eigenaar is van het klembord voordat SetClipboardDatawordt aangeroepen. Het klembord moet worden gesloten voordat er wordt teruggekeerd.

De labeltoepassing verwerkt de WM_RENDERFORMAT en WM_RENDERALLFORMATS berichten als volgt.

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

In beide gevallen roept de vensterprocedure de door de toepassing gedefinieerde RenderFormat-functie aan, die als volgt is gedefinieerd.

De structuur, LABELBOXgenoemd, wordt als volgt gedefinieerd.

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

Het WM_DESTROYCLIPBOARD-bericht verwerken

Een venster kan het WM_DESTROYCLIPBOARD bericht verwerken om resources vrij te maken die zijn gereserveerd om vertraagde rendering te ondersteunen. Wanneer u bijvoorbeeld een label naar het klembord kopieert, wijst de toepassing Label een lokaal geheugenobject toe. Vervolgens wordt dit object als volgt vrijgemaakt als reactie op het WM_DESTROYCLIPBOARD bericht.

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

De Owner-Display Klembordformaat gebruiken

Als een venster informatie op het klembord plaatst met behulp van de CF_OWNERDISPLAY klembordindeling, moet dit het volgende doen:

  • Het WM_PAINTCLIPBOARD bericht verwerken. Dit bericht wordt naar de eigenaar van het Klembord verzonden wanneer een deel van het venster van het Klembordviewer opnieuw moet worden geschilderd.
  • Het WM_SIZECLIPBOARD bericht verwerken. Dit bericht wordt verzonden naar de eigenaar van het klembord wanneer het venster van de klembordviewer is gewijzigd of de inhoud ervan is gewijzigd. Normaal gesproken reageert een venster op dit bericht door de schuifposities en bereiken voor het klembordviewervenster in te stellen. Als reactie op dit bericht werkt de labeltoepassing ook een SIZE structuur voor het klembordviewervenster bij.
  • De WM_HSCROLLCLIPBOARD en WM_VSCROLLCLIPBOARD berichten verwerken. Deze berichten worden verzonden naar de eigenaar van het klembord wanneer een schuifbalk-gebeurtenis plaatsvindt in het venster van de klembordviewer.
  • Het WM_ASKCBFORMATNAME bericht verwerken. Het klembordviewervenster verzendt dit bericht naar een toepassing om de naam van het eigenaarsweergave-formaat op te halen.

De vensterprocedure voor de labeltoepassing verwerkt deze berichten als volgt.

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

Inhoud van Klembord bewaken

Er zijn drie manieren om wijzigingen in het klembord te controleren. De oudste methode is door een klembordweergavevenster te maken. Windows 2000 heeft de mogelijkheid toegevoegd om een query uit te voeren op het reeksnummer van het Klembord en Windows Vista heeft ondersteuning toegevoegd voor listeners voor de klembordindeling. Klembordviewer-vensters worden ondersteund voor compatibiliteit met eerdere versies van Windows. Nieuwe programma's moeten klembordformaatluisteraars of het volgnummer van het klembord gebruiken.

Een query uitvoeren op het klembordreeksnummer

Telkens wanneer de inhoud van het klembord wordt gewijzigd, wordt een 32-bits waarde, ook wel het klembordreeksnummer genoemd, verhoogd. Een programma kan het huidige klembordreeksnummer ophalen door de GetClipboardSequenceNumber functie aan te roepen. Door de geretourneerde waarde te vergelijken met een waarde die door een vorige aanroep naar GetClipboardSequenceNumberwordt geretourneerd, kan een programma bepalen of de inhoud van het klembord is gewijzigd. Deze methode is geschikter voor programma's die resultaten opslaan op basis van de huidige inhoud van het klembord en moeten weten of de berekeningen nog geldig zijn voordat de resultaten van die cache worden gebruikt. Houd er rekening mee dat dit geen notificatiemethode is en niet mag worden gebruikt in een polling-lus. Als u een melding wilt ontvangen wanneer de inhoud van het klembord wordt gewijzigd, gebruikt u een listener voor de klembordindeling of een klembordviewer.

Een listener voor klembordindeling maken

Een listener voor klembordindeling is een venster dat is geregistreerd om op de hoogte te worden gesteld wanneer de inhoud van het klembord is gewijzigd. Deze methode wordt aanbevolen boven het maken van een klembordviewervenster, omdat het eenvoudiger is te implementeren en problemen te vermijden als programma's de keten van de klembordviewer niet goed onderhouden of als een venster in de keten van de klembordviewer stopt met reageren op berichten.

Een venster wordt geregistreerd als een klembordformaatlistener door de functie AddClipboardFormatListener aan te roepen. Wanneer de inhoud van het klembord wordt gewijzigd, krijgt het venster een WM_CLIPBOARDUPDATE bericht. De registratie blijft geldig totdat de registratie van het venster ongedaan wordt gemaakt door de RemoveClipboardFormatListener functie aan te roepen.

Een KlembordViewer-venster maken

In een klembordviewervenster wordt de huidige inhoud van het klembord weergegeven en worden berichten ontvangen wanneer de inhoud van het klembord wordt gewijzigd. Als u een klembordviewervenster wilt maken, moet uw toepassing het volgende doen:

  • Voeg het venster toe aan de viewerketen van het Klembord.
  • Het WM_CHANGECBCHAIN bericht verwerken.
  • Het WM_DRAWCLIPBOARD bericht verwerken.
  • Verwijder het venster uit de klembordviewerketen voordat het wordt vernietigd.

Een venster toevoegen aan de klembord-kijkerketen

Een venster voegt zichzelf toe aan de viewerketen van het Klembord door de functie SetClipboardViewer aan te roepen. De retourwaarde is de ingang naar het volgende venster in de keten. Een venster moet deze waarde bijhouden, bijvoorbeeld door deze op te slaan in een statische variabele met de naam hwndNextViewer.

In het volgende voorbeeld wordt een venster toegevoegd aan de klembord-viewerketen in reactie op het WM_CREATE-bericht.

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

Codefragmenten worden weergegeven voor de volgende taken:

Het WM_CHANGECBCHAIN-bericht verwerken

Een klembordkijkervenster ontvangt het WM_CHANGECBCHAIN-bericht wanneer een ander venster zichzelf verwijdert uit de klembordkijkerketen. Als het venster dat wordt verwijderd het volgende venster in de keten is, moet het venster dat het bericht ontvangt, het volgende venster loskoppelen van de keten. Anders moet dit bericht worden doorgegeven aan het volgende venster in de keten.

In het volgende voorbeeld ziet u de verwerking van het WM_CHANGECBCHAIN bericht.

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;

Een venster verwijderen uit de keten van klembordviewers

Om zichzelf uit de klembord viewerketen te verwijderen, roept een venster de functie ChangeClipboardChain aan. In het volgende voorbeeld wordt een venster uit de klembordviewerketen verwijderd als reactie op het WM_DESTROY-bericht.

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

Het WM_DRAWCLIPBOARD-bericht verwerken

Het WM_DRAWCLIPBOARD-bericht informeert een venster voor klembordweergave dat de inhoud van het klembord is gewijzigd. Een venster moet het volgende doen bij het verwerken van het WM_DRAWCLIPBOARD bericht:

  1. Bepaal welke van de beschikbare klembordindelingen moet worden weergegeven.
  2. Haal de klembordgegevens op en geef deze weer in het venster. Ofwel, als de klembordindeling CF_OWNERDISPLAYis, stuurt u een WM_PAINTCLIPBOARD-bericht naar de eigenaar van het klembord.
  3. Verzend het bericht naar het volgende venster in de viewerketen van het Klembord.

Zie de voorbeeldvermelding in Voorbeeld van een Klembordviewer-voor een voorbeeld van het verwerken van het WM_DRAWCLIPBOARD bericht.

Voorbeeld van een Klembordviewer

In het volgende voorbeeld ziet u een eenvoudige klembordviewertoepassing.

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