Verwenden der Zwischenablage

Dieser Abschnitt enthält Codebeispiele für die folgenden Aufgaben:

Implementieren der Befehle Ausschneiden, Kopieren und Einfügen

In diesem Abschnitt wird beschrieben, wie die Standardbefehle Ausschneiden, Kopieren und Einfügen in einer Anwendung implementiert werden. Im Beispiel in diesem Abschnitt werden diese Methoden verwendet, um Daten mithilfe eines registrierten Zwischenablageformats, des CF_OWNERDISPLAY Formats und des Formats in der CF_TEXT Zwischenablage zu platzieren. Das registrierte Format wird verwendet, um rechteckige oder elliptische Textfenster darzustellen, die als Bezeichnungen bezeichnet werden.

Auswählen von Daten

Bevor Informationen in die Zwischenablage kopiert werden können, muss der Benutzer bestimmte Informationen auswählen, die kopiert oder ausgeschnitten werden sollen. Eine Anwendung sollte dem Benutzer die Möglichkeit bieten, Informationen in einem Dokument auszuwählen, und eine Art visuelles Feedback, um ausgewählte Daten anzugeben.

Erstellen eines Bearbeitungsmenüs

Eine Anwendung sollte eine Zugriffstastentabelle laden, die die Standardtastenbeschleunigungen für die Menübefehle Bearbeiten enthält. Die TranslateAccelerator Funktion muss der Nachrichtenschleife der Anwendung hinzugefügt werden, damit die Zugriffstasten wirksam werden. Weitere Informationen zu Tastaturbeschleunigungen finden Sie unter Tastaturbeschleunigungen.

Verarbeiten der WM_INITMENUPOPUP Nachricht

Nicht alle Befehle der Zwischenablage stehen dem Benutzer zu einem bestimmten Zeitpunkt zur Verfügung. Eine Anwendung sollte die WM_INITMENUPOPUP Meldung verarbeiten, um die Menüelemente für verfügbare Befehle zu aktivieren und nicht verfügbare Befehle zu deaktivieren.

Im Folgenden ist der WM_INITMENUPOPUP Fall für eine Anwendung namens Label.

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

Die InitMenu Funktion ist wie folgt definiert.

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

Verarbeiten der WM_COMMAND Nachricht

Um Menübefehle zu verarbeiten, fügen Sie den WM_COMMAND Fall der Standard-Fensterprozedur Ihrer Anwendung hinzu. Es folgt der Fall für die WM_COMMAND Fensterprozedur der Label-Anwendung.

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; 

Um die Befehle Kopieren und Ausschneiden auszuführen, ruft die Fensterprozedur die anwendungsdefinierte Funktion auf EditCopy . Weitere Informationen finden Sie unter Kopieren von Informationen in die Zwischenablage. Um den Befehl Einfügen auszuführen, ruft die Fensterprozedur die anwendungsdefinierte Funktion auf EditPaste . Weitere Informationen zur EditPaste Funktion finden Sie unter Einfügen von Informationen aus der Zwischenablage.

Kopieren von Informationen in die Zwischenablage

In der Label-Anwendung kopiert die anwendungsdefinierte EditCopy-Funktion die aktuelle Auswahl in die Zwischenablage. Diese Funktion führt Folgendes aus:

  1. Öffnet die Zwischenablage durch Aufrufen der OpenClipboard Funktion.
  2. Leert die Zwischenablage durch Aufrufen der EmptyClipboard Funktion.
  3. Ruft die SetClipboardData Funktion einmal für jedes Von der Anwendung bereitgestellte Zwischenablageformat auf.
  4. Schließt die Zwischenablage durch Aufrufen der CloseClipboard Funktion.

Abhängig von der aktuellen Auswahl kopiert die EditCopy-Funktion entweder einen Textbereich oder kopiert eine anwendungsdefinierte Struktur, die eine gesamte Bezeichnung darstellt. Die -Struktur mit dem Namen LABELBOXwird wie folgt definiert.

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

Es folgt die EditCopy -Funktion.

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

Einfügen von Informationen aus der Zwischenablage

