Compartir a través de


Control de escenarios de transferencia de datos de shell

El documento Objeto de datos de Shell explicó el enfoque general que se usa para transferir datos de Shell con arrastrar y colocar o el Portapapeles. Sin embargo, para implementar la transferencia de datos de Shell en la aplicación, también debe comprender cómo aplicar estos principios y técnicas generales a la variedad de formas en que se pueden transferir datos de Shell. En este documento se presentan escenarios comunes de transferencia de datos de Shell y se describe cómo implementar cada uno de ellos en la aplicación.

Nota:

Aunque cada uno de estos escenarios describe una operación de transferencia de datos específica, muchas de ellas se aplican a una variedad de escenarios relacionados. Por ejemplo, la diferencia principal entre la mayoría del Portapapeles y las transferencias de arrastrar y colocar es en cómo llega el objeto de datos al destino. Una vez que el destino tiene un puntero a la interfaz IDataObject del objeto de datos, los procedimientos para extraer información son en gran medida los mismos para ambos tipos de transferencia de datos. Sin embargo, algunos de los escenarios se limitan a un tipo específico de operación. Consulte el escenario individual para obtener más información.

 

Instrucciones generales

Cada una de las secciones siguientes describe un único escenario de transferencia de datos bastante específico. Sin embargo, las transferencias de datos suelen ser más complejas y pueden implicar aspectos de varios escenarios. Normalmente no sabe, con antelación, qué escenario necesitará controlar. Estas son algunas directrices generales que debe tener en cuenta.

Para orígenes de datos:

  • Los formatos del Portapapeles de Shell, a excepción de CF_HDROP, no están predefinidos. Cada formato que quiera usar debe registrarse llamando a RegisterClipboardFormat.
  • Los formatos de los objetos de datos se proporcionan en el orden de preferencia del origen. Enumere el objeto de datos y elija el primero que puede consumir.
  • Incluya tantos formatos como pueda admitir. Por lo general, no sabe dónde se quitará el objeto de datos. Esta práctica mejora las probabilidades de que el objeto de datos contenga un formato que el destino de colocación pueda aceptar.
  • Los archivos existentes deben ofrecerse con el formato CF_HDROP .
  • Ofrezca datos similares a archivos con formatos de CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Este enfoque permite al destino crear un archivo a partir de un objeto de datos sin necesidad de conocer nada sobre el almacenamiento de datos subyacente. Normalmente, debe presentar los datos como una interfaz IStream . Este mecanismo de transferencia de datos es más flexible que un objeto de memoria global y usa mucha menos memoria.
  • Los orígenes de arrastre deben ofrecer el formato CFSTR_SHELLIDLIST al arrastrar elementos de Shell. Los objetos de datos de los elementos se pueden adquirir a través de los métodos IShellFolder::GetUIObjectOf o IShellItem::BindToHandler. Los orígenes de datos pueden crear una implementación de objeto de datos estándar que admita el formato CFSTR_SHELLIDLIST mediante SHCreateDataObject.
  • Los destinos de colocación que quieran razonar sobre los elementos que se arrastran mediante el modelo de programación de elementos de shell pueden convertir un IDataObject en un IShellItemArray mediante SHCreateShellItemArrayFromDataObject.
  • Use cursores de comentarios estándar.
  • Admita el arrastre izquierdo y derecho.
  • Utilice el propio objeto de datos de un objeto incrustado. Este enfoque permite que la aplicación recupere cualquier formato adicional que el objeto de datos tenga que ofrecer y evite crear una capa adicional de contención. Por ejemplo, un objeto incrustado del servidor A se arrastra desde el servidor o contenedor B y se coloca en el contenedor C. C debe crear un objeto incrustado del servidor A, no un objeto incrustado del servidor B que contenga un objeto incrustado del servidor A.
  • Recuerde que el Shell podría usar operaciones optimizadas de movimiento o eliminación al mover archivos. La aplicación debe ser capaz de reconocer estas operaciones y responder adecuadamente.

Para destinos de datos:

  • Los formatos del Portapapeles de Shell, a excepción de CF_HDROP, no están predefinidos. Cada formato que quiera usar debe registrarse llamando a RegisterClipboardFormat.
  • Implemente y registre un destino de colocación OLE. Evite usar destinos de Windows 3.1 o el mensaje de WM_DROPFILES , si es posible.
  • Los formatos contenidos en un objeto de datos varían, dependiendo de dónde procede el objeto. Dado que por lo general no sabe con antelación de dónde procede un objeto de datos, no suponga que un formato determinado estará presente. El objeto de datos debe enumerar los formatos en orden de calidad, empezando por lo mejor. Por lo tanto, para obtener el mejor formato disponible, las aplicaciones suelen enumerar los formatos disponibles y usar el primer formato en la enumeración que pueden admitir.
  • Compatibilidad con arrastrar a la derecha. Puede personalizar el menú contextual de arrastrar mediante la creación de un controlador de arrastrar y colocar.
  • Si la aplicación aceptará archivos existentes, debe poder controlar el formato CF_HDROP .
  • En general, las aplicaciones que aceptan archivos también deben controlar los formatos CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Aunque los archivos del sistema de archivos tienen el formato CF_HDROP, los archivos de proveedores como las extensiones de espacio de nombres suelen usar CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Algunos ejemplos son las carpetas de Windows CE, las carpetas del Protocolo de transferencia de archivos (FTP), las carpetas web y las carpetas CAB. Normalmente, el origen implementa una interfaz IStream para presentar datos de su almacenamiento como un archivo.
  • Recuerde que el Shell podría usar operaciones optimizadas de movimiento o eliminación al mover archivos. La aplicación debe ser capaz de reconocer estas operaciones y responder adecuadamente.

