Uso degli Appunti

In questa sezione sono disponibili esempi di codice per le attività seguenti:

Implementazione dei comandi Taglia, Copia e Incolla

In questa sezione viene descritto il modo in cui i comandi Standard Taglia, Copia e Incolla vengono implementati in un'applicazione. L'esempio in questa sezione usa questi metodi per inserire i dati negli Appunti usando un formato registrato degli Appunti, il CF_OWNERDISPLAY formato e il CF_TEXT formato. Il formato registrato viene usato per rappresentare finestre di testo rettangolari o ellittiche, denominate etichette.

Selezione dei dati

Prima che le informazioni possano essere copiate negli Appunti, l'utente deve selezionare informazioni specifiche da copiare o tagliare. Un'applicazione deve fornire un mezzo per consentire all'utente di selezionare le informazioni all'interno di un documento e un qualche tipo di feedback visivo per indicare i dati selezionati.

Creazione di un menu Modifica

Un'applicazione deve caricare una tabella di tasti di scelta rapida contenente gli acceleratori di tastiera standard per i comandi di menu Modifica . La TranslateAccelerator funzione deve essere aggiunta al ciclo di messaggi dell'applicazione per rendere effettive le operazioni degli acceleratori. Per altre informazioni sui tasti di scelta rapida, vedere Tasti di scelta rapida.

Elaborazione del WM_INITMENUPOPUP messaggio

Non tutti i comandi degli Appunti sono disponibili per l'utente in un determinato momento. Un'applicazione deve elaborare il WM_INITMENUPOPUP messaggio per abilitare le voci di menu per i comandi disponibili e disabilitare i comandi non disponibili.

Di seguito è riportato il WM_INITMENUPOPUP caso di un'applicazione denominata Label.

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

La InitMenu funzione è definita come segue.

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

Elaborazione del WM_COMMAND messaggio

Per elaborare i comandi di menu, aggiungere il WM_COMMAND caso alla routine della finestra principale dell'applicazione. Di seguito è riportato il WM_COMMAND caso della procedura finestra dell'applicazione Label.

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; 

Per eseguire i comandi Copia e Taglia , la routine della finestra chiama la funzione definita dall'applicazione EditCopy . Per altre informazioni, vedere Copia di informazioni negli Appunti. Per eseguire il comando Incolla , la routine della finestra chiama la funzione definita dall'applicazione EditPaste . Per altre informazioni sulla EditPaste funzione, vedere Incolla di informazioni negli Appunti.

Copia delle informazioni negli Appunti

Nell'applicazione Label la funzione EditCopy definita dall'applicazione copia la selezione corrente negli Appunti. Questa funzione esegue le operazioni seguenti:

  1. Apre gli Appunti chiamando la OpenClipboard funzione .
  2. Svuota gli Appunti chiamando la EmptyClipboard funzione .
  3. Chiama la SetClipboardData funzione una volta per ogni formato degli Appunti fornito dall'applicazione.
  4. Chiude gli Appunti chiamando la CloseClipboard funzione .

A seconda della selezione corrente, la funzione EditCopy copia un intervallo di testo o copia una struttura definita dall'applicazione che rappresenta un'intera etichetta. La struttura, denominata LABELBOX, è definita come segue.

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

Di seguito è riportata la EditCopy funzione .

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

Incolla delle informazioni dagli Appunti

Nell'applicazione Etichetta la funzione definita dall'applicazione EditPaste incolla il contenuto degli Appunti. Questa funzione esegue le operazioni seguenti:

  1. Apre gli Appunti chiamando la OpenClipboard funzione .
  2. Determina quale dei formati degli Appunti disponibili da recuperare.
  3. Recupera l'handle per i dati nel formato selezionato chiamando la GetClipboardData funzione .
  4. Inserisce una copia dei dati nel documento. L'handle restituito dagli GetClipboardData Appunti è ancora di proprietà degli Appunti, pertanto un'applicazione non deve liberarla o lasciarla bloccata.
  5. Chiude gli Appunti chiamando la CloseClipboard funzione .

Se un'etichetta è selezionata e contiene un punto di inserimento, la funzione EditPaste inserisce il testo dagli Appunti nel punto di inserimento. Se non è selezionata alcuna selezione o se è selezionata un'etichetta, la funzione crea una nuova etichetta usando la struttura definita dall'applicazione LABELBOX negli Appunti. La LABELBOX struttura viene posizionata negli Appunti utilizzando un formato registrato negli Appunti.

La struttura, denominata LABELBOX, è definita come segue.

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

Di seguito è riportata la EditPaste funzione .

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

Registrazione di un formato appunti