In der Bezeichnungsanwendung fügt die anwendungsdefinierte EditPaste Funktion den Inhalt der Zwischenablage ein. Diese Funktion führt Folgendes aus:

  1. Öffnet die Zwischenablage durch Aufrufen der OpenClipboard Funktion.
  2. Bestimmt, welche der verfügbaren Zwischenablageformate abgerufen werden sollen.
  3. Ruft das Handle für die Daten im ausgewählten Format ab, indem die GetClipboardData -Funktion aufgerufen wird.
  4. Fügt eine Kopie der Daten in das Dokument ein. Das von GetClipboardData zurückgegebene Handle gehört weiterhin der Zwischenablage, sodass eine Anwendung es nicht freigeben oder gesperrt lassen darf.
  5. Schließt die Zwischenablage durch Aufrufen der CloseClipboard Funktion.

Wenn eine Bezeichnung ausgewählt ist und eine Einfügemarke enthält, fügt die Funktion EditPaste den Text aus der Zwischenablage an der Einfügemarke ein. Wenn keine Auswahl vorhanden ist oder eine Bezeichnung ausgewählt ist, erstellt die Funktion eine neue Bezeichnung unter Verwendung der anwendungsdefinierte LABELBOX Struktur in der Zwischenablage. Die LABELBOX Struktur wird mithilfe eines registrierten Zwischenablageformats in der Zwischenablage platziert.

Die -Struktur mit dem Namen LABELBOXwird wie folgt definiert.

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

Es folgt die EditPaste -Funktion.

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

Registrieren eines Zwischenablageformats

Um ein Zwischenablageformat zu registrieren, fügen Sie der RegisterClipboardFormat instance Initialisierungsfunktion Ihrer Anwendung wie folgt einen Aufruf der Funktion hinzu.

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

Verarbeiten der WM_RENDERFORMAT Nachrichten und WM_RENDERALLFORMATS

Wenn ein Fenster ein NULL Handle an die SetClipboardData Funktion übergibt, muss es die WM_RENDERFORMAT Nachrichten und WM_RENDERALLFORMATS verarbeiten, um Daten auf Anforderung zu rendern.

Wenn ein Fenster das Rendern eines bestimmten Formats verzögert und dann eine andere Anwendung Daten in diesem Format anfordert, wird eine WM_RENDERFORMAT Nachricht an das Fenster gesendet. Wenn ein Fenster das Rendern eines oder mehrerer Formate verzögert und einige dieser Formate nicht gerendert werden, wenn das Fenster zerstört werden soll, wird eine WM_RENDERALLFORMATS Nachricht an das Fenster gesendet, bevor es zerstört wird.

Um ein Zwischenablageformat zu rendern, sollte die Fensterprozedur mithilfe der -Funktion ein Nicht-DatenhandleNULL in der SetClipboardData Zwischenablage platzieren. Wenn die Fensterprozedur als Reaktion auf die WM_RENDERFORMAT Nachricht ein Format rendert, darf sie die Zwischenablage nicht öffnen, bevor aufgerufen SetClipboardDatawird. Wenn jedoch ein oder mehrere Formate als Reaktion auf die WM_RENDERALLFORMATS Nachricht gerendert werden, muss die Zwischenablage geöffnet und überprüft werden, ob das Fenster noch die Zwischenablage besitzt, bevor SetClipboardDataaufgerufen wird, und es muss die Zwischenablage schließen, bevor zurückgegeben wird.

Die Label-Anwendung verarbeitet die WM_RENDERFORMAT Nachrichten und WM_RENDERALLFORMATS wie folgt.

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

In beiden Fällen ruft die Fensterprozedur die anwendungsdefinierte RenderFormat Funktion auf, die wie folgt definiert ist.

Die -Struktur mit dem Namen LABELBOXwird wie folgt definiert.

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

Verarbeiten der WM_DESTROYCLIPBOARD Nachricht

Ein Fenster kann die WM_DESTROYCLIPBOARD Nachricht verarbeiten, um alle Ressourcen freizugeben, die für die Unterstützung verzögerter Renderings bereitgestellt wurden. Beispielsweise weist die Label-Anwendung beim Kopieren einer Bezeichnung in die Zwischenablage ein lokales Speicherobjekt zu. Anschließend wird dieses Objekt als Antwort auf die WM_DESTROYCLIPBOARD Nachricht wie folgt freigegeben.

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