Copiar nombres de archivo desde el Portapapeles a una aplicación

Escenario: un usuario selecciona uno o varios archivos en el Explorador de Windows y los copia en el Portapapeles. La aplicación extrae los nombres de archivo y los pega en el documento.

Este escenario podría usarse, por ejemplo, para permitir que un usuario cree un vínculo HTML cortando y pegando el archivo en la aplicación. A continuación, la aplicación puede extraer el nombre de archivo del objeto de datos y procesarlo para crear una etiqueta de anclaje.

Cuando un usuario selecciona un archivo en el Explorador de Windows y lo copia en el Portapapeles, shell crea un objeto de datos. A continuación, llama a OleSetClipboard para colocar un puntero a la interfaz IDataObject del objeto de datos en el Portapapeles.

Cuando el usuario selecciona el comando Pegar en el menú o la barra de herramientas de la aplicación:

  1. Llame a OleGetClipboard para recuperar la interfaz IDataObject del objeto de datos.
  2. Llame a IDataObject::EnumFormatEtc para solicitar un objeto enumerador.
  3. Use la interfaz IEnumFORMATETC del objeto enumerador para enumerar los formatos contenidos en el objeto de datos.

Nota:

Los dos últimos pasos de este procedimiento se incluyen por integridad. Normalmente no son necesarios para transferencias de archivos simples. Todos los objetos de datos usados para este tipo de transferencia de datos deben contener el formato CF_HDROP , que se puede usar para determinar los nombres de los archivos contenidos por el objeto . Sin embargo, para transferencias de datos más generales, debe enumerar los formatos y seleccionar el mejor que pueda controlar la aplicación.

 

Extracción de los nombres de archivo del objeto de datos

El siguiente paso consiste en extraer uno o varios nombres de archivo del objeto de datos y pegarlos en la aplicación. Tenga en cuenta que el procedimiento descrito en esta sección para extraer un nombre de archivo de un objeto de datos se aplica igualmente bien a las transferencias de arrastrar y colocar.

La manera más sencilla de recuperar nombres de archivo de un objeto de datos es el formato CF_HDROP :

  1. Llame a IDataObject::GetData. Establezca el miembro cfFormat de la estructura FORMATETC en CF_HDROP y el miembro tymed en TYMED_HGLOBAL. El miembro dwAspect se establece normalmente en DVASPECT_CONTENT. Sin embargo, si necesita tener la ruta de acceso del archivo en formato corto (8.3), establezca dwAspect en DVASPECT_SHORT.

    Cuando IDataObject::GetData devuelve, el miembro hGlobal de la estructura STGMEDIUM apunta a un objeto de memoria global que contiene los datos.

  2. Cree una variable HDROP y establézcala en el miembro hGlobal de la estructura STGMEDIUM. La variable HDROP es ahora un identificador de una estructura DROPFILES seguida de una cadena terminada en null doble que contiene las rutas de acceso de archivo completas de los archivos copiados.

  3. Determine cuántas rutas de acceso de archivo hay en la lista llamando a DragQueryFile con el parámetro iFile establecido en 0xFFFFFFFF. La función devuelve el número de rutas de acceso de archivo de la lista. El índice de base cero de la ruta de acceso del archivo en esta lista se usa en el paso siguiente para identificar una ruta de acceso determinada.

  4. Extraiga las rutas de acceso de archivo del objeto de memoria global llamando a DragQueryFile una vez para cada archivo, con iFile establecido en el índice del archivo.

  5. Procese las rutas de acceso de archivo según sea necesario y péguelas en la aplicación.

  6. Llame a ReleaseStgMedium y pase el puntero a la estructura STGMEDIUM que pasó a IDataObject::GetData en el paso 1. Una vez que haya liberado la estructura, el valor HDROP que creó en el paso 2 ya no es válido y no debe usarse.

Copiar el contenido de un archivo quitado en una aplicación

Escenario: un usuario arrastra uno o varios archivos desde el Explorador de Windows y los coloca en la ventana de la aplicación. La aplicación extrae el contenido del archivo (s) y lo pega en la aplicación.

