Verwenden der Zwischenablage
Dieser Abschnitt enthält Codebeispiele für die folgenden Aufgaben:
- Implementieren der Befehle "Ausschneiden", "Kopieren" und "Einfügen"
- Auswählen von Daten
- Erstellen eines Bearbeitungsmenüs
- Verarbeiten der WM _ INITMENUPOPUP-Nachricht
- Verarbeiten der WM _ COMMAND-Meldung
- Kopieren von Informationen in die Zwischenablage
- Eingefügte Informationen aus der Zwischenablage
- Registrieren eines Zwischenablageformats
- Verarbeiten der WM _ RENDERFORMAT- und WM _ RENDERALLFORMATS-Meldungen
- Verarbeiten der WM _ DESTROYCLIPBOARD-Nachricht
- Verwenden des Owner-Display-Zwischenablageformats
- Überwachen des Inhalts der Zwischenablage
- Abfragen der Sequenznummer der Zwischenablage
- Erstellen eines Zwischenablageformatlisteners
- Erstellen eines Zwischenablage-Viewer-Fensters
- Hinzufügen eines Fensters zur Zwischenablage-Viewer-Kette
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 in der Zwischenablage unter Verwendung eines registrierten Zwischenablageformats, des CF _ OWNERDISPLAY-Formats und des CF _ TEXT-Formats zu platzieren. Das registrierte Format wird verwendet, um rechteckige oder elliptische Textfenster, sogenannte Bezeichnungen, zu darstellen.
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 zur Angabe ausgewählter Daten bereitstellen.
Erstellen eines Bearbeitungsmenüs
Eine Anwendung sollte eine Zugriffstastentabelle laden, die die Standardtastenbeschleuniger 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 Tastenkombinationen finden Sie unter Tastenkombinationen.
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-Nachricht verarbeiten, um die Menüelemente für verfügbare Befehle zu aktivieren und nicht verfügbare Befehle zu deaktivieren.
Es folgt der WM _ INITMENUPOPUP-Fall für eine Anwendung mit dem Namen 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-Meldung
Um Menübefehle zu verarbeiten, fügen Sie die WM _ COMMAND-Case zum Hauptfensterprozedur Ihrer Anwendung hinzu. Im Folgenden finden Sie den WM _ COMMAND-Fall für die Fensterprozedur der Bezeichnungsanwendung.
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 EditCopy-Funktion auf. Weitere Informationen finden Sie unter Kopieren von Informationen in die Zwischenablage. Um den Befehl Einfügen auszuführen, ruft die Fensterprozedur die anwendungsdefinierte EditPaste-Funktion auf. Weitere Informationen zur EditPaste-Funktion finden Sie unter Eingefügte Informationen aus der Zwischenablage.
Kopieren von Informationen in die Zwischenablage
In der Anwendung Label kopiert die anwendungsdefinierte EditCopy-Funktion die aktuelle Auswahl in die Zwischenablage. Diese Funktion führt Folgendes aus:
- Öffnet die Zwischenablage durch Aufrufen der OpenClipboard-Funktion.
- Leert die Zwischenablage durch Aufrufen der EmptyClipboard-Funktion.
- Ruft die SetClipboardData-Funktion einmal für jedes von der Anwendung zur Verfügung stellende Zwischenablageformat auf.
- Schließt die Zwischenablage durch Aufrufen der CloseClipboard-Funktion.
Abhängig von der aktuellen Auswahl kopiert die EditCopy-Funktion entweder einen Textbereich oder eine anwendungsdefinierte Struktur, die eine gesamte Bezeichnung darstellt. Die Struktur namens LABELBOX wird 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;
}
Eingefügte Informationen aus der Zwischenablage
In der Anwendung Label eingefügt die anwendungsdefinierte EditPaste-Funktion den Inhalt der Zwischenablage. Diese Funktion führt Folgendes aus:
Öffnet die Zwischenablage durch Aufrufen der OpenClipboard-Funktion.
Bestimmt, welches der verfügbaren Zwischenablageformate abgerufen werden soll.
Ruft das Handle für die Daten im ausgewählten Format ab, indem die GetClipboardData-Funktion aufgerufen wird.
Fügt eine Kopie der Daten in das Dokument ein.
Das von GetClipboardData zurückgegebene Handle befindet sich weiterhin im Besitz der Zwischenablage, sodass eine Anwendung sie weder frei noch gesperrt lassen darf.
Schließt die Zwischenablage durch Aufrufen der CloseClipboard-Funktion.
Wenn eine Bezeichnung ausgewählt ist und eine Einfügemarke enthält, fügt die EditPaste-Funktion den Text an der Einfügemarke aus der Zwischenablage ein. Wenn keine Auswahl getroffen wird oder eine Bezeichnung ausgewählt ist, erstellt die Funktion mithilfe der anwendungsdefinierten LABELBOX-Struktur in der Zwischenablage eine neue Bezeichnung. Die LABELBOX-Struktur wird in der Zwischenablage mithilfe eines registrierten Zwischenablageformats platziert.
Die Struktur namens LABELBOX wird 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 wie folgt einen Aufruf der RegisterClipboardFormat-Funktion zur Instanzin initialisierungsfunktion Ihrer Anwendung 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- und WM _ RENDERALLFORMATS-Meldungen
Wenn ein Fenster ein NULL-Handle an die SetClipboardData-Funktion übergibt, muss es die WM _ RENDERFORMAT- und WM _ RENDERALLFORMATS-Meldungen 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 an fordert, wird eine WM _ RENDERFORMAT-Meldung an das Fenster gesendet. Wenn ein Fenster das Rendern eines oder mehrere Formate verzögert und einige dieser Formate nicht beschädigt werden, wenn das Fenster zerstört werden soll, wird eine WM _ RENDERALLFORMATS-Meldung an das Fenster gesendet, bevor es zerstört wird.
Zum Rendern eines Zwischenablageformats sollte die Fensterprozedur mithilfe der Funktion SetClipboardData ein Nicht-NULL-Datenhandle in der Zwischenablage platzieren. Wenn die Fensterprozedur ein Format als Reaktion auf die WM _ RENDERFORMAT-Meldung rendert, darf sie die Zwischenablage nicht öffnen, bevor SetClipboardData aufruft. Wenn jedoch ein oder mehrere Formate als Reaktion auf die WM _ RENDERALLFORMATS-Meldung gerendert werden, muss die Zwischenablage geöffnet und überprüft werden, ob das Fenster weiterhin die Zwischenablage besitzt, bevor SetClipboardData aufruft und die Zwischenablage vor der Rückgabe geschlossen werden muss.
Die Label-Anwendung verarbeitet die MELDUNGEN WM _ RENDERFORMAT 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 namens LABELBOX wird 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 frei zu geben, die es zur Unterstützung des verzögerten Renderings vorgesehen hat. Beispielsweise ordnet die Anwendung Label beim Kopieren einer Bezeichnung in die Zwischenablage ein lokales Speicherobjekt zu. Anschließend wird dieses Objekt als Reaktion auf die WM _ DESTROYCLIPBOARD-Nachricht wie folgt frei.
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
Verwenden des Owner-Display Zwischenablageformats
Wenn ein Fenster Informationen in der Zwischenablage mithilfe des CF OWNERDISPLAY-Zwischenablageformats _ platziert, muss folgende Schritte erfolgen:
Verarbeiten Sie die WM _ PAINTCLIPBOARD-Nachricht. Diese Meldung wird an den Besitzer der Zwischenablage gesendet, wenn ein Teil des Zwischenablageanzeigefensters neu gepaint werden muss.
Verarbeiten Sie die WM _ SIZECLIPBOARD-Nachricht. Diese Meldung 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 antwortet ein Fenster auf diese Meldung, indem die Bildlaufpositionen und -bereiche für das Viewerfenster der Zwischenablage festlegen. Als Reaktion auf diese Meldung aktualisiert die Anwendung Label auch eine SIZE-Struktur für das Viewerfenster der Zwischenablage.
Verarbeiten Sie die NACHRICHTEN WM _ HSCROLLCLIPBOARD und WM _ VSCROLLCLIPBOARD. Diese Meldungen werden an den Besitzer der Zwischenablage gesendet, wenn im Viewerfenster der Zwischenablage ein Bildlaufleistenereignis auftritt.
Verarbeiten Sie die _ WM ASKCBFORMATNAME-Nachricht. Das Viewerfenster der Zwischenablage sendet diese Meldung an eine Anwendung, um den Namen des Anzeigeformats des Besitzers abzurufen.
Die Fensterprozedur für die Label-Anwendung verarbeitet diese Nachrichten 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 Inhalts der Zwischenablage
Es gibt drei Möglichkeiten, Änderungen an der Zwischenablage zu überwachen. Die älteste Methode besteht im Erstellen eines Viewerfensters für die Zwischenablage. Windows 2000 wurde die Möglichkeit zum Abfragen der Sequenznummer der Zwischenablage hinzugefügt, und Windows Vista wurde Unterstützung für Zwischenablageformatlistener hinzugefügt. Zwischenablageanzeigefenster werden aus Gründen der Abwärtskompatibilität mit früheren Versionen von Windows. Neue Programme sollten Zwischenablageformatlistener oder die Sequenznummer der Zwischenablage verwenden.
Abfragen der Sequenznummer der Zwischenablage
Bei jeder Änderung des Inhalts der Zwischenablage wird ein 32-Bit-Wert erhöht, der als Sequenznummer der Zwischenablage bezeichnet wird. Ein Programm kann die aktuelle Sequenznummer der Zwischenablage abrufen, indem es die GetClipboardSequenceNumber-Funktion aufruft. Durch Vergleichen des zurückgegebenen Werts mit einem Wert, der durch einen vorherigen Aufruf von GetClipboardSequenceNumber zurü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 Inhalt der Zwischenablage zwischenspeichern und wissen müssen, ob die Berechnungen noch gültig sind, bevor die Ergebnisse aus diesem Cache verwendet werden. Beachten Sie, dass dies keine Benachrichtigungsmethode ist und nicht in einer Abrufschleife verwendet werden sollte. Verwenden Sie einen Zwischenablageformatlistener oder einen Zwischenablage-Viewer, um benachrichtigt zu werden, wenn sich der Inhalt der Zwischenablage ändert.
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 empfohlen, um ein Viewerfenster für die Zwischenablage zu erstellen, da es einfacher zu implementieren ist und Probleme vermieden werden, wenn Programme die Zwischenablageanzeigekette nicht ordnungsgemäß verwalten können oder wenn ein Fenster in der Zwischenablage-Viewerkette nicht mehr auf Nachrichten reagiert.
Ein Fenster wird als Zwischenablageformatlistener registriert, indem die AddClipboardFormatListener-Funktion aufruft. Wenn sich der Inhalt der Zwischenablage ändert, wird dem Fenster eine WM _ CLIPBOARDUPDATE-Meldung gesendet. Die Registrierung bleibt gültig, bis die Registrierung des Fensters durch Aufrufen der RemoveClipboardFormatListener-Funktion aufgehoben wird.
Erstellen eines Zwischenablage-Viewer-Fensters
Ein Zwischenablage-Viewer-Fenster zeigt den aktuellen Inhalt der Zwischenablage an und empfängt Meldungen, wenn sich der Inhalt der Zwischenablage ändert. Um ein Viewerfenster für die Zwischenablage zu erstellen, muss Ihre Anwendung Folgendes tun:
- Fügen Sie das Fenster der Viewer-Kette der Zwischenablage hinzu.
- Verarbeiten Sie die WM _ CHANGECBCHAIN-Nachricht.
- Verarbeiten Sie die WM _ DRAWCLIPBOARD-Nachricht.
- Entfernen Sie das Fenster aus der Zwischenablage-Viewer-Kette, bevor es zerstört wird.
Hinzufügen eines Fensters zur Zwischenablage-Viewer-Kette
Ein Fenster fügt sich der Viewer-Kette der Zwischenablage durch Aufrufen der SetClipboardViewer-Funktion hinzu. Der Rückgabewert ist das Handle für das nächste 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 Viewerkette der Zwischenablage als Reaktion auf die WM _ CREATE-Meldung ein Fenster hinzufü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
- Entfernen eines Fensters aus der Zwischenablage-Viewer-Kette
- Verarbeiten der WM _ DRAWCLIPBOARD-Nachricht
- Beispiel für einen Zwischenablage-Viewer
Verarbeiten der WM _ CHANGECBCHAIN-Nachricht
Ein Zwischenablageanzeigefenster empfängt die WM _ CHANGECBCHAIN-Meldung, wenn ein anderes Fenster sich selbst 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 entfernen. 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 Zwischenablage-Viewer-Kette
Um sich selbst aus der Viewer-Kette der Zwischenablage zu entfernen, ruft ein Fenster die ChangeClipboardChain-Funktion auf. Im folgenden Beispiel wird ein Fenster aus der Viewerkette der Zwischenablage als Reaktion auf die WM _ DESTROY-Nachricht entfernt.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Verarbeiten der WM _ DRAWCLIPBOARD-Nachricht
Die WM _ DRAWCLIPBOARD-Meldung benachrichtigt ein Zwischenablageanzeigefenster, dass sich der Inhalt der Zwischenablage geändert hat. Bei der Verarbeitung der WM _ DRAWCLIPBOARD-Nachricht sollte ein Fenster Folgendes tun:
- Bestimmen Sie, welches der verfügbaren Zwischenablageformate angezeigt werden soll.
- Rufen Sie die Zwischenablagedaten ab, und zeigen Sie sie im Fenster an. Wenn das Zwischenablageformat CF _ OWNERDISPLAY ist, senden Sie eine WM _ PAINTCLIPBOARD-Nachricht an den Besitzer der Zwischenablage.
- Senden Sie die Nachricht an das nächste Fenster in der Viewerkette der Zwischenablage.
Ein Beispiel für die Verarbeitung der WM _ DRAWCLIPBOARD-Nachricht finden Sie in der Beispielauflistung im Beispiel eines Zwischenablage-Viewers.
Beispiel für einen Zwischenablage-Viewer
Das folgende Beispiel zeigt eine einfache Anwendung für den Zwischenablage-Viewer.
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;
}