Verwenden des Owner-Display Zwischenablageformats

Wenn ein Fenster Mithilfe des Zwischenablageformats Informationen in der CF_OWNERDISPLAY Zwischenablage angibt, muss folgendes ausgeführt werden:

  • Verarbeiten Sie die WM_PAINTCLIPBOARD Nachricht. Diese Nachricht wird an den Besitzer der Zwischenablage gesendet, wenn ein Teil des Zwischenablageanzeigefensters neu gezeichnet werden muss.
  • Verarbeiten Sie die WM_SIZECLIPBOARD Nachricht. Diese Nachricht wird an den Besitzer der Zwischenablage gesendet, wenn die Größe des Zwischenablageanzeigefensters geändert wurde oder der Inhalt geändert wurde. In der Regel reagiert ein Fenster auf diese Nachricht, indem es die Bildlaufpositionen und Bereiche für das Zwischenablageanzeigefenster festlegt. Als Reaktion auf diese Meldung aktualisiert die Label-Anwendung auch eine SIZE Struktur für das Zwischenablageanzeigefenster.
  • Verarbeiten sie die WM_HSCROLLCLIPBOARD Nachrichten und WM_VSCROLLCLIPBOARD . Diese Nachrichten werden an den Besitzer der Zwischenablage gesendet, wenn ein Bildlaufleistenereignis im Fenster der Zwischenablageanzeige auftritt.
  • Verarbeiten Sie die WM_ASKCBFORMATNAME Nachricht. Das Zwischenablageanzeigefenster sendet diese Nachricht an eine Anwendung, um den Namen des Besitzer-Anzeigeformats abzurufen.

Die Fensterprozedur für die Label-Anwendung verarbeitet diese Meldungen wie folgt.

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

Überwachen des Zwischenablageinhalts

Es gibt drei Möglichkeiten, Änderungen an der Zwischenablage zu überwachen. Die älteste Methode besteht darin, ein Zwischenablageanzeigefenster zu erstellen. Windows 2000 hat die Möglichkeit hinzugefügt, die Zwischenablagesequenznummer abzufragen, und Windows Vista hat Unterstützung für Zwischenablageformatlistener hinzugefügt. Zwischenablageanzeigefenster werden aus Gründen der Abwärtskompatibilität mit früheren Versionen von Windows unterstützt. Neue Programme sollten Zwischenablageformatlistener oder die Zwischenablagesequenznummer verwenden.

Abfragen der Zwischenablagesequenznummer

Jedes Mal, wenn sich der Inhalt der Zwischenablage ändert, wird ein 32-Bit-Wert, der als Zwischenablagesequenznummer bezeichnet wird, erhöht. Ein Programm kann die aktuelle Zwischenablagesequenznummer abrufen, indem es die GetClipboardSequenceNumber Funktion aufruft. Durch Vergleichen des zurückgegebenen Werts mit einem Wert, der von einem vorherigen Aufruf von GetClipboardSequenceNumberzurückgegeben wurde, kann ein Programm bestimmen, ob sich der Inhalt der Zwischenablage geändert hat. Diese Methode eignet sich besser für Programme, die Ergebnisse basierend auf dem aktuellen Zwischenablageinhalt zwischenspeichern und wissen müssen, ob die Berechnungen noch gültig sind, bevor sie die Ergebnisse aus diesem Cache verwenden. Beachten Sie, dass dies keine Benachrichtigungsmethode ist und nicht in einer Abfrageschleife verwendet werden sollte. Um benachrichtigt zu werden, wenn sich der Inhalt der Zwischenablage ändert, verwenden Sie einen Zwischenablageformatlistener oder einen Zwischenablage-Viewer.

Erstellen eines Zwischenablageformatlisteners

Ein Zwischenablageformatlistener ist ein Fenster, das registriert wurde, um benachrichtigt zu werden, wenn sich der Inhalt der Zwischenablage geändert hat. Diese Methode wird gegenüber dem Erstellen eines Zwischenablageanzeigefensters empfohlen, da es einfacher zu implementieren ist und Probleme vermeidet, wenn Programme die Zwischenablage-Viewerkette nicht ordnungsgemäß verwalten können oder wenn ein Fenster in der Zwischenablage-Viewerkette nicht mehr auf Nachrichten reagiert.