En este escenario se usa arrastrar y colocar para transferir los archivos desde el Explorador de Windows a la aplicación. Antes de la operación, la aplicación debe:

  1. Llame a RegisterClipboardFormat para registrar los formatos necesarios del Portapapeles de Shell.
  2. Llame a RegisterDragDrop para registrar una ventana de destino y la interfaz IDropTarget de la aplicación.

Una vez que el usuario inicie la operación, seleccione uno o varios archivos y empiece a arrastrarlos:

  1. El Explorador de Windows crea un objeto de datos y carga los formatos admitidos en él.
  2. El Explorador de Windows llama a DoDragDrop para iniciar el bucle de arrastre.
  3. Cuando la imagen de arrastre llega a la ventana de destino, el sistema le notifica llamando a IDropTarget::D ragEnter.
  4. Para determinar qué contiene el objeto de datos, llame al método IDataObject::EnumFormatEtc del objeto de datos. Utilice el objeto enumerador devuelto por el método para enumerar los formatos contenidos en el objeto de datos. Si la aplicación no quiere aceptar ninguno de estos formatos, devuelva DROPEFFECT_NONE. Para este escenario, la aplicación debe omitir los objetos de datos que no contengan formatos usados para transferir archivos, como CF_HDROP.
  5. Cuando el usuario quita los datos, el sistema llama a IDropTarget::D rop.
  6. Use la interfaz IDataObject para extraer el contenido de los archivos.

Hay varias maneras diferentes de extraer el contenido de un objeto Shell de un objeto de datos. En general, use el orden siguiente:

Si el proceso de extracción de datos será largo, es posible que desee realizar la operación de forma asincrónica en un subproceso en segundo plano. El subproceso principal puede continuar sin retrasos innecesarios. Para obtener una explicación sobre cómo controlar la extracción asincrónica de datos, consulte Arrastrar y quitar objetos de Shell de forma asincrónica.

Usar el formato CFSTR_FILECONTENTS para extraer datos de un archivo

El formato CFSTR_FILECONTENTS proporciona una manera muy flexible y eficaz de transferir el contenido de un archivo. Ni siquiera es necesario que los datos se almacenen como un único archivo. Todo lo necesario para este formato es que el objeto de datos presenta los datos al destino como si fuera un archivo. Por ejemplo, los datos reales pueden ser una sección de un documento de texto o un bloque de datos extraídos de una base de datos. El destino puede tratar los datos como un archivo y no necesita saber nada sobre el mecanismo de almacenamiento subyacente.

Normalmente, las extensiones de espacio de nombres usan CFSTR_FILECONTENTS para transferir datos porque este formato no supone ningún mecanismo de almacenamiento determinado. Una extensión de espacio de nombres puede usar cualquier mecanismo de almacenamiento conveniente y usar este formato para presentar sus objetos a las aplicaciones como si fueran archivos.

Normalmente, el mecanismo de transferencia de datos para CFSTR_FILECONTENTS es TYMED_ISTREAM. La transferencia de un puntero de interfaz IStream requiere mucha menos memoria que cargar los datos en un objeto de memoria global y IStream es una manera más sencilla de representar datos que IStorage.

Un formato CFSTR_FILECONTENTS siempre va acompañado de un formato CFSTR_FILEDESCRIPTOR . Primero debe examinar el contenido de este formato. Si se transfiere más de un archivo, el objeto de datos contendrá realmente varios formatos de CFSTR_FILECONTENTS , uno para cada archivo. El formato CFSTR_FILEDESCRIPTOR contiene el nombre y los atributos de cada archivo y proporciona un valor de índice para cada archivo necesario para extraer el formato CFSTR_FILECONTENTS de un archivo determinado.

