Compartir a través de


Uso del portapapeles

En esta sección se incluyen ejemplos de código para las siguientes tareas:

Implementación de los comandos Cortar, Copiar y Pegar

En esta sección se describe cómo se implementan los comandos Cut, Copy y Paste estándar en una aplicación. En el ejemplo de esta sección se usan estos métodos para colocar datos en el Portapapeles mediante un formato de Portapapeles registrado, los formatos CF_OWNERDISPLAY y CF_TEXT. El formato registrado se usa para representar ventanas de texto rectangulares o elípticas, denominadas etiquetas.

Selección de datos

Para poder copiar la información en el Portapapeles, el usuario debe seleccionar información específica para copiarla o cortarla. Una aplicación debe proporcionar un medio para que el usuario seleccione información dentro de un documento y algún tipo de comentarios visuales para indicar los datos seleccionados.

Crear un menú Editar

Una aplicación debe cargar una tabla de aceleradores que contenga los aceleradores de teclado estándar para los comandos del menú Editar . La función TranslateAccelerator debe agregarse al bucle de mensajes de la aplicación para que los aceleradores surtan efecto. Para obtener más información sobre los aceleradores de teclado, consulte Aceleradores de teclado.

Procesar el mensaje de WM_INITMENUPOPUP

No todos los comandos del Portapapeles están disponibles para el usuario en un momento dado. Una aplicación debe procesar el mensaje WM_INITMENUPOPUP para habilitar los elementos de menú para los comandos disponibles y deshabilitar los comandos no disponibles.

A continuación se muestra el WM_INITMENUPOPUP caso de una aplicación denominada Label.

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

La InitMenu función se define de la manera siguiente.

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

Procesar el mensaje de WM_COMMAND

Para procesar comandos de menú, agregue el WM_COMMAND caso al procedimiento de ventana principal de la aplicación. A continuación se muestra el WM_COMMAND caso del procedimiento de ventana de la aplicación 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; 

Para llevar a cabo los comandos Copiar y cortar , el procedimiento de ventana llama a la función definida por EditCopy la aplicación. Para obtener más información, vea Copiar información en el Portapapeles. Para llevar a cabo el comando Pegar, el procedimiento de ventana llama a la función definida por la aplicación EditPaste. Para obtener más información sobre la EditPaste función, vea Pegar información del Portapapeles.

Copiar información en el Portapapeles

En la aplicación Etiqueta, la función EditCopy definida por la aplicación copia la selección actual al portapapeles. Esta función hace lo siguiente:

  1. Abre el Portapapeles llamando a la función OpenClipboard .
  2. Vacía el Portapapeles llamando a la función EmptyClipboard .
  3. Llama a la función SetClipboardData una vez para cada formato del Portapapeles que proporciona la aplicación.
  4. Cierra el Portapapeles llamando a la función CloseClipboard .

Según la selección actual, la función EditCopy copia un intervalo de texto o copia una estructura definida por la aplicación que representa una etiqueta completa. La estructura, denominada LABELBOX, se define de la siguiente manera:

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

A continuación se muestra la EditCopy función :

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

Pegar información del Portapapeles

En la aplicación Label, la función EditPaste definida por la aplicación pega el contenido del portapapeles. Esta función hace lo siguiente:

  1. Abre el Portapapeles llamando a la función OpenClipboard .
  2. Determina cuál de los formatos del Portapapeles disponibles recuperar.
  3. Recupera el identificador de los datos en el formato seleccionado llamando a la función GetClipboardData .
  4. Inserta una copia de los datos en el documento. El identificador devuelto por GetClipboardData sigue siendo propiedad del Portapapeles, por lo que una aplicación no debe liberarla ni dejarla bloqueada.
  5. Cierra el Portapapeles llamando a la función CloseClipboard .

Si se selecciona una etiqueta y contiene un punto de inserción, la función EditPaste inserta el texto del Portapapeles en el punto de inserción. Si no hay ninguna selección o si se selecciona una etiqueta, la función crea una nueva etiqueta mediante la estructura definida por la aplicación en el Portapapeles. La estructura LABELBOX se coloca en el portapapeles utilizando un formato registrado del portapapeles.

La estructura, denominada LABELBOX, se define de la siguiente manera:

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

A continuación se muestra la EditPaste función :

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

Registrar un formato de portapapeles

Para registrar un formato de portapapeles, agregue una llamada a la función RegisterClipboardFormat en la función de inicialización de la instancia de su aplicación, como se indica a continuación:

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