Per registrare un formato degli Appunti, aggiungere una chiamata alla RegisterClipboardFormat funzione di inizializzazione dell'istanza dell'applicazione, come indicato di seguito.

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

Elaborazione dei WM_RENDERFORMAT messaggi e WM_RENDERALLFORMATS

Se una finestra passa un NULL handle alla SetClipboardData funzione, deve elaborare i WM_RENDERFORMAT messaggi e WM_RENDERALLFORMATS per il rendering dei dati su richiesta.

Se una finestra ritarda il rendering di un formato specifico e un'altra applicazione richiede dati in tale formato, viene inviato un WM_RENDERFORMAT messaggio alla finestra. Inoltre, se una finestra ritarda il rendering di uno o più formati e se alcuni di questi formati rimangono annullati quando la finestra sta per essere distrutta, un WM_RENDERALLFORMATS messaggio viene inviato alla finestra prima della sua distruzione.

Per eseguire il rendering di un formato degli Appunti, la routine della finestra deve posizionare un handle di dati nonNULL negli Appunti usando la SetClipboardData funzione . Se la routine della finestra esegue il rendering di un formato in risposta al WM_RENDERFORMAT messaggio, non deve aprire gli Appunti prima di chiamare SetClipboardData. Tuttavia, se esegue il rendering di uno o più formati in risposta al WM_RENDERALLFORMATS messaggio, è necessario aprire gli Appunti e verificare che la finestra sia ancora proprietaria degli Appunti prima di chiamare SetClipboardDatae chiudere gli Appunti prima di tornare.

L'applicazione Etichetta elabora i WM_RENDERFORMAT messaggi e WM_RENDERALLFORMATS come indicato di seguito.

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

In entrambi i casi, la routine window chiama la funzione definita dall'applicazione RenderFormat , definita come segue.

La struttura, denominata LABELBOX, è definita come segue.

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

Elaborazione del WM_DESTROYCLIPBOARD messaggio

Una finestra può elaborare il WM_DESTROYCLIPBOARD messaggio per liberare tutte le risorse che ha messo da parte per supportare il rendering ritardato. Ad esempio, l'applicazione Label, quando si copia un'etichetta negli Appunti, alloca un oggetto memoria locale. Libera quindi questo oggetto in risposta al WM_DESTROYCLIPBOARD messaggio, come indicato di seguito.

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

Uso del formato Degli Appunti proprietario

Se una finestra inserisce informazioni negli Appunti usando il CF_OWNERDISPLAY formato degli Appunti, deve eseguire le operazioni seguenti:

  • Elaborare il WM_PAINTCLIPBOARD messaggio. Questo messaggio viene inviato al proprietario degli Appunti quando è necessario riavviare una parte della finestra del visualizzatore appunti.
  • Elaborare il WM_SIZECLIPBOARD messaggio. Questo messaggio viene inviato al proprietario degli Appunti quando la finestra del visualizzatore Appunti è stata ridimensionata o il relativo contenuto è stato modificato. In genere, una finestra risponde a questo messaggio impostando le posizioni e gli intervalli di scorrimento per la finestra del visualizzatore appunti. In risposta a questo messaggio, l'applicazione Etichetta aggiorna anche una SIZE struttura per la finestra del visualizzatore degli Appunti.
  • Elaborare i WM_HSCROLLCLIPBOARD messaggi e WM_VSCROLLCLIPBOARD . Questi messaggi vengono inviati al proprietario degli Appunti quando si verifica un evento della barra di scorrimento nella finestra del visualizzatore appunti.
  • Elaborare il WM_ASKCBFORMATNAME messaggio. La finestra del visualizzatore Appunti invia questo messaggio a un'applicazione per recuperare il nome del formato di visualizzazione proprietario.

La procedura della finestra per l'applicazione Etichetta elabora questi messaggi, come indicato di seguito.

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

Monitoraggio del contenuto degli Appunti

Esistono tre modi per monitorare le modifiche apportate agli Appunti. Il metodo meno recente consiste nel creare una finestra del visualizzatore degli Appunti. Windows 2000 ha aggiunto la possibilità di eseguire query sul numero di sequenza degli Appunti e Windows Vista ha aggiunto il supporto per i listener di formato Appunti. Le finestre del visualizzatore Appunti sono supportate per la compatibilità con le versioni precedenti di Windows. I nuovi programmi devono usare listener in formato Appunti o il numero di sequenza degli Appunti.

Esecuzione di query sul numero di sequenza degli Appunti