Para extraer un formato de CFSTR_FILECONTENTS :

  1. Extraiga el formato CFSTR_FILEDESCRIPTOR como un valor de TYMED_HGLOBAL .
  2. El miembro hGlobal de la estructura STGMEDIUM devuelta apunta a un objeto de memoria global. Bloquee ese objeto pasando el valor hGlobal a GlobalLock.
  3. Convierta el puntero devuelto por GlobalLock en un puntero FILEGROUPDESCRIPTOR. Apuntará a una estructura FILEGROUPDESCRIPTOR seguida de una o varias estructuras FILEDESCRIPTOR. Cada estructura FILEDESCRIPTOR contiene una descripción de un archivo que está incluido en uno de los formatos de CFSTR_FILECONTENTS adjuntos.
  4. Examine las estructuras FILEDESCRIPTOR para determinar cuál corresponde al archivo que desea extraer. El índice de base cero de esa estructura FILEDESCRIPTOR se usa para identificar el formato de CFSTR_FILECONTENTS del archivo. Dado que el tamaño de un bloque de memoria global no es preciso byte, use los miembros nFileSizeLow y nFileSizeHigh de la estructura para determinar cuántos bytes representan el archivo en el objeto de memoria global.
  5. Llame a IDataObject::GetData con el miembro cfFormat de la estructura FORMATETC establecida en el valor CFSTR_FILECONTENTS y el miembro lIndex establecido en el índice determinado en el paso anterior. El miembro tymed se establece normalmente en TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE. A continuación, el objeto de datos puede elegir su mecanismo de transferencia de datos preferido.
  6. La estructura STGMEDIUM que devuelve IDataObject::GetData contendrá un puntero a los datos del archivo. Examine el miembro tymed de la estructura para determinar el mecanismo de transferencia de datos.
  7. Si tymed se establece en TYMED_ISTREAM o TYMED_ISTORAGE, use la interfaz para extraer los datos. Si tymed se establece en TYMED_HGLOBAL, los datos se encuentran en un objeto de memoria global. Para obtener una explicación sobre cómo extraer datos de un objeto de memoria global, vea la sección Extracción de un objeto de memoria global de un objeto de datos de Shell Data Object.
  8. Llame a GlobalLock para desbloquear el objeto de memoria global que bloqueó en el paso 2.

Control de las operaciones de movimiento optimizadas

Escenario: un archivo se mueve del sistema de archivos a una extensión de espacio de nombres mediante un movimiento optimizado.

En una operación de movimiento convencional, el destino realiza una copia de los datos y el origen elimina el original. Este procedimiento puede ser ineficaz porque requiere dos copias de los datos. Con objetos de gran tamaño como bases de datos, es posible que una operación de movimiento convencional no sea incluso práctica.

Con un movimiento optimizado, el destino usa su comprensión de cómo se almacenan los datos para controlar toda la operación de movimiento. Nunca hay una segunda copia de los datos y no es necesario que el origen elimine los datos originales. Los datos de Shell son adecuados para los movimientos optimizados, ya que el destino puede controlar toda la operación mediante la API de Shell. Un ejemplo típico es mover archivos. Una vez que el destino tiene la ruta de acceso de un archivo que se va a mover, puede usar SHFileOperation para moverlo. No es necesario que el origen elimine el archivo original.

Nota:

El shell normalmente usa un movimiento optimizado para mover archivos. Para controlar correctamente la transferencia de datos de Shell, la aplicación debe ser capaz de detectar y controlar un movimiento optimizado.

 

Los movimientos optimizados se controlan de la siguiente manera:

  1. El origen llama a DoDragDrop con el parámetro dwEffect establecido en DROPEFFECT_MOVE para indicar que los objetos de origen se pueden mover.

  2. El destino recibe el valor de DROPEFFECT_MOVE a través de uno de sus métodos IDropTarget , lo que indica que se permite un movimiento.

  3. El destino copia el objeto (movimiento no optimizado) o mueve el objeto (movimiento optimizado).

  4. A continuación, el destino indica al origen si necesita eliminar los datos originales.

    Un movimiento optimizado es la operación predeterminada, con los datos eliminados por el destino. Para informar al origen de que se realizó un movimiento optimizado:

      • El destino establece el valor pdwEffect que recibió a través de su método IDropTarget::D rop en algún valor distinto de DROPEFFECT_MOVE. Normalmente se establece en DROPEFFECT_NONE o DROPEFFECT_COPY. DoDragDrop devolverá el valor al origen.
      • El destino también llama al método IDataObject::SetData del objeto de datos y lo pasa un identificador de formato CFSTR_PERFORMEDDROPEFFECT establecido en DROPEFFECT_NONE. Esta llamada al método es necesaria porque es posible que algunos destinos de colocación no establezcan correctamente el parámetro pdwEffect de DoDragDrop . El formato CFSTR_PERFORMEDDROPEFFECT es la manera confiable de indicar que se ha realizado un movimiento optimizado.

    Si el destino hizo un movimiento no optimizado, el origen debe eliminar los datos. Para informar al origen de que se realizó un movimiento no optimizado:

  5. El origen inspecciona los dos valores que el destino puede devolver. Si ambos se establecen en DROPEFFECT_MOVE, completa el movimiento no optimizado eliminando los datos originales. De lo contrario, el destino hizo un movimiento optimizado y se eliminaron los datos originales.

Control de las operaciones de eliminación al pegar

Escenario: uno o varios archivos se cortan de una carpeta en el Explorador de Windows y se pegan en una extensión de espacio de nombres. El Explorador de Windows deja los archivos resaltados hasta que recibe comentarios sobre el resultado de la operación de pegado.

Tradicionalmente, cuando un usuario corta los datos, desaparece inmediatamente de la vista. Esto puede no ser eficaz y puede provocar problemas de facilidad de uso si el usuario se preocupa por lo que ha ocurrido con los datos. Un enfoque alternativo consiste en usar una operación de eliminación al pegar.