Procesar los mensajes de WM_RENDERFORMAT y WM_RENDERALLFORMATS

Si una ventana pasa un NULL identificador a la función SetClipboardData, debe procesar los mensajes WM_RENDERFORMAT y WM_RENDERALLFORMATS para procesar los datos cuando se solicite.

Si una ventana retrasa la representación de un formato específico y, a continuación, otra aplicación solicita datos en ese formato, se envía un mensaje WM_RENDERFORMAT a la ventana. Además, si una ventana retrasa la representación de uno o varios formatos, y si algunos de esos formatos permanecen sin representar cuando la ventana está a punto de destruirse, se envía un mensaje de WM_RENDERALLFORMATS a la ventana antes de su destrucción.

Para representar un formato de portapapeles, el procedimiento de ventana debe colocar un identificador de datos noNULL en el portapapeles mediante la función SetClipboardData. Si el procedimiento de ventana está procesando un formato en respuesta al mensaje WM_RENDERFORMAT, no debe abrir el Portapapeles antes de llamar a SetClipboardData. Pero si representa uno o varios formatos en respuesta al WM_RENDERALLFORMATS mensaje, debe abrir el Portapapeles y comprobar que la ventana sigue siendo propietaria del Portapapeles antes de llamar a SetClipboardData y debe cerrar el Portapapeles antes de volver.

La aplicación Label procesa los mensajes WM_RENDERFORMAT y WM_RENDERALLFORMATS de la siguiente manera:

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

En ambos casos, el procedimiento de ventana llama a la función definida por la aplicación, que está definida en el código que sigue.

La estructura, denominada LABELBOX, se define de la siguiente manera:

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

Procesar el mensaje de WM_DESTROYCLIPBOARD

Una ventana puede procesar el mensaje WM_DESTROYCLIPBOARD para liberar los recursos que ha reservado para admitir la renderización diferida. Por ejemplo, la aplicación Etiqueta, al copiar una etiqueta en el Portapapeles, asigna un objeto de memoria local. A continuación, libera este objeto en respuesta al WM_DESTROYCLIPBOARD mensaje, como se indica a continuación:

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

Formato de visualización del propietario del portapapeles

Si una ventana coloca información en el Portapapeles mediante el formato del CF_OWNERDISPLAY Portapapeles, debe hacer lo siguiente:

  • Procese el mensaje de WM_PAINTCLIPBOARD . Este mensaje se envía al propietario del Portapapeles cuando se debe volver a dibujar una parte de la ventana del visor del Portapapeles.
  • Procese el mensaje WM_SIZECLIPBOARD. Este mensaje se envía al propietario del Portapapeles cuando se ha cambiado el tamaño de la ventana del visor del Portapapeles o su contenido ha cambiado. Normalmente, una ventana responde a este mensaje estableciendo las posiciones y rangos de desplazamiento de la ventana del visor del Portapapeles. En respuesta a este mensaje, la aplicación Etiqueta también actualiza una estructura SIZE para la ventana del visor del Portapapeles.
  • Procese los mensajes WM_HSCROLLCLIPBOARD y WM_VSCROLLCLIPBOARD . Estos mensajes se envían al propietario del Portapapeles cuando se produce un evento de barra de desplazamiento en la ventana del visor del Portapapeles.
  • Procese el mensaje WM_ASKCBFORMATNAME . La ventana del visor del Portapapeles envía este mensaje a una aplicación para recuperar el nombre del formato de presentación del propietario.

El procedimiento de ventana para la aplicación Label procesa estos mensajes se define de la siguiente manera:

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

Supervisión del contenido del Portapapeles

Hay tres maneras de supervisar los cambios en el Portapapeles. El método más antiguo consiste en crear una ventana del visor del Portapapeles. Windows 2000 agregó la capacidad de consultar el número de secuencia del Portapapeles y Windows Vista agregó compatibilidad con los agentes de escucha de formato del Portapapeles. Las ventanas del visor del Portapapeles son compatibles para asegurar la compatibilidad retroactiva con versiones anteriores de Windows. Los nuevos programas deben usar escuchadores de formato del portapapeles o el número de secuencia del portapapeles.

Consultar el número de secuencia del portapapeles

