Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
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 Nachricht
- Informationen in die Zwischenablage kopieren
- Einfügen von Informationen aus der Zwischenablage
- Registrieren eines Zwischenablageformats
- Verarbeiten der WM_RENDERFORMAT- und WM_RENDERALLFORMATS-Nachrichten
- Verarbeiten der WM_DESTROYCLIPBOARD Nachricht
- Verwenden Sie das "Owner-Display-Zwischenablageformat"
- Überwachen des Inhalts der Zwischenablage
- Abfrage der Sequenznummer der Zwischenablage
- Erstellen eines Listeners im Zwischenablageformat
- Erstellen Sie ein Zwischenablage-Anzeigefenster
- Hinzufügen eines Fensters zur Viewerkette der Zwischenablage
Implementieren der Befehle "Ausschneiden", "Kopieren" und "Einfügen"
In diesem Abschnitt wird beschrieben, wie standardmäßige Befehle "Ausschneiden", "Kopieren" und "Einfügen " in einer Anwendung implementiert werden. Das Beispiel in diesem Abschnitt verwendet diese Methoden zum Platzieren von Daten in der Zwischenablage mithilfe eines registrierten Zwischenablageformats, des CF_OWNERDISPLAY
Formats und des CF_TEXT
Formats. Das registrierte Format wird verwendet, um rechteckige oder elliptische Textfenster darzustellen, die als Labels 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 Akzeleratortabelle laden, die die Standardtasten für die Menübefehle Bearbeiten enthält. Die TranslateAccelerator-Funktion muss der Nachrichtenverarbeitungsschleife der Anwendung hinzugefügt werden, damit die Tastenkombinationen wirksam werden. Weitere Informationen zu Zugriffstasten finden Sie unter Zugriffstasten.
Verarbeiten der WM_INITMENUPOPUP Nachricht
Nicht alle Zwischenablagebefehle stehen dem Benutzer zu jeder beliebigen Zeit 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.
Im Folgenden sehen Sie den WM_INITMENUPOPUP Fall für eine Anwendung mit dem Namen "Label".
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
Die InitMenu
Funktion wird 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 zur Hauptfensterprozedur Ihrer Anwendung hinzu. Im Folgenden finden Sie den Fall für die WM_COMMAND
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;
Zum Ausführen der Befehle "Kopieren " und " Ausschneiden " ruft die Fensterprozedur die anwendungsdefinierte EditCopy
Funktion auf. Weitere Informationen finden Sie unter Kopieren von Informationen in die Zwischenablage. Zum Ausführen des Befehls "Einfügen " ruft die Fensterprozedur die anwendungsdefinierte EditPaste
Funktion auf. Weitere Informationen zur EditPaste
Funktion finden Sie unter Einfügen von Informationen aus der Zwischenablage.
Informationen in die Zwischenablage kopieren
In der Label-Anwendung kopiert die anwendungsdefinierte Funktion EditCopy die aktuelle Auswahl in die Zwischenablage. Diese Funktion führt folgende Aktionen aus:
- Öffnet die Zwischenablage durch Aufrufen der OpenClipboard-Funktion .
- Leert die Zwischenablage, indem die EmptyClipboard-Funktion aufgerufen wird.
- Ruft die SetClipboardData-Funktion einmal für jedes von der Anwendung bereitgestellte 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 kopiert eine anwendungsdefinierte Struktur, die eine gesamte Beschriftung darstellt. Die benannte LABELBOX
Struktur 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;
}
Informationen aus der Zwischenablage einfügen
In der Label-Anwendung fügt die anwendungsdefinierte EditPaste
Funktion den Inhalt der Zwischenablage ein. Diese Funktion führt folgende Aktionen 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 gehört weiterhin der Zwischenablage, sodass eine Anwendung es nicht freigeben oder im gesperrten Zustand lassen darf.
- Schließt die Zwischenablage durch Aufrufen der CloseClipboard-Funktion .
Wenn eine Beschriftung ausgewählt ist und eine Einfügemarke vorhanden ist, fügt die Funktion EditPaste den Text aus der Zwischenablage an der Einfügemarke ein. Wenn keine Auswahl vorhanden ist oder eine Beschriftung ausgewählt ist, erstellt die Funktion eine neue Bezeichnung, wobei die anwendungsdefinierte LABELBOX
Struktur in der Zwischenablage verwendet wird. Die LABELBOX
Struktur wird mithilfe eines registrierten Zwischenablageformats in die Zwischenablage eingefügt.
Die benannte LABELBOX
Struktur 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 Instanzinitialisierungsfunktion 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-Nachrichten
Wenn ein Fenster ein NULL
Handle an die SetClipboardData-Funktion übergibt, muss es die WM_RENDERFORMAT und WM_RENDERALLFORMATS Nachrichten verarbeiten, um Daten auf Anforderung zu rendern.
Wenn ein Fenster das Rendern eines bestimmten Formats verzögert und 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 beim Löschen des Fensters nicht mehr gerendert werden, wird eine WM_RENDERALLFORMATS Nachricht vor deren Zerstörung an das Fenster gesendet.
Zum Rendern eines Zwischenablageformats sollte die Fensterprozedur ein Nicht-NULL
-Daten-Handle auf die Zwischenablage mithilfe der SetClipboardData-Funktion platzieren. Wenn die Fensterprozedur als Reaktion auf die WM_RENDERFORMAT Nachricht ein Format rendert, darf sie die Zwischenablage vor dem Aufrufen SetClipboardData
nicht öffnen. Wenn es jedoch ein oder mehrere Formate als Reaktion auf die WM_RENDERALLFORMATS Nachricht rendert, muss sie die Zwischenablage öffnen und überprüfen, ob das Fenster noch die Zwischenablage besitzt, bevor sie aufgerufen SetClipboardData
wird, und sie muss die Zwischenablage schließen, bevor sie zurückgegeben wird.
Die Bezeichnungsanwendung verarbeitet die WM_RENDERFORMAT und WM_RENDERALLFORMATS Nachrichten 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 im folgenden Code definiert ist.
Die benannte LABELBOX
Struktur 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 freizugeben, die sie für die Unterstützung des verzögerten Renderings eingerichtet haben. Beispielsweise weist die Etikettenanwendung beim Kopieren eines Etiketts 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 Formats "Eigentümeranzeige-Zwischenablage"
Wenn ein Fenster Informationen in der Zwischenablage mithilfe des CF_OWNERDISPLAY
Zwischenablageformats platziert, muss folgendes ausgeführt werden:
- Verarbeiten der WM_PAINTCLIPBOARD Nachricht. Diese Nachricht wird an den Besitzer der Zwischenablage gesendet, wenn ein Teil des Zwischenablage-Viewer-Fensters neu gezeichnet werden muss.
- Verarbeiten der WM_SIZECLIPBOARD Nachricht. Diese Nachricht wird an den Besitzer der Zwischenablage gesendet, wenn die Größe des Anzeigefensters der Zwischenablage 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 Anzeigefenster der Zwischenablage festlegt. Als Reaktion auf diese Meldung aktualisiert die Label-Anwendung auch eine SIZE-Struktur für das Anzeigefenster der Zwischenablage.
- Verarbeiten Sie die WM_HSCROLLCLIPBOARD- und WM_VSCROLLCLIPBOARD-Nachrichten. Diese Nachrichten werden an den Besitzer der Zwischenablage gesendet, wenn im Zwischenablageanzeigefenster ein Scroll-Ereignis auftritt.
- Verarbeiten der WM_ASKCBFORMATNAME Nachricht. Das Anzeigefenster der Zwischenablage sendet diese Nachricht an eine Anwendung, um den Namen des Besitzeranzeigeformats abzurufen.
Wie folgt ist die Fensterprozedur für die Label-Anwendung definiert, um diese Nachrichten zu verarbeiten:
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;
}
Zwischenablageinhalte überwachen
Es gibt drei Möglichkeiten, Änderungen an der Zwischenablage zu überwachen. Die älteste Methode besteht darin, ein Zwischenablage-Anzeigefenster zu erstellen. Windows 2000 hat die Möglichkeit hinzugefügt, die Sequenznummer der Zwischenablage abzufragen, und Windows Vista hat Unterstützung für Listener im Zwischenablageformat hinzugefügt. Fenster der Zwischenablageanzeige werden aus Gründen der Abwärtskompatibilität mit früheren Versionen von Windows unterstützt. Neue Programme sollten Listener im Zwischenablageformat oder die Sequenznummer der Zwischenablage verwenden.
Abfrage der Sequenznummer der Zwischenablage
Jedes Mal, wenn sich der Inhalt der Zwischenablage ändert, wird ein 32-Bit-Wert, der als Sequenznummer der Zwischenablage bezeichnet wird, erhöht. Ein Programm kann die aktuelle Zwischenablage-Sequenznummer abrufen, indem es die GetClipboardSequenceNumber-Funktion aufruft. Durch den Vergleich des von einem aktuellen Aufruf von GetClipboardSequenceNumber
zurückgegebenen Werts mit dem eines vorherigen Aufrufs kann ein Programm feststellen, 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 Abfrageschleife verwendet werden sollte. Um benachrichtigt zu werden, wenn sich der Inhalt der Zwischenablage ändert, verwenden Sie einen Listener im Zwischenablageformat oder einen Zwischenablageanzeiger.
Erstellen eines Listeners für das Zwischenablageformat
Ein Listener im Zwischenablageformat ist ein Fenster, das registriert wurde, um benachrichtigt zu werden, wenn sich der Inhalt der Zwischenablage geändert hat. Es wird empfohlen, diese Methode anstelle des Erstellens eines Anzeigefensters für die Zwischenablage zu verwenden, da sie einfacher zu implementieren ist und Probleme vermeidet, falls Programme die Zwischenablageanzeigekette nicht ordnungsgemäß verwalten oder wenn ein Fenster in der Zwischenablageanzeigekette nicht mehr auf Nachrichten reagiert.
Ein Fenster registriert sich als Listener im Zwischenablageformat durch Aufrufen der AddClipboardFormatListener-Funktion . Wenn sich der Inhalt der Zwischenablage ändert, wird dem Fenster eine WM_CLIPBOARDUPDATE-Nachricht gesendet. Die Registrierung bleibt gültig, bis das Fenster sich selbst durch Aufrufen der RemoveClipboardFormatListener-Funktion entregistert .
Erstellen eines Anzeigefensters für die Zwischenablage
Ein Anzeigefenster der Zwischenablage zeigt den aktuellen Inhalt der Zwischenablage an und empfängt Nachrichten, wenn sich der Inhalt der Zwischenablage ändert. Zum Erstellen eines Zwischenablage-Anzeigefensters muss Ihre Anwendung folgende Schritte ausführen:
- Fügen Sie das Fenster zur Zuschauerkette der Zwischenablage hinzu.
- Verarbeiten der WM_CHANGECBCHAIN Nachricht.
- Verarbeiten der WM_DRAWCLIPBOARD Nachricht.
- Entfernen Sie das Fenster aus der Kette des Zwischenablage-Viewers, bevor es vernichtet wird.
Hinzufügen eines Fensters zur Anzeigekette der Zwischenablage
Ein Fenster fügt sich der Zwischenablage-Viewerkette 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
.
Das folgende Beispiel fügt der Zwischenablage-Viewerkette als Reaktion auf die WM_CREATE-Nachricht ein Fenster hinzu.
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
Codeausschnitte werden für die folgenden Aufgaben angezeigt:
- WM_CHANGECBCHAIN-Nachricht bearbeiten
- 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 Anzeigefenster der Zwischenablage empfängt die WM_CHANGECBCHAIN Nachricht, wenn sich ein anderes Fenster aus der Zwischenablageanzeigekette 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 aus der Kette aufheben. Andernfalls sollte diese Nachricht 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 Viewerkette der Zwischenablage
Um sich aus der Zwischenablage-Viewer-Kette zu entfernen, ruft ein Fenster die ChangeClipboardChain-Funktion auf. Im folgenden Beispiel wird als Reaktion auf die Nachricht WM_DESTROY ein Fenster aus der Zwischenablage-Viewer-Kette entfernt.
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
Verarbeiten der WM_DRAWCLIPBOARD Nachricht
Die WM_DRAWCLIPBOARD Meldung benachrichtigt ein Anzeigefenster der Zwischenablage, dass der Inhalt der Zwischenablage geändert wurde. Ein Fenster sollte beim Verarbeiten der WM_DRAWCLIPBOARD
Nachricht Folgendes ausführen:
- Bestimmen Sie, welche der verfügbaren Zwischenablageformate angezeigt werden sollen.
- Rufen Sie die Zwischenablagedaten ab, und zeigen Sie sie im Fenster an. Oder wenn das Format der Zwischenablage lautet
CF_OWNERDISPLAY
, senden Sie eine WM_PAINTCLIPBOARD Nachricht an den Besitzer der Zwischenablage. - Senden Sie die Nachricht an das nächste Fenster in der Zwischenablage-Viewer-Kette.
Ein Beispiel für die Verarbeitung der WM_DRAWCLIPBOARD-Nachricht finden Sie im Beispieleintrag im folgenden Abschnitt.
Beispiel für einen Zwischenablage-Viewer
Das folgende Beispiel zeigt eine vollständige, einfache Zwischenablageanzeige-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;
}