Con una operación de eliminación al pegar, los datos seleccionados no se quitan inmediatamente de la vista. En su lugar, la aplicación de origen la marca como seleccionada, quizás cambiando la fuente o el color de fondo. Una vez que la aplicación de destino haya pegado los datos, notifica al origen el resultado de la operación. Si el destino realizó un movimiento optimizado, el origen simplemente puede actualizar su presentación. Si el destino realizó un movimiento normal, el origen también debe eliminar su copia de los datos. Si se produce un error en el pegado, la aplicación de origen restaura los datos seleccionados a su apariencia original.

Nota:

El shell usa normalmente delete-on-paste cuando se usa una operación de cortar y pegar para mover archivos. Las operaciones de eliminación al pegar con objetos shell normalmente usan un movimiento optimizado para mover los archivos. Para controlar correctamente la transferencia de datos de Shell, la aplicación debe ser capaz de detectar y controlar las operaciones de eliminación al pegar.

 

El requisito esencial para eliminar al pegar es que el destino debe notificar el resultado de la operación al origen. Sin embargo, las técnicas estándar del Portapapeles no se pueden usar para implementar la eliminación al pegar porque no proporcionan una manera de que el destino se comunique con el origen. En su lugar, la aplicación de destino usa el método IDataObject::SetData del objeto de datos para notificar el resultado al objeto de datos. Después, el objeto de datos puede comunicarse con el origen a través de una interfaz privada.

El procedimiento básico para una operación delete-on-paste es el siguiente:

  1. El origen marca la pantalla de los datos seleccionados.
  2. El origen crea un objeto de datos. Indica una operación de corte agregando el formato CFSTR_PREFERREDDROPEFFECT con un valor de datos de DROPEFFECT_MOVE.
  3. El origen coloca el objeto de datos en el Portapapeles mediante OleSetClipboard.
  4. El destino recupera el objeto de datos del Portapapeles mediante OleGetClipboard.
  5. El destino extrae los datos de CFSTR_PREFERREDDROPEFFECT . Si se establece en solo DROPEFFECT_MOVE, el destino puede realizar un movimiento optimizado o simplemente copiar los datos.
  6. Si el destino no realiza un movimiento optimizado, llama al método IDataObject::SetData con el formato CFSTR_PERFORMEDDROPEFFECT establecido en DROPEFFECT_MOVE.
  7. Una vez completado el pegado, el destino llama al método IDataObject::SetData con el formato CFSTR_PASTESUCCEEDED establecido en DROPEFFECT_MOVE.
  8. Cuando se llama al método IDataObject::SetData del origen con el formato CFSTR_PASTESUCCEEDED establecido en DROPEFFECT_MOVE, debe comprobar si también recibió el formato CFSTR_PERFORMEDDROPEFFECT establecido en DROPEFFECT_MOVE. Si el destino envía ambos formatos, el origen tendrá que eliminar los datos. Si solo se recibe el formato CFSTR_PASTESUCCEEDED , el origen simplemente puede quitar los datos de su pantalla. Si se produce un error en la transferencia, el origen actualiza la pantalla a su apariencia original.

Transferencia de datos hacia y desde carpetas virtuales

Escenario: un usuario arrastra un objeto desde o lo coloca en una carpeta virtual.

Las carpetas virtuales contienen objetos que generalmente no forman parte del sistema de archivos. Algunas carpetas virtuales, como la Papelera de reciclaje, pueden representar datos almacenados en el disco duro, pero no como objetos normales del sistema de archivos. Algunos pueden representar datos almacenados que están en un sistema remoto, como un equipo portátil o un sitio FTP. Otros, como la carpeta Impresoras, contienen objetos que no representan datos almacenados en absoluto. Aunque algunas carpetas virtuales forman parte del sistema, los desarrolladores también pueden crear e instalar carpetas virtuales personalizadas mediante la implementación de una extensión de espacio de nombres.

Independientemente del tipo de datos o de cómo se almacene, el Shell presenta los objetos de carpeta y archivo que contiene una carpeta virtual como si fueran archivos y carpetas normales. Es responsabilidad de la carpeta virtual tomar los datos que contenga y presentarlos al Shell adecuadamente. Este requisito significa que las carpetas virtuales normalmente admiten transferencias de datos de arrastrar y colocar y Portapapeles.

Por lo tanto, hay dos grupos de desarrolladores que deben preocuparse por la transferencia de datos hacia y desde carpetas virtuales:

  • Desarrolladores cuyas aplicaciones necesitan aceptar datos transferidos desde una carpeta virtual.
  • Desarrolladores cuyas extensiones de espacio de nombres necesitan admitir correctamente la transferencia de datos.

Aceptar datos de una carpeta virtual

