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
Nachricht - Kopieren von Informationen in die Zwischenablage
- Einfügen von Informationen aus der Zwischenablage
- Registrieren eines Zwischenablageformats
-
Verarbeiten der
WM_RENDERFORMAT
Nachrichten undWM_RENDERALLFORMATS
-
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 Zwischenablageanzeigefensters
- Hinzufügen eines Fensters zur Zwischenablageanzeigekette
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:
- Ö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 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 Bezeichnung darstellt. Die -Struktur mit dem Namen 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;
}
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:
- Öffnet die Zwischenablage durch Aufrufen der
OpenClipboard
Funktion. - Bestimmt, welche der verfügbaren Zwischenablageformate abgerufen werden sollen.
- 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 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 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 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 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 SetClipboardData
wird. 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 SetClipboardData
aufgerufen 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 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 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 eineSIZE
Struktur für das Zwischenablageanzeigefenster. - Verarbeiten sie die
WM_HSCROLLCLIPBOARD
Nachrichten undWM_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 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 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 - Entfernen eines Fensters aus der Zwischenablageanzeigekette
-
Verarbeiten der
WM_DRAWCLIPBOARD
Nachricht - Beispiel für eine Zwischenablageanzeige
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:
- 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 Zwischenablageformat lautet
CF_OWNERDISPLAY
, senden Sie eineWM_PAINTCLIPBOARD
Nachricht an den Besitzer der Zwischenablage. - 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;
}