Ogni volta che il contenuto degli Appunti cambia, viene incrementato un valore a 32 bit noto come numero di sequenza degli Appunti. Un programma può recuperare il numero di sequenza degli Appunti corrente chiamando la GetClipboardSequenceNumber funzione . Confrontando il valore restituito con un valore restituito da una chiamata precedente a GetClipboardSequenceNumber, un programma può determinare se il contenuto degli Appunti è stato modificato. Questo metodo è più adatto ai programmi che memorizzano nella cache i risultati in base al contenuto corrente degli Appunti e devono sapere se i calcoli sono ancora validi prima di usare i risultati di tale cache. Si noti che non si tratta di un metodo di notifica e non deve essere usato in un ciclo di polling. Per ricevere una notifica quando il contenuto degli Appunti cambia, usare un listener in formato Appunti o un visualizzatore appunti.

Creazione di un listener in formato Appunti

Un listener di formato Appunti è una finestra che ha registrato per ricevere una notifica quando il contenuto degli Appunti è stato modificato. Questo metodo è consigliato per la creazione di una finestra del visualizzatore Appunti perché è più semplice implementare ed evitare problemi se i programmi non riescono a mantenere correttamente la catena del visualizzatore degli Appunti o se una finestra nella catena del visualizzatore degli Appunti smette di rispondere ai messaggi.

Una finestra viene registrata come listener in formato Appunti chiamando la AddClipboardFormatListener funzione . Quando il contenuto degli Appunti cambia, la finestra viene inviata un WM_CLIPBOARDUPDATE messaggio. La registrazione rimane valida fino a quando la finestra non annulla la registrazione chiamando la RemoveClipboardFormatListener funzione .

Creazione di una finestra visualizzatore Appunti

Una finestra del visualizzatore appunti visualizza il contenuto corrente degli Appunti e riceve messaggi quando cambia il contenuto degli Appunti. Per creare una finestra del visualizzatore Appunti, l'applicazione deve eseguire le operazioni seguenti:

  • Aggiungere la finestra alla catena del visualizzatore degli Appunti.
  • Elaborare il WM_CHANGECBCHAIN messaggio.
  • Elaborare il WM_DRAWCLIPBOARD messaggio.
  • Rimuovere la finestra dalla catena del visualizzatore degli Appunti prima che venga eliminata definitivamente.

Aggiunta di una finestra alla catena del visualizzatore Appunti

Una finestra si aggiunge alla catena di visualizzatore degli Appunti chiamando la SetClipboardViewer funzione . Il valore restituito è l'handle della finestra successiva nella catena. Una finestra deve tenere traccia di questo valore, ad esempio salvandolo in una variabile statica denominata hwndNextViewer.

Nell'esempio seguente viene aggiunta una finestra alla catena del visualizzatore degli Appunti in risposta al WM_CREATE messaggio.

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

I frammenti di codice vengono visualizzati per le attività seguenti:

Elaborazione del WM_CHANGECBCHAIN messaggio

Una finestra del visualizzatore Appunti riceve il WM_CHANGECBCHAIN messaggio quando un'altra finestra viene rimossa dalla catena del visualizzatore degli Appunti. Se la finestra da rimuovere è la finestra successiva della catena, la finestra che riceve il messaggio deve scollegare la finestra successiva dalla catena. In caso contrario, questo messaggio deve essere passato alla finestra successiva nella catena.

Nell'esempio seguente viene illustrata l'elaborazione del WM_CHANGECBCHAIN messaggio.

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;

Rimozione di una finestra dalla catena del visualizzatore Appunti

Per rimuovere se stesso dalla catena del visualizzatore degli Appunti, una finestra chiama la ChangeClipboardChain funzione . Nell'esempio seguente viene rimossa una finestra dalla catena del visualizzatore degli Appunti in risposta al WM_DESTROY messaggio.

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

Elaborazione del WM_DRAWCLIPBOARD messaggio

Il WM_DRAWCLIPBOARD messaggio notifica a una finestra del visualizzatore Appunti che il contenuto degli Appunti è stato modificato. Una finestra deve eseguire le operazioni seguenti durante l'elaborazione del WM_DRAWCLIPBOARD messaggio:

  1. Determinare quali formati degli Appunti disponibili visualizzare.
  2. Recuperare i dati degli Appunti e visualizzarli nella finestra. In alternativa, se il formato degli Appunti è CF_OWNERDISPLAY, inviare un WM_PAINTCLIPBOARD messaggio al proprietario degli Appunti.
  3. Inviare il messaggio alla finestra successiva nella catena del visualizzatore appunti.

Per un esempio di elaborazione del WM_DRAWCLIPBOARD messaggio, vedere l'elenco di esempio in Esempio di visualizzatore Appunti.

Esempio di visualizzatore Appunti

Nell'esempio seguente viene illustrata una semplice applicazione visualizzatore appunti.

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