Las carpetas virtuales pueden representar prácticamente cualquier tipo de datos y pueden almacenar esos datos de cualquier manera que elijan. Algunas carpetas virtuales pueden contener realmente archivos y carpetas normales del sistema de archivos. Otros pueden, por ejemplo, empaquetar todos sus objetos en un único documento o base de datos.

Cuando un objeto de sistema de archivos se transfiere a una aplicación, el objeto de datos normalmente contiene un formato de CF_HDROP con la ruta de acceso completa del objeto. La aplicación puede extraer esta cadena y usar las funciones normales del sistema de archivos para abrir el archivo y extraer sus datos. Sin embargo, dado que las carpetas virtuales normalmente no contienen objetos normales del sistema de archivos, por lo general no usan CF_HDROP.

En lugar de CF_HDROP, los datos se transfieren normalmente desde carpetas virtuales con los formatos de CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/. El formato CFSTR_FILECONTENTS tiene dos ventajas sobre CF_HDROP:

  • No se supone ningún método concreto de almacenamiento de datos.
  • El formato es más flexible. Admite tres mecanismos de transferencia de datos: un objeto de memoria global, una interfaz IStream o una interfaz IStorage.

Los objetos de memoria global rara vez se usan para transferir datos a objetos virtuales o desde ellos porque los datos deben copiarse en la memoria en su totalidad. La transferencia de un puntero de interfaz requiere casi ninguna memoria y es mucho más eficaz. Con archivos muy grandes, un puntero de interfaz podría ser el único mecanismo práctico de transferencia de datos. Normalmente, los datos se representan mediante un puntero IStream, ya que esa interfaz es algo más flexible que IStorage. El destino extrae el puntero del objeto de datos y usa los métodos de interfaz para extraer los datos.

Para obtener más información sobre cómo controlar los formatos de CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS, consulte Uso del formato CFSTR_FILECONTENTS para extraer datos de un archivo.

Transferencia de datos hacia y desde una extensión de espacio de nombres

Al implementar una extensión de espacio de nombres, normalmente querrá admitir funcionalidades de arrastrar y colocar. Siga las recomendaciones para quitar orígenes y destinos descritos en Directrices generales. En concreto, una extensión de espacio de nombres debe:

  • Puede controlar los formatos de CFSTR_FILECONTENTS de CFSTR_FILEDESCRIPTOR/. Estos dos formatos se usan normalmente para transferir objetos hacia y desde extensiones de espacio de nombres.
  • Puede controlar los movimientos optimizados. Shell espera que los objetos shell se muevan con un movimiento optimizado.
  • Puede controlar una operación de eliminación al pegar . Shell usa delete-on-paste cuando los objetos se mueven del Shell con una operación de cortar y pegar.
  • Puede controlar la transferencia de datos a través de una interfaz IStream o IStorage. La transferencia de datos a una carpeta virtual se controla normalmente mediante la transferencia de uno de estos dos punteros de interfaz, normalmente un puntero IStream . A continuación, el destino llama a los métodos de interfaz para extraer los datos:
      • Como origen de eliminación, la extensión de espacio de nombres debe extraer los datos del almacenamiento y pasarlos a través de esta interfaz al destino.
      • Como destino de colocación, una extensión de espacio de nombres debe aceptar datos de un origen a través de esta interfaz y almacenarlos correctamente.

Quitar archivos en la papelera de reciclaje

Escenario: el usuario quita un archivo en la Papelera de reciclaje. La aplicación o la extensión de espacio de nombres elimina el archivo original.

La Papelera de reciclaje es una carpeta virtual que se usa como repositorio para los archivos que ya no son necesarios. Siempre que la Papelera de reciclaje no se haya vaciado, el usuario podrá recuperar el archivo más adelante y devolverlo al sistema de archivos.

En su mayor parte, transferir objetos Shell a la Papelera de reciclaje funciona de forma muy similar a cualquier otra carpeta. Sin embargo, cuando un usuario quita un archivo en la Papelera de reciclaje, el origen debe eliminar el original, incluso si los comentarios de la carpeta indican una operación de copia. Normalmente, un origen de eliminación no tiene forma de saber en qué carpeta se ha quitado su objeto de datos. Sin embargo, para los sistemas Windows 2000 y versiones posteriores, cuando se quita un objeto de datos en la Papelera de reciclaje, shell llamará al método IDataObject::SetData del objeto de datos con un formato de CFSTR_TARGETCLSID establecido en el identificador de clase de la Papelera de reciclaje (CLSID) (CLSID_RecycleBin). Para controlar correctamente el caso de la papelera de reciclaje, el objeto de datos debe ser capaz de reconocer este formato y comunicar la información al origen a través de una interfaz privada.

Nota:

Cuando se llama a IDataObject::SetData con un formato CFSTR_TARGETCLSID establecido en CLSID_RecycleBin, el origen de datos debe cerrar los identificadores abiertos a los objetos que se transfieren antes de volver del método. De lo contrario, puede crear infracciones de uso compartido.

 