Ein Fenster registriert sich als Zwischenablageformatlistener, indem die AddClipboardFormatListener Funktion aufgerufen wird. Wenn sich der Inhalt der Zwischenablage ändert, wird dem Fenster eine WM_CLIPBOARDUPDATE Nachricht gesendet. Die Registrierung bleibt gültig, bis das Fenster die Registrierung durch Aufrufen der RemoveClipboardFormatListener Funktion auf hebt.

Erstellen eines Zwischenablageanzeigefensters

Ein Zwischenablageanzeigefenster zeigt den aktuellen Inhalt der Zwischenablage an und empfängt Nachrichten, wenn sich der Inhalt der Zwischenablage ändert. Um ein Zwischenablageanzeigefenster zu erstellen, muss Ihre Anwendung Folgendes tun:

  • Fügen Sie das Fenster der Zwischenablage-Viewerkette hinzu.
  • Verarbeiten Sie die WM_CHANGECBCHAIN Nachricht.
  • Verarbeiten Sie die WM_DRAWCLIPBOARD Nachricht.
  • Entfernen Sie das Fenster aus der Zwischenablage-Viewerkette, bevor es zerstört wird.

Hinzufügen eines Fensters zur Zwischenablageanzeigekette

Ein Fenster fügt sich selbst zur Zwischenablage-Viewerkette hinzu, indem die SetClipboardViewer Funktion aufgerufen wird. Der Rückgabewert ist das Handle zum nächsten Fenster in der Kette. Ein Fenster muss diesen Wert nachverfolgen, z. B. durch Speichern in einer statischen Variablen mit dem Namen hwndNextViewer.

Im folgenden Beispiel wird der Zwischenablage-Viewerkette als Reaktion auf die WM_CREATE Nachricht ein Fenster hinzugefügt.

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

Codeausschnitte werden für die folgenden Aufgaben angezeigt:

Verarbeiten der WM_CHANGECBCHAIN Nachricht

Ein Zwischenablageanzeigefenster empfängt die WM_CHANGECBCHAIN Meldung, wenn sich ein anderes Fenster aus der Zwischenablage-Viewerkette entfernt. Wenn das zu entfernende Fenster das nächste Fenster in der Kette ist, muss das Fenster, das die Nachricht empfängt, die Verknüpfung des nächsten Fensters mit der Kette aufheben. Andernfalls sollte diese Meldung an das nächste Fenster in der Kette übergeben werden.

Das folgende Beispiel zeigt die Verarbeitung der WM_CHANGECBCHAIN Nachricht.

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;

Entfernen eines Fensters aus der Zwischenablageanzeigekette

Um sich selbst aus der Zwischenablage-Viewerkette zu entfernen, ruft ein Fenster die ChangeClipboardChain Funktion auf. Im folgenden Beispiel wird ein Fenster als Reaktion auf die Nachricht aus der WM_DESTROY Zwischenablage-Viewerkette entfernt.

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

Verarbeiten der WM_DRAWCLIPBOARD Nachricht

Die WM_DRAWCLIPBOARD Nachricht benachrichtigt ein Zwischenablageanzeigefenster, dass sich der Inhalt der Zwischenablage geändert hat. Ein Fenster sollte beim Verarbeiten der Nachricht die WM_DRAWCLIPBOARD folgenden Schritte ausführen:

  1. Bestimmen Sie, welche der verfügbaren Zwischenablageformate angezeigt werden sollen.
  2. Rufen Sie die Zwischenablagedaten ab, und zeigen Sie sie im Fenster an. Oder wenn das Zwischenablageformat lautet CF_OWNERDISPLAY, senden Sie eine WM_PAINTCLIPBOARD Nachricht an den Besitzer der Zwischenablage.
  3. Senden Sie die Nachricht an das nächste Fenster in der Zwischenablageanzeigekette.

Ein Beispiel für die Verarbeitung der WM_DRAWCLIPBOARD Nachricht finden Sie in der Beispielliste unter Beispiel für eine Zwischenablageanzeige.

Beispiel für eine Zwischenablageanzeige

Das folgende Beispiel zeigt eine einfache Zwischenablage-Viewer-Anwendung.

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