Cada vez que cambia el contenido del Portapapeles, se incrementa un valor de 32 bits conocido como número de secuencia del Portapapeles. Un programa puede recuperar el número de secuencia actual del Portapapeles llamando a la función GetClipboardSequenceNumber . Al comparar el valor devuelto con un valor devuelto por una llamada anterior a GetClipboardSequenceNumber, un programa puede determinar si el contenido del Portapapeles ha cambiado. Este método es más adecuado para programas que almacenan en caché los resultados en función del contenido actual del Portapapeles y deben saber si los cálculos siguen siendo válidos antes de usar los resultados de esa memoria caché. Tenga en cuenta que no es un método de notificación y no debe usarse en un bucle de sondeo. Para recibir una notificación cuando cambie el contenido del portapapeles, use un oyente de formato de portapapeles o un visor de portapapeles.

Crear un agente de escucha con formato de Portapapeles

Un monitor de cambios en el formato del portapapeles es una ventana que se ha registrado para recibir una notificación cuando el contenido del portapapeles cambia. Este método se recomienda sobre la creación de una ventana del visor del Portapapeles porque es más fácil de implementar y evita problemas si los programas no mantienen correctamente la cadena del visor del Portapapeles o si una ventana de la cadena del visor del Portapapeles deja de responder a los mensajes.

Una ventana se registra como agente de escucha de formato de Portapapeles llamando a la función AddClipboardFormatListener . Cuando el contenido del Portapapeles cambia, se envía a la ventana un mensaje WM_CLIPBOARDUPDATE. El registro sigue siendo válido hasta que la ventana se anule el registro mediante una llamada a la función RemoveClipboardFormatListener .

Crear una ventana del visor del portapapeles

Una ventana del visor del Portapapeles muestra el contenido actual del Portapapeles y recibe mensajes cuando cambia el contenido del Portapapeles. Para crear una ventana del visor del Portapapeles, la aplicación debe hacer lo siguiente:

  • Agregue la ventana a la cadena de visores del Portapapeles.
  • Procese el mensaje WM_CHANGECBCHAIN.
  • Procese el mensaje de WM_DRAWCLIPBOARD .
  • Quite la ventana de la cadena del visor del portapapeles antes de ser destruida.

Agregar una ventana a la cadena del visor del Portapapeles

Una ventana se agrega a la cadena del visor del Portapapeles llamando a la función SetClipboardViewer . El valor devuelto es el identificador de la siguiente ventana de la cadena. Una ventana debe realizar un seguimiento de este valor; por ejemplo, guardándola en una variable estática denominada hwndNextViewer.

En el ejemplo siguiente se agrega una ventana a la cadena del visor del Portapapeles en respuesta al mensaje WM_CREATE .

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

Los fragmentos de código se muestran para las siguientes tareas:

Procesar el mensaje de WM_CHANGECBCHAIN

Una ventana del visor del Portapapeles recibe el mensaje WM_CHANGECBCHAIN cuando otra ventana se elimina de la cadena de visores del Portapapeles. Si la ventana que se va a quitar es la siguiente ventana de la cadena, la ventana que recibe el mensaje debe desvincular la siguiente ventana de la cadena. De lo contrario, este mensaje debe pasarse a la siguiente ventana de la cadena.

En el ejemplo siguiente se muestra el procesamiento del mensaje de WM_CHANGECBCHAIN .

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;

Quitar una ventana de la cadena de visor del portapapeles

Para quitarse de la cadena del visualizador del portapapeles, una ventana llama a la función ChangeClipboardChain. En el ejemplo siguiente se quita una ventana de la cadena del visor del Portapapeles en respuesta al mensaje WM_DESTROY .

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

Procesar el mensaje de WM_DRAWCLIPBOARD

El WM_DRAWCLIPBOARD mensaje notifica a una ventana del visor del Portapapeles que ha cambiado el contenido del Portapapeles. Una ventana debe hacer lo siguiente al procesar el WM_DRAWCLIPBOARD mensaje:

  1. Determinar cuál de los formatos del portapapeles disponibles se va a mostrar.
  2. Recupere los datos del Portapapeles y muestranlos en la ventana. O bien, si el formato del Portapapeles es CF_OWNERDISPLAY, envíe un mensaje WM_PAINTCLIPBOARD al propietario del Portapapeles.
  3. Envíe el mensaje a la siguiente ventana de la cadena del visor del portapapeles.

Para obtener un ejemplo de procesamiento del mensaje de WM_DRAWCLIPBOARD , vea la lista de ejemplo en la sección siguiente.

Ejemplo de un visor del Portapapeles

En el ejemplo siguiente se muestra una aplicación completa y sencilla del visor del Portapapeles:

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

aceleradores de teclado de