Crear e importar archivos de extracción

Escenario: un usuario arrastra algunos datos desde el archivo de datos de una aplicación OLE y lo coloca en el escritorio o en el Explorador de Windows.

Windows permite a los usuarios arrastrar un objeto desde el archivo de datos de una aplicación OLE y colocarlo en el escritorio o en una carpeta del sistema de archivos. Esta operación crea un archivo de extracción, que contiene los datos o un vínculo a los datos. El nombre de archivo se toma del nombre corto registrado para el CLSID del objeto y los datos CF_TEXT . Para que shell cree un archivo de extracción que contenga datos, la interfaz IDataObject de la aplicación debe admitir el formato CF_EMBEDSOURCE Portapapeles. Para crear un archivo que contenga un vínculo, IDataObject debe admitir el formato CF_LINKSOURCE.

También hay tres características opcionales que una aplicación puede implementar para admitir archivos de extracción:

  • Soporte técnico de ida y vuelta
  • Formatos de datos almacenados en caché
  • Representación retrasada

Soporte técnico de ida y vuelta

Un recorrido de ida y vuelta implica transferir un objeto de datos a otro contenedor y volver al documento original. Por ejemplo, un usuario podría transferir un grupo de celdas de una hoja de cálculo al escritorio, creando un archivo de extracción con los datos. Si el usuario vuelve a transferir la extracción a la hoja de cálculo, los datos deben integrarse en el documento tal como estaba antes de la transferencia original.

Cuando shell crea el archivo de extracción, representa los datos como un objeto de inserción. Cuando la extracción se transfiere a otro contenedor, se transfiere como un objeto de inserción, incluso si se devuelve al documento original. La aplicación es responsable de determinar el formato de datos contenido en la extracción y devolver los datos a su formato nativo si es necesario.

Para establecer el formato del objeto incrustado, determine su CLSID recuperando el formato CF_OBJECTDESCRIPTOR del objeto. Si clSID indica un formato de datos que pertenece a la aplicación, debe transferir los datos nativos en lugar de llamar a OleCreateFromData.

Formatos de datos almacenados en caché

Cuando shell crea un archivo de extracción, comprueba el registro para ver la lista de formatos disponibles. De forma predeterminada, hay dos formatos disponibles: CF_EMBEDSOURCE y CF_LINKSOURCE. Sin embargo, hay una serie de escenarios en los que es posible que las aplicaciones necesiten tener archivos de extracción en diferentes formatos:

  • Para permitir que los fragmentos se transfieran a contenedores que no sean OLE, que no pueden aceptar formatos de objeto incrustados.
  • Para permitir que los conjuntos de aplicaciones se comuniquen con un formato privado.
  • Para que los viajes de ida y vuelta sean más fáciles de manejar.

Las aplicaciones pueden agregar formatos a la extracción almacenando en caché en el registro. Hay dos tipos de formatos almacenados en caché:

  • Formatos de caché de prioridad. Para estos formatos, los datos se copian en su totalidad en la extracción del objeto de datos.
  • Formatos representados con retraso. Para estos formatos, el objeto de datos no se copia en el scrap. En su lugar, la representación se retrasa hasta que un destino solicita los datos. La representación con retraso se describe con más detalle en la sección siguiente.

Para agregar una caché de prioridad o un formato representado por retraso, cree una subclave DataFormat en la clave CLSID de la aplicación que es el origen de los datos. En esa subclave, cree una subclave PriorityCacheFormats o DelayRenderFormats . Para cada caché de prioridad o formato representado con retraso, cree una subclave numerada a partir de cero. Establezca el valor de esta clave en una cadena con el nombre registrado del formato o un valor de #X, donde X representa el número de formato de un formato de Portapapeles estándar.

En el ejemplo siguiente se muestran los formatos almacenados en caché para dos aplicaciones. La aplicación MyProg1 tiene el formato de texto enriquecido como formato de caché de prioridad y un formato privado "Mi formato" como formato representado con retraso. La aplicación MyProg2 tiene el formato CF_BITMAP (#8") como formato de caché de prioridad.

HKEY_CLASSES_ROOT
   CLSID
      {GUID}
         (Default) = MyProg1
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = Rich Text Format
            DelayRenderFormats
               0
                  (Default) = My Format
      {GUID}
         (Default) = MyProg2
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = #8

Se pueden agregar formatos adicionales mediante la creación de subclaves numeradas adicionales.

Representación diferida

Un formato de representación retrasado permite a una aplicación crear un archivo de extracción, pero retrasar el gasto de representación de los datos hasta que un destino lo solicite. La interfaz IDataObject de una extracción ofrecerá los formatos de representación retrasados al destino junto con datos nativos y almacenados en caché. Si el destino solicita un formato de representación retrasado, shell ejecutará la aplicación y proporcionará los datos al destino desde el objeto activo.

Nota:

Dado que la representación diferida es algo arriesgada, se debe usar con precaución. No funcionará si el servidor no está disponible o en las aplicaciones que no están habilitadas para OLE.

 

Arrastrar y quitar objetos de shell de forma asincrónica

Escenario: un usuario transfiere un bloque grande de datos de origen a destino. Para evitar el bloqueo de ambas aplicaciones durante una cantidad significativa de tiempo, el destino extrae los datos de forma asincrónica.

Normalmente, arrastrar y colocar es una operación sincrónica. En resumen:

  1. El origen de colocación llama a DoDragDrop y bloquea su subproceso principal hasta que la función devuelve. El bloqueo del subproceso principal normalmente bloquea el procesamiento de la interfaz de usuario.
  2. Después de llamar al método IDropTarget::D rop del destino, el destino extrae los datos del objeto de datos en su subproceso principal. Este procedimiento normalmente bloquea el procesamiento de la interfaz de usuario del destino durante el proceso de extracción.
  3. Una vez extraídos los datos, el destino devuelve la llamada IDropTarget::D rop, el sistema devuelve DoDragDrop y ambos subprocesos pueden continuar.

En resumen, la transferencia de datos sincrónica puede bloquear los subprocesos principales de ambas aplicaciones durante una cantidad significativa de tiempo. En concreto, ambos subprocesos deben esperar mientras el destino extrae los datos. Para pequeñas cantidades de datos, el tiempo necesario para extraer datos es pequeño y la transferencia de datos sincrónica funciona bastante bien. Sin embargo, la extracción sincrónica de grandes cantidades de datos puede provocar retrasos prolongados e interferir con la interfaz de usuario de destino y origen.

La interfaz IDataObjectAsyncCapability de IAsyncOperation/es una interfaz opcional que un objeto de datos puede implementar. Proporciona al destino de colocación la capacidad de extraer datos del objeto de datos de forma asincrónica en un subproceso en segundo plano. Una vez que la extracción de datos se entrega al subproceso en segundo plano, los subprocesos principales de ambas aplicaciones pueden continuar.

Uso de IASyncOperation/IDataObjectAsyncCapability

Nota:

La interfaz se llamaba originalmente IAsyncOperation, pero posteriormente se cambió a IDataObjectAsyncCapability. De lo contrario, las dos interfaces son idénticas.

 

El propósito de IAsyncOperation/IDataObjectAsyncCapability es permitir que el origen y el destino de colocación negocien si los datos se pueden extraer de forma asincrónica. En el procedimiento siguiente se describe cómo usa la interfaz el origen de colocación:

  1. Cree un objeto de datos que exponga IDataObjectAsyncCapability de IAsyncOperation/.
  2. Llame a SetAsyncMode con fDoOpAsync establecido en VARIANT_TRUE para indicar que se admite una operación asincrónica.
  3. Después de que DoDragDrop devuelva, llame a InOperation:
    • Si InOperation produce un error o devuelve VARIANT_FALSE, se ha realizado una transferencia de datos sincrónica normal y se finaliza el proceso de extracción de datos. El origen debe realizar cualquier limpieza necesaria y continuar.
    • Si InOperation devuelve VARIANT_TRUE, los datos se extraen de forma asincrónica. EndOperation debe controlar las operaciones de limpieza.
  4. Libere el objeto de datos.
  5. Cuando se completa la transferencia de datos asincrónica, el objeto de datos normalmente notifica al origen a través de una interfaz privada.

En el procedimiento siguiente se describe cómo el destino de colocación usa la interfaz IDataObjectAsyncCapability de IAsyncOperation/para extraer datos de forma asincrónica:

  1. Cuando el sistema llama a IDropTarget::D rop, llame a IDataObject::QueryInterface y solicite una interfaz IDataObjectAsyncCapability de IAsyncOperation/(IID_IAsyncOperation/IID_IDataObjectAsyncCapability) desde el objeto de datos.
  2. Llame a GetAsyncMode. Si el método devuelve VARIANT_TRUE, el objeto de datos admite la extracción de datos asincrónica.
  3. Cree un subproceso independiente para controlar la extracción de datos y llamar a StartOperation.
  4. Devuelve la llamada A IDropTarget::D rop , como haría para una operación normal de transferencia de datos. DoDragDrop devolverá y desbloqueará el origen de colocación. No llame a IDataObject::SetData para indicar el resultado de una operación optimizada de movimiento o eliminación al pegar. Espere hasta que finalice la operación.
  5. Extraiga los datos en el subproceso en segundo plano. El subproceso principal del destino está desbloqueado y es libre de continuar.
  6. Si la transferencia de datos era una operación de movimiento o eliminación optimizada para pegar , llame a IDataObject::SetData para indicar el resultado.
  7. Notifique al objeto de datos que finaliza la extracción llamando a EndOperation.