Operaciones del Portapapeles
Una ventana debe usar el Portapapeles al cortar, copiar o pegar datos. Una ventana coloca los datos en el Portapapeles para las operaciones de cortar y copiar y recupera datos del Portapapeles para las operaciones de pegado. En las siguientes secciones se describen estas operaciones y problemas relacionados.
Para colocar o recuperar datos del Portapapeles, una ventana debe abrir primero el Portapapeles mediante la función OpenClipboard. Solo una ventana puede abrir el Portapapeles a la vez. Para averiguar qué ventana abre el Portapapeles, llame a la función GetOpenClipboardWindow. Cuando haya terminado, la ventana debe cerrar el Portapapeles llamando a la función CloseClipboard.
En esta sección se explican los temas siguientes:
- Operaciones de cortar y copiar
- Operaciones de pegado
- Propiedad del Portapapeles
- Representación diferida
- Memoria y Portapapeles
Operaciones de cortar y copiar
Para colocar información en el Portapapeles, una ventana borra primero cualquier contenido anterior del Portapapeles mediante la función EmptyClipboard. Esta función envía el mensjae WM_DESTROYCLIPBOARD al propietario anterior del Portapapeles, libera los recursos asociados a los datos del Portapapeles y asigna la propiedad del Portapapeles a la ventana que lo tiene abierto. Para averiguar qué ventana posee el Portapapeles, llame a la función GetClipboardOwner.
Después de vaciar el Portapapeles, la ventana coloca los datos allí en tantos formatos de Portapapeles como sea posible, ordenados del formato más descriptivo al menos descriptivo. Para cada formato, la ventana llama a la función SetClipboardData, especificando el identificador de formato y un identificador de memoria global. El identificador de memoria puede ser un valor NULL, lo que indica que la ventana representa los datos a petición. Para más información, consulte Representación diferida.
Operaciones de pegado
Para recuperar la información de pegado del Portapapeles, una ventana determina primero el formato del Portapapeles que se va a recuperar. Normalmente, una ventana enumera los formatos disponibles del Portapapeles mediante la función EnumClipboardFormats y usa el primer formato que reconoce. Este método selecciona el mejor formato disponible según el conjunto de prioridades cuando los datos se colocaron en el Portapapeles.
Como alternativa, una ventana puede usar la función GetPriorityClipboardFormat. Esta función identifica el mejor formato de Portapapeles disponible según una prioridad especificada. Una ventana que reconozca solo un formato de Portapapeles puede simplemente determinar si ese formato está disponible mediante la función IsClipboardFormatAvailable.
Después de determinar el formato del Portapapeles que se va a usar, una ventana llama a la función GetClipboardData. Esta función devuelve el identificador a un objeto de memoria global que contiene datos en el formato especificado. Una ventana puede bloquear brevemente el objeto de memoria para examinar o copiar los datos. Sin embargo, una ventana no debe liberar el objeto ni dejarlo bloqueado durante un largo período de tiempo.
Propiedad del Portapapeles
El propietario del Portapapeles es la ventana asociada a la información del Portapapeles. Una ventana se convierte en propietario cuando coloca datos en el Portapapeles, en concreto, cuando llama a la función EmptyClipboard. La ventana permanece el propietario del Portapapeles hasta que se cierra u otra ventana vacía el Portapapeles.
Cuando se vacía el Portapapeles, el propietario del Portapapeles recibe un mensaje WM_DESTROYCLIPBOARD. Estos son algunos de los motivos por los que una ventana puede procesar este mensaje:
- La representación diferida de la ventana de uno o varios formatos del Portapapeles. En respuesta al mensaje WM_DESTROYCLIPBOARD, la ventana podría liberar recursos que había asignado para representar datos a petición. Para más información sobre la representación de datos, consulte Representación diferida.
- La ventana coloca los datos en el Portapapeles en un formato privado. El sistema no libera los datos de los formatos privados del Portapapeles cuando se vacía. Por lo tanto, el propietario del Portapapeles debe liberar los datos al recibir el mensaje WM_DESTROYCLIPBOARD. Para más información sobre los formatos privados del Portapapeles, consulte Formatos de Portapapeles.
- La ventana coloca los datos en el Portapapeles con el formato CF_OWNERDISPLAY. En respuesta al mensaje WM_DESTROYCLIPBOARD, la ventana podría liberar recursos que había usado para mostrar información en la ventana del visor del Portapapeles. Para más información sobre este formato alternativo, consulte Formato de presentación del propietario.
Representación diferida
Al colocar un formato del Portapapeles en el Portapapeles, una ventana puede diferir la representación de los datos en ese formato hasta que se necesiten los datos. Para ello, la aplicación puede especificar un valor NULL para el parámetro hData de la función SetClipboardData. Esto resulta útil si la aplicación admite varios formatos del Portapapeles, algunos de los cuales, o todos ellos, consumen mucho tiempo en la representación. Al pasar un identificador NULL, una ventana representa formatos complejos del Portapapeles solo cuando y si son necesarios.
Si una ventana retrasa la representación de un formato del Portapapeles, debe estar preparada para representar el formato a petición siempre que sea el propietario del Portapapeles. El sistema envía al propietario del Portapapeles un mensaje WM_RENDERFORMAT cuando se recibe una solicitud para un formato específico que no se ha representado. Al recibir este mensaje, la ventana debe llamar a la función SetClipboardData para colocar un identificador de memoria global en el Portapapeles en el formato solicitado.
Una aplicación no debe abrir el Portapapeles antes de llamar a SetClipboardData en respuesta al mensaje WM_RENDERFORMAT. No es necesario abrir el Portapapeles y se producirá un error en cualquier intento de hacerlo, ya que la aplicación que solicitó que se represente el formato se mantiene abierta actualmente.
Si el propietario del Portapapeles está a punto de destruirse y ha diferido la representación de algunos o todos los formatos del Portapapeles, recibe el mensaje WM_RENDERALLFORMATS. Al recibir este mensaje, la ventana debe abrir el Portapapeles, comprobar que sigue siendo el propietario del Portapapeles con la función GetClipboardOwner y, a continuación, colocar controladores de memoria válidos en el Portapapeles para todos los formatos que proporciona. Esto garantiza que estos formatos permanezcan disponibles después de que se destruya el propietario del Portapapeles.
A diferencia de WM_RENDERFORMAT, una aplicación que responde a WM_RENDERALLFORMATS debe abrir el Portapapeles antes de llamar a SetClipboardData para colocar los identificadores de memoria globales en el Portapapeles.
Los formatos del Portapapeles que no se representan en respuesta al mensaje WM_RENDERALLFORMATS dejan de estar disponibles para otras aplicaciones y ya no se enumeran mediante las funciones del Portapapeles.
Guía de representación diferida
La representación diferida es una característica de rendimiento, lo que permite a una aplicación evitar el trabajo para representar datos del Portapapeles en un formato que nunca se puede solicitar. Sin embargo, el uso de la representación diferida implica los siguientes inconvenientes que se deben tener en cuenta:
- El uso de la representación diferida agrega cierta complejidad a la aplicación, lo que requiere que controle dos mensajes de ventana de representación, como se ha descrito anteriormente.
- El uso de la representación diferida significa que la aplicación pierde la opción de mantener la capacidad de respuesta de la interfaz de usuario si la representación de los datos tarda lo suficiente para que lo note el usuario. Con la representación diferida, si los datos finalmente son necesarios, la ventana debe representar los datos mientras se procesa un mensaje de la ventana de representación, como se ha descrito anteriormente. Como resultado, si los datos tardan mucho tiempo en representarse, la aplicación puede dejar de responder visiblemente mientras se produce la representación, ya que no se pueden procesar otros mensajes de ventana mientras se procesa el mensaje de la ventana de representación. Una aplicación que no usa la representación diferida podría optar por representar datos en un subproceso en segundo plano para mantener la respuesta de la interfaz de usuario mientras se produce la representación, quizás proporcionando opciones de progreso o cancelación, que no están disponibles al usar la representación diferida.
- El uso de la representación diferida agrega una pequeña cantidad de sobrecarga si los datos finalmente son necesarios. Al usar la representación diferida, una ventana llama inicialmente a la función SetClipboardData con un identificador NULL y, si los datos son necesarios más adelante, la ventana debe responder a un mensaje de ventana y llamar a la función SetClipboardData una segunda vez con un identificador para los datos representados, como se describió anteriormente. Como resultado, si los datos finalmente son necesarios, el uso de la representación diferida agrega el costo de procesar un mensaje de ventana y llamar a la función SetClipboardData una segunda vez. Este costo es pequeño pero no cero. Si una aplicación solo admite un único formato de Portapapeles y, si los datos siempre se solicitan, el uso de la representación diferida solo agrega esta pequeña cantidad de sobrecarga (el costo varía según el hardware; una estimación está entre 10 y 100 microsegundos). Sin embargo, si los datos son pequeños, la sobrecarga del uso de la representación diferida puede superar el costo de representar los datos, lo que podría derrotar el propósito de usar la representación diferida para mejorar el rendimiento. (En las pruebas, para los datos que ya están en su forma final, la sobrecarga de usar la representación diferida superó sistemáticamente el costo de copiar los datos en el Portapapeles si los datos eran de 100 KiB o menos. Esta prueba no incluye el costo de representar datos, solo para copiarlos una vez que se represente).
- La representación diferida es una ventaja de rendimiento neto si ahorra más tiempo de lo que agrega en sobrecarga. Para determinar la sobrecarga de la representación diferida, la medición es mejor, pero de 10 a 100 microsegundos es una estimación. Para calcular el ahorro de uso de la representación diferida para cada formato del Portapapeles, mida el costo de representar los datos en ese formato y determine con qué frecuencia se solicita ese formato (en función de los mensajes de ventana descritos anteriormente). Multiplique el costo de representar los datos por el porcentaje de tiempo que los datos no se solicitan finalmente (antes de vaciar el Portapapeles o su cambio de contenido) para determinar el ahorro de representación diferida para cada formato del Portapapeles. La representación diferida es una ventaja de rendimiento neto si el ahorro supera el costo de sobrecarga.
- Como norma concreta, para las aplicaciones que solo admiten un solo formato de Portapapeles, como texto, donde los datos no son significativamente costosos de representar, considere la posibilidad de colocar los datos directamente en el Portapapeles si el tamaño de los datos es de 4 KiB o menos.
Memoria y portapapeles
Se debe asignar un objeto de memoria que se va a colocar en el Portapapeles mediante la función GlobalAlloc con la marca GMEM_MOVEABLE.
Después de colocar un objeto de memoria en el Portapapeles, la propiedad de ese identificador de memoria se transfiere al sistema. Cuando se vacía el Portapapeles y el objeto de memoria tiene uno de los siguientes formatos, el sistema libera el objeto de memoria llamando a la función especificada:
Función para liberar objetos | Formato de Portapapeles incorrecto. |
---|---|
DeleteMetaFile |
CF_DSPENHMETAFILE CF_DSPMETAFILEPICT CF_ENHMETAFILE CF_METAFILEPICT |
DeleteObject |
CF_BITMAP CF_DSPBITMAP CF_PALETTE |
GlobalFree |
CF_DIB CF_DIBV5 CF_DSPTEXT CF_OEMTEXT CF_TEXT CF_UNICODETEXT |
None |
CF_OWNERDISPLAY Cuando el Portapapeles está vacío de un objeto CF_OWNERDISPLAY, la propia aplicación debe liberar el objeto de memoria. |