Objeto de datos de shell

El objeto de datos es fundamental para todas las transferencias de datos de Shell. Es principalmente un contenedor que contiene los datos transferidos. Sin embargo, el destino también puede comunicarse con el objeto de datos para facilitar algunos tipos especializados de transferencia de datos de Shell, como los movimientos optimizados. En este tema se proporciona una explicación general de cómo funcionan los objetos de datos de Shell, cómo se construyen mediante un origen y cómo se controlan mediante un destino. Para obtener una explicación detallada sobre cómo usar objetos de datos para transferir diferentes tipos de datos de Shell, consulte Control de escenarios de transferencia de datos de Shell.

Cómo funcionan los objetos de datos

Los objetos de datos son objetos del Modelo de objetos componentes (COM), creados por el origen de datos para transferir datos a un destino. Normalmente llevan más de un elemento de datos. Hay dos razones para esta práctica:

  • Aunque casi cualquier tipo de datos se puede transferir con un objeto de datos, el origen normalmente no sabe qué tipo de datos puede aceptar el destino. Por ejemplo, los datos pueden ser una parte de un documento de texto con formato. Aunque el destino puede controlar información de formato compleja, también podría aceptar solo texto ANSI. Por este motivo, los objetos de datos suelen incluir los mismos datos en varios formatos diferentes. Después, el destino puede extraer los datos en un formato que pueda controlar.
  • Los objetos de datos también pueden contener elementos de datos auxiliares que no son versiones de datos de origen. Este tipo de elemento de datos suele proporcionar información adicional sobre la operación de transferencia de datos. Por ejemplo, el Shell usa elementos de datos auxiliares para indicar si se va a copiar o mover un archivo.

Formatos del Portapapeles

Cada elemento de datos de un objeto de datos tiene un formato asociado, normalmente denominado formato de Portapapeles. Hay una serie de formatos de Portapapeles estándar, declarados en Winuser.h, que corresponden a tipos de datos usados habitualmente. Los formatos del Portapapeles son enteros, pero normalmente se conocen por su nombre equivalente, que tiene el formato CF_XXX. Por ejemplo, el formato del Portapapeles para el texto ANSI es CF_TEXT.

Las aplicaciones pueden ampliar el intervalo de formatos de Portapapeles disponibles mediante la definición de formatos privados. Para definir un formato privado, una aplicación llama a RegisterClipboardFormat con una cadena que identifica el formato. El entero sin signo que devuelve la función es un valor de formato válido que se puede usar igual que un formato de Portapapeles estándar. Sin embargo, tanto el origen como el destino deben registrar el formato para poder usarlo. Con una excepción, CF_HDROP, los formatos del Portapapeles que se usan para transferir datos de Shell se definen como formatos privados. Deben estar registrados por el origen y el destino para poder usarlos. Para obtener una descripción de los formatos de Portapapeles de Shell disponibles, consulte Formatos del Portapapeles de Shell.

Aunque hay algunas excepciones, los objetos de datos normalmente contienen solo un elemento de datos para cada formato de Portapapeles que admiten. Esta correlación uno a uno entre formato y datos permite usar el valor de formato como identificador para el elemento de datos asociado. De hecho, al analizar el contenido de un objeto de datos, un elemento determinado de datos normalmente se denomina "formato" y se hace referencia a él por su nombre de formato. Por ejemplo, frases como "Extraer el formato de CF_TEXT..." normalmente se usan al analizar el elemento de datos de texto ANSI de un objeto de datos.

Cuando el destino de colocación recibe el puntero al objeto de datos, el destino de colocación enumera los formatos disponibles para determinar qué tipos de datos están disponibles. A continuación, solicita uno o varios de los formatos disponibles y extrae los datos. La manera específica de que el destino extrae datos de Shell de un objeto de datos varía con el formato; esto se describe en detalle en Cómo un destino controla un objeto de datos.

Con transferencias de datos simples del Portapapeles, los datos se colocan en un objeto de memoria global. La dirección de ese objeto se coloca en el Portapapeles, junto con su formato. El formato del Portapapeles indica al destino qué tipo de datos encontrará en la dirección asociada. Aunque las transferencias simples del Portapapeles son fáciles de implementar:

  • Los objetos de datos proporcionan una manera mucho más flexible de transferir datos.
  • Los objetos de datos son más adecuados para transferir grandes cantidades de datos.
  • Los objetos de datos se deben usar para transferir datos con una operación de arrastrar y colocar.

Por estos motivos, todas las transferencias de datos de Shell usan objetos de datos. Con los objetos de datos, los formatos del Portapapeles no se usan directamente. En su lugar, los elementos de datos se identifican con una generalización del formato del Portapapeles, una estructura FORMATETC .

FORMATETC (estructura)

La estructura FORMATETC es una versión extendida de un formato de Portapapeles. Como se usa para las transferencias de datos de Shell, la estructura FORMATETC tiene las siguientes características:

  • Un elemento de datos todavía se identifica con su formato de Portapapeles, en el miembro cfFormat .

  • La transferencia de datos no se limita a los objetos de memoria global. El miembro tymed se usa para indicar el mecanismo de transferencia de datos contenido en la estructura STGMEDIUM asociada. Se establece en uno de los valores de TYMED_XXX .

  • Shell usa el miembro lIndex con su formato CFSTR_FILECONTENTS para permitir que un objeto de datos contenga más de un elemento de datos por formato. Para obtener una explicación sobre cómo usar este formato, consulte la sección Uso del formato de CFSTR_FILECONTENTS para extraer datos de un archivo de control de escenarios de transferencia de datos de Shell.

  • El miembro dwAspect se establece normalmente en DVASPECT_CONTENT. Sin embargo, hay tres valores definidos en Shlobj.h que se pueden usar para la transferencia de datos de Shell.

    Valor Descripción
    DVASPECT_COPY Se usa para indicar que el formato representa una copia de los datos.
    DVASPECT_LINK Se usa para indicar que el formato representa un acceso directo a los datos.
    DVASPECT_SHORTNAME Se usa con el formato CF_HDROP para solicitar una ruta de acceso de archivo con los nombres abreviados al formato 8.3.

     

  • El miembro ptd no se usa para las transferencias de datos de Shell y normalmente se establece en NULL.

Estructura STGMEDIUM

La estructura STGMEDIUM proporciona acceso a los datos que se transfieren. Se admiten tres mecanismos de transferencia de datos para los datos de Shell:

El miembro tymed de la estructura STGMEDIUM es un valor TYMED_XXX que identifica el mecanismo de transferencia de datos. El segundo miembro es un puntero que usa el destino para extraer los datos. El puntero puede ser uno de varios tipos, dependiendo del valor tymed . Los tres valores tymed que se usan para las transferencias de datos de Shell se resumen en la tabla siguiente, junto con su nombre de miembro STGMEDIUM correspondiente.

Valor tymed Nombre del miembro Descripción
TYMED_HGLOBAL hGlobal Puntero a un objeto de memoria global. Este tipo de puntero se usa normalmente para transferir pequeñas cantidades de datos. Por ejemplo, shell usa objetos de memoria global para transferir cadenas de texto corto, como nombres de archivo o direcciones URL.
TYMED_ISTREAM pstm Puntero a una interfaz IStream . Este tipo de puntero se prefiere para la mayoría de las transferencias de datos de Shell porque requiere relativamente poca memoria en comparación con TYMED_HGLOBAL. Además, el mecanismo de transferencia de datos TYMED_ISTREAM no requiere que el origen almacene sus datos de ninguna manera determinada.
TYMED_ISTORAGE Pstg Puntero a una interfaz IStorage . El destino llama a los métodos de interfaz para extraer los datos. Al igual que TYMED_ISTREAM, este tipo de puntero requiere relativamente poca memoria. Sin embargo, dado que TYMED_ISTORAGE es menos flexible que TYMED_ISTREAM, no se usa con frecuencia.

 

Cómo crea un origen un objeto de datos

Cuando un usuario inicia una transferencia de datos de Shell, el origen es responsable de crear un objeto de datos y cargarlo con datos. En el procedimiento siguiente se resume el proceso:

  1. Llame a RegisterClipboardFormat para obtener un valor de formato de Portapapeles válido para cada formato de Shell que se incluirá en el objeto de datos. Recuerde que CF_HDROP ya es un formato de Portapapeles válido y no es necesario registrarse.
  2. Para cada formato que se va a transferir, coloque los datos asociados en un objeto de memoria global o cree un objeto que proporcione acceso a esos datos a través de una interfaz IStream o IStorage . Las interfaces IStream e IStorage se crean mediante técnicas COM estándar. Para obtener una explicación sobre cómo controlar objetos de memoria global, vea Cómo agregar un objeto de memoria global a un objeto de datos.
  3. Cree estructuras FORMATETC y STGMEDIUM para cada formato.
  4. Cree una instancia de un objeto de datos.
  5. Cargue los datos en el objeto de datos llamando al método IDataObject::SetData para cada formato admitido y pasando las estructuras FORMATETC y STGMEDIUM del formato.
  6. Con las transferencias de datos del Portapapeles, llame a OleSetClipboard para colocar un puntero a la interfaz IDataObject del objeto de datos en el Portapapeles. Para las transferencias de arrastrar y colocar, inicie un bucle de arrastre llamando a DoDragDrop. El puntero IDataObject se pasará al destino de colocación cuando se quiten los datos, finalizando el bucle de arrastre.

El objeto de datos ya está listo para transferirse al destino. Para las transferencias de datos del Portapapeles, el objeto se mantiene simplemente hasta que el destino lo solicita llamando a OleGetClipboard. Para las transferencias de datos de arrastrar y colocar, el objeto de datos es responsable de crear un icono para representar los datos y moverlos a medida que el usuario mueve el cursor. Mientras el objeto está en el bucle de arrastre, el origen recibe información de estado a través de su interfaz IDropSource . Para obtener más información, consulte Implementación de IDropSource.

El origen no recibe ninguna notificación si un destino recupera el objeto de datos del Portapapeles. Cuando un objeto se coloca en un destino mediante una operación de arrastrar y colocar, se devolverá la función DoDragDrop a la que se llamó para iniciar el bucle de arrastre.

Cómo agregar un objeto de memoria global a un objeto de datos

Muchos de los formatos de datos de Shell están en forma de un objeto de memoria global. Use el procedimiento siguiente para crear un formato que contenga un objeto de memoria global y cargarlo en el objeto de datos:

  1. Cree una estructura FORMATETC . Establezca el miembro cfFormat en el valor de formato del Portapapeles adecuado y el miembro tymed en TYMED_HGLOBAL.
  2. Cree una estructura STGMEDIUM . Establezca el miembro tymed en TYMED_HGLOBAL.
  3. Cree un objeto de memoria global llamando a GlobalAlloc para asignar un bloque de memoria de tamaño adecuado.
  4. Asigne el bloque de datos que se transferirá a la dirección devuelta por GlobalAlloc.
  5. Asigne la dirección del objeto de memoria global al miembro hGlobal de la estructura STGMEDIUM .
  6. Cargue el formato en el objeto de datos llamando a IDataObject::SetData y pasando las estructuras FORMATETC y STGMEDIUM creadas en los pasos anteriores.

La siguiente función de ejemplo crea un objeto de memoria global que contiene un valor DWORD y lo carga en un objeto de datos. El parámetro pdtobj es un puntero a la interfaz IDataObject del objeto de datos, cf es el valor de formato del Portapapeles y dw es el valor de datos.

STDAPI DataObj_SetDWORD(IDataObject *pdtobj, UINT cf, DWORD dw)
{
    FORMATETC fmte = {(CLIPFORMAT) cf, 
                      NULL, 
                      DVASPECT_CONTENT, 
                      -1, 
                      TYMED_HGLOBAL};
    STGMEDIUM medium;

    HRESULT hres = E_OUTOFMEMORY;
    DWORD *pdw = (DWORD *)GlobalAlloc(GPTR, sizeof(DWORD));
    
    if (pdw)
    {
        *pdw = dw;       
        medium.tymed = TYMED_HGLOBAL;
        medium.hGlobal = pdw;
        medium.pUnkForRelease = NULL;

        hres = pdtobj->SetData(&fmte, &medium, TRUE);
 
        if (FAILED(hres))
            GlobalFree((HGLOBAL)pdw);
    }
    return hres;
}

Implementación de IDataObject

IDataObject es una interfaz principal del objeto de datos. Todos los objetos de datos deben implementarlo. Lo usa tanto el origen como el destino para diversos propósitos, entre los que se incluyen:

  • Carga de datos en el objeto de datos.
  • Extracción de datos del objeto de datos.
  • Determinar qué tipos de datos se encuentran en el objeto de datos.
  • Proporcionar comentarios al objeto de datos sobre el resultado de la transferencia de datos.

IDataObject admite varios métodos. En esta sección se describe cómo implementar los tres métodos más importantes para objetos de datos de Shell, SetData, EnumFormatEtc y GetData. Para obtener una explicación de los otros métodos, consulte la referencia de IDataObject .

SetData (método)

La función principal del método IDataObject::SetData es permitir que el origen cargue datos en el objeto de datos. Para cada formato que se va a incluir, el origen crea una estructura FORMATETC para identificar el formato y una estructura STGMEDIUM para contener un puntero a los datos. A continuación, el origen llama al método IDataObject::SetData del objeto y pasa las estructuras FORMATETC y STGMEDIUM del objeto. El método debe almacenar esta información para que esté disponible cuando el destino llama a IDataObject::GetData para extraer datos del objeto.

Sin embargo, al transferir archivos, el Shell suele poner la información de cada archivo en un formato de CFSTR_FILECONTENTS independiente. Para distinguir los distintos archivos, el miembro lIndex de la estructura FORMATETC de cada archivo se establece en un valor de índice que identifica el archivo determinado. La implementación de IDataObject::SetData debe ser capaz de almacenar varios formatos de CFSTR_FILECONTENTS que solo difieren por sus miembros lIndex .

Mientras el cursor está sobre la ventana de destino, el destino puede usar el objeto auxiliar de arrastrar y colocar para especificar la imagen de arrastre. El objeto auxiliar de arrastrar y colocar llama a IDataObject::SetData para cargar formatos privados en el objeto de datos que se usa para la compatibilidad entre procesos. Para admitir el objeto auxiliar de arrastrar y colocar, la implementación de IDataObject::SetData debe ser capaz de aceptar y almacenar formatos privados arbitrarios.

Una vez eliminados los datos, algunos tipos de transferencia de datos de Shell requieren que el destino llame a IDataObject::SetData para proporcionar al objeto de datos información sobre el resultado de la operación de eliminación. Por ejemplo, al mover archivos con una operación de movimiento optimizada, el destino normalmente elimina los archivos originales, pero no es necesario hacerlo. El destino informa al objeto de datos si eliminó los archivos llamando a IDataObject::SetData con un formato de CFSTR_LOGICALPERFORMEDDROPEFFECT . Hay otros formatos del Portapapeles de Shell que también usa el destino para pasar información al objeto de datos. La implementación de IDataObject::SetData debe ser capaz de reconocer estos formatos y responder adecuadamente. Para obtener más información, consulte Control de escenarios de transferencia de datos de Shell.

Método EnumFormatEtc

Cuando el destino recibe un objeto de datos, normalmente llama a FORMATETC para determinar qué formato contiene el objeto. El método crea un objeto de enumeración OLE y devuelve un puntero a la interfaz IEnumFORMATETC del objeto. A continuación, el destino usa la interfaz para enumerar los formatos disponibles.

Un objeto de enumeración siempre debe enumerar los formatos disponibles en orden de calidad, empezando por lo mejor. La calidad relativa de los formatos se define mediante el origen de colocación. En general, los formatos de mayor calidad contienen los datos más ricos y completos. Por ejemplo, una imagen de color de 24 bits normalmente se consideraría de mayor calidad que una versión a escala gris de esa imagen. La razón para enumerar los formatos en orden de su calidad es que los destinos suelen enumerar hasta que obtienen un formato que admiten y, a continuación, usan ese formato para extraer los datos. Para que este procedimiento genere el mejor formato disponible que el destino pueda admitir, se deben enumerar los formatos en orden de su calidad.

Un objeto de enumeración para los datos de Shell se implementa de la misma manera que para otros tipos de transferencia de datos, con una excepción notable. Dado que los objetos de datos normalmente contienen solo un elemento de datos por formato, normalmente enumeran todos los formatos que se pasan a IDataObject::SetData. Sin embargo, como se describe en la sección Método SetData , los objetos de datos de Shell pueden contener varios formatos de CFSTR_FILECONTENTS .

Dado que el propósito de IDataObject::EnumFormatEtc es permitir que el destino determine qué tipos de datos están presentes, no es necesario enumerar más de un formato de CFSTR_FILECONTENTS . Si el destino necesita saber cuántos de estos formatos contiene el objeto de datos, el destino puede recuperar esa información del formato de CFSTR_FILEDESCRIPTOR adjunto. Para obtener más información sobre cómo implementar IDataObject::EnumFormatEtc, consulte la documentación de referencia del método.

GetData (método)

El destino llama a IDataObject::GetData para extraer un formato de datos determinado. El destino especifica el formato pasando la estructura FORMATETC adecuada. IDataObject::GetData devuelve la estructura STGMEDIUM del formato.

El destino puede establecer el miembro tymed de la estructura FORMATETC en un valor TYMED_XXX específico para especificar qué mecanismo de transferencia de datos usará para extraer los datos. Sin embargo, el destino también puede realizar una solicitud más genérica y permitir que el objeto de datos decida. Para pedir al objeto de datos que seleccione el mecanismo de transferencia de datos, el destino establece todos los valores TYMED_XXX que admite. IDataObject::GetData selecciona uno de estos mecanismos de transferencia de datos y devuelve la estructura STGMEDIUM adecuada. Por ejemplo, tymed se establece normalmente en TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE solicitar cualquiera de los tres mecanismos de transferencia de datos de Shell.

Nota

Dado que puede haber varios formatos de CFSTR_FILECONTENTS , los miembros cfFormat y tymed de la estructura FORMATETC no son suficientes para indicar qué estructura STGMEDIUMIDataObject::GetData debe devolver. Para el formato CFSTR_FILECONTENTS, IDataObject::GetData también debe examinar el miembro lIndex de la estructura FORMATETC para devolver la estructura STGMEDIUM correcta.

 

El formato CFSTR_INDRAGLOOP se coloca en objetos de datos para permitir que los destinos comprueben el estado del bucle de arrastrar y colocar, a la vez que se evita la representación intensiva de memoria de los datos del objeto. Los datos del formato son un valor DWORD que se establece en un valor distinto de cero si el objeto de datos está dentro de un bucle de arrastre. El valor de datos del formato se establece en cero si se han quitado los datos. Si un destino solicita este formato y no lo ha cargado el origen, IDataObject::GetData debe responder como si el origen hubiera cargado el formato con un valor de cero.

Mientras el cursor está sobre la ventana de destino, el destino puede usar el objeto auxiliar de arrastrar y colocar para especificar la imagen de arrastre. El objeto auxiliar de arrastrar y colocar llama a IDataObject::SetData para cargar formatos privados en el objeto de datos que se usa para la compatibilidad entre procesos. Más adelante llama a IDataObject::GetData para recuperarlos. Para admitir el objeto auxiliar de arrastrar y colocar, la implementación del objeto de datos de Shell debe poder devolver formatos privados arbitrarios cuando se solicitan.

Implementación de IDropSource

El origen debe crear un objeto que exponga una interfaz IDropSource . Esta interfaz permite al origen actualizar la imagen de arrastre que indica la posición actual del cursor y proporcionar comentarios al sistema sobre cómo finalizar una operación de arrastrar y colocar. IDropSource tiene dos métodos: GiveFeedback y QueryContinueDrag.

GiveFeedback (método)

Mientras se encuentra en el bucle de arrastre, un origen de colocación es responsable de realizar un seguimiento de la posición del cursor y mostrar una imagen de arrastre adecuada. Sin embargo, en algunos casos es posible que desee cambiar la apariencia de la imagen de arrastre cuando se encuentra sobre la ventana del destino de colocación.

Cuando el cursor entra o sale de la ventana de destino y mientras se mueve sobre la ventana de destino, el sistema llama periódicamente a la interfaz IDropTarget del destino. El destino responde con un valor DROPEFFECT que se reenvía al origen a través del método GiveFeedback . Si procede, el origen puede modificar la apariencia del cursor en función del valor DROPEFFECT . Para obtener más información, consulte las referencias de GiveFeedback y DoDragDrop .

QueryContinueDrag (método)

Se llama a este método si el botón del mouse o el estado del teclado cambian mientras el objeto de datos está en el bucle de arrastre. Notifica al origen si se ha presionado la tecla ESC y proporciona el estado actual de las teclas modificadoras del teclado, como CTRL o MAYÚS. El valor devuelto del método QueryContinueDrag especifica una de las tres acciones:

  • S_OK. Continuar con la operación de arrastrar
  • DRAGDROP_S_DROP. Quite los datos. A continuación, el sistema llama al método IDropTarget::D rop del destino.
  • DRAGDROP_S_CANCEL. Finalice el bucle de arrastre sin colocar los datos. Normalmente, este valor se devuelve si se presionó la tecla ESCAPE.

Para obtener más información, consulte las referencias QueryContinueDrag y DoDragDrop .

Cómo un destino controla un objeto de datos

El destino recibe un objeto de datos cuando recupera el objeto de datos del Portapapeles o lo ha quitado en la ventana de destino por el usuario. A continuación, el destino puede extraer datos del objeto de datos. Si es necesario, el destino también puede notificar al objeto de datos el resultado de la operación. Antes de una transferencia de datos de Shell, un destino de colocación debe prepararse para la operación:

  1. El destino debe llamar a RegisterClipboardFormat para obtener un valor de formato de Portapapeles válido para todos los formatos de Shell, que no sean CF_HDROP, que podrían incluirse en el objeto de datos. CF_HDROP ya es un formato de Portapapeles válido y no es necesario registrarse.
  2. Para admitir una operación de arrastrar y colocar, el destino debe implementar una interfaz IDropTarget y registrar una ventana de destino. Para registrar una ventana de destino, el destino llama a RegisterDragDrop y pasa el identificador de la ventana y el puntero de interfaz IDropTarget .

Para las transferencias del Portapapeles, el destino no recibe ninguna notificación de que se ha colocado un objeto de datos en el Portapapeles. Normalmente, se notifica a una aplicación que un objeto está en el Portapapeles mediante una acción de usuario, como hacer clic en el botón Pegar de la barra de herramientas de la aplicación. A continuación, el destino recupera el puntero IDataObject del objeto de datos desde el Portapapeles mediante una llamada a OleGetClipboard. Para las transferencias de datos de arrastrar y colocar, el sistema usa la interfaz IDropTarget del destino para proporcionar al destino información sobre el progreso de la transferencia de datos:

  • El sistema llama a IDropTarget::D ragEnter cuando el cursor entra en la ventana de destino.
  • El sistema llama periódicamente a IDropTarget::D ragOver a medida que el cursor pasa sobre la ventana de destino, para proporcionar al destino la posición actual del cursor.
  • El sistema llama a IDropTarget::D ragLeave cuando el cursor sale de la ventana de destino.
  • El sistema llama a IDropTarget::D rop cuando el usuario quita el objeto de datos en la ventana de destino.

Para obtener una explicación sobre cómo implementar estos métodos, consulte IDropTarget.

Cuando se quitan los datos, IDropTarget::D rop proporciona al destino un puntero a la interfaz IDataObject del objeto de datos. A continuación, el destino usa esta interfaz para extraer datos del objeto de datos.

Extracción de datos de Shell de un objeto de datos

Una vez que se ha quitado o recuperado un objeto de datos del Portapapeles, el destino puede extraer los datos que necesita. El primer paso del proceso de extracción suele ser enumerar los formatos contenidos por el objeto de datos:

  • Llame a IDataObject::EnumFormatEtc. El objeto de datos crea un objeto de enumeración OLE estándar y devuelve un puntero a su interfaz IEnumFORMATETC .
  • Use los métodos IEnumFORMATETC para enumerar los formatos contenidos por el objeto de datos. Esta operación normalmente recupera una estructura FORMATETC para cada formato que contiene el objeto. Sin embargo, el objeto de enumeración normalmente devuelve solo una estructura FORMATETC para el formato CFSTR_FILECONTENTS , independientemente del número de formatos que contenga el objeto de datos.
  • Seleccione uno o varios formatos que se van a extraer y almacene sus estructuras FORMATETC .

Para recuperar un formato determinado, pase la estructura FORMATETC asociada a IDataObject::GetData. Este método devuelve una estructura STGMEDIUM que proporciona acceso a los datos. Para especificar un mecanismo de transferencia de datos determinado, establezca el valor tymed de la estructura FORMATETC en el valor de TYMED_XXX correspondiente. Para pedir al objeto de datos que seleccione un mecanismo de transferencia de datos, el destino establece los valores TYMED_XXX para cada mecanismo de transferencia de datos que pueda controlar el destino. El objeto de datos selecciona uno de estos mecanismos de transferencia de datos y devuelve la estructura STGMEDIUM adecuada.

Para la mayoría de los formatos, el destino puede recuperar los datos pasando la estructura FORMATETC que recibió al enumerar los formatos disponibles. Una excepción a esta regla es CFSTR_FILECONTENTS. Dado que un objeto de datos puede contener varias instancias de este formato, es posible que la estructura FORMATETC devuelta por el enumerador no corresponda al formato determinado que desea extraer. Además de especificar los miembros cfFormat y tymed , también debe establecer el miembro lIndex en el valor de índice del archivo. Para obtener más información, consulte la sección Uso del formato de CFSTR_FILECONTENTS para extraer datos de un archivo de control de escenarios de transferencia de datos de Shell.

El proceso de extracción de datos depende del tipo de puntero contenido por la estructura STGMEDIUM devuelta. Si la estructura contiene un puntero a una interfaz IStream o IStorage , use los métodos de interfaz para extraer los datos. El proceso de extracción de datos de un objeto de memoria global se describe en la sección siguiente.

Extracción de un objeto de memoria global de un objeto de datos

Muchos de los formatos de datos de Shell están en forma de un objeto de memoria global. Use el procedimiento siguiente para extraer un formato que contenga un objeto de memoria global de un objeto de datos y asignar sus datos a una variable local:

  1. Cree una estructura FORMATETC . Establezca el miembro cfFormat en el valor de formato del Portapapeles adecuado y el miembro tymed en TYMED_HGLOBAL.

  2. Cree una estructura STGMEDIUM vacía.

  3. Llame a IDataObject::GetData y pase punteros a las estructuras FORMATETC y STGMEDIUM .

    Cuando se devuelve IDataObject::GetData , la estructura STGMEDIUM contendrá un puntero al objeto de memoria global que contiene los datos.

  4. Asigne los datos a una variable local llamando a GlobalLock y pasando el miembro hGlobal de la estructura STGMEDIUM .

  5. Llame a GlobalUnlock para liberar el bloqueo en el objeto de memoria global.

  6. Llame a ReleaseStgMedium para liberar el objeto de memoria global.

Nota

Debe usar ReleaseStgMedium para liberar el objeto de memoria global, no GlobalFree.

 

En el ejemplo siguiente se muestra cómo extraer un valor DWORD almacenado como un objeto de memoria global de un objeto de datos. El parámetro pdtobj es un puntero a la interfaz IDataObject del objeto de datos, cf es el formato del Portapapeles que identifica los datos deseados y pdwOut se usa para devolver el valor de datos.

STDAPI DataObj_GetDWORD(IDataObject *pdtobj, UINT cf, DWORD *pdwOut)
{    STGMEDIUM medium;
   FORMATETC fmte = {(CLIPFORMAT) cf, NULL, DVASPECT_CONTENT, -1, 
       TYMED_HGLOBAL};
    HRESULT hres = pdtobj->GetData(&fmte, &medium);
    if (SUCCEEDED(hres))
   {
       DWORD *pdw = (DWORD *)GlobalLock(medium.hGlobal);
       if (pdw)
       {
           *pdwOut = *pdw;
           GlobalUnlock(medium.hGlobal);
       }
       else
       {
           hres = E_UNEXPECTED;
       }
       ReleaseStgMedium(&medium);
   }
   return hres;
}

Implementación de IDropTarget

El sistema usa la interfaz IDropTarget para comunicarse con el destino mientras el cursor está sobre la ventana de destino. Las respuestas del destino se reenvía al origen a través de su interfaz IDropSource . En función de la respuesta, el origen puede modificar el icono que representa los datos. Si el destino de colocación necesita especificar el icono de datos, puede hacerlo mediante la creación de un objeto auxiliar de arrastrar y colocar.

Con las operaciones convencionales de arrastrar y colocar, el destino informa al objeto de datos del resultado de la operación estableciendo el parámetro pdwEffect de IDropTarget::D rop al valor DROPEFFECT adecuado. Con los objetos de datos de Shell, es posible que el destino también tenga que llamar a IDataObject::SetData. Para obtener información sobre cómo los destinos deben responder para diferentes escenarios de transferencia de datos, consulte Control de escenarios de transferencia de datos de Shell.

En las secciones siguientes se describe brevemente cómo implementar los métodos IDropTarget::D ragEnter, IDropTarget::D ragOver e IDropTarget::D rop . Para obtener más información, consulte la documentación de referencia.

Método DragEnter

El sistema llama al método IDropTarget::D ragEnter cuando el cursor entra en la ventana de destino. Sus parámetros proporcionan al destino la ubicación del cursor, el estado de las teclas modificadoras de teclado, como la tecla CTRL, y un puntero a la interfaz IDataObject del objeto de datos. El destino es responsable de usar esa interfaz para determinar si puede aceptar cualquiera de los formatos contenidos en el objeto de datos. Si es posible, normalmente deja el valor de pdwEffect sin cambios. Si no puede aceptar ningún dato del objeto de datos, establece el parámetro pdwEffect en DROPEFFECT_NONE. El sistema pasa el valor de este parámetro a la interfaz IDropSource del objeto de datos para permitirle mostrar la imagen de arrastre adecuada.

Los destinos no deben usar el método IDataObject::GetData para representar los datos de Shell antes de que se hayan quitado. La representación completa de los datos del objeto para cada repetición puede provocar que el cursor de arrastre se detenga. Para evitar este problema, algunos objetos shell contienen un formato CFSTR_INDRAGLOOP . Al extraer este formato, los destinos pueden comprobar el estado del bucle de arrastre y evitar la representación intensiva de memoria de los datos del objeto. El valor de datos del formato es un DWORD que se establece en un valor distinto de cero si el objeto de datos está dentro de un bucle de arrastre. El valor de datos del formato se establece en cero si se han quitado los datos.

Si el destino puede aceptar datos del objeto de datos, debe examinar grfKeyState para determinar si se ha presionado alguna tecla modificadora para modificar el comportamiento de colocación normal. Por ejemplo, la operación predeterminada suele ser un movimiento, pero la depresión de la tecla CTRL suele indicar una operación de copia.

Mientras el cursor se encuentra sobre la ventana de destino, el destino puede usar el objeto auxiliar de arrastrar y colocar para reemplazar la imagen de arrastre del objeto de datos por su propia. Si es así, IDropTarget::D ragEnter debe llamar a IDropTargetHelper::D ragEnter para pasar la información contenida en los parámetros DragEnter al objeto auxiliar de arrastrar y colocar.

Método DragOver

A medida que el cursor se mueve dentro de la ventana de destino, el sistema llama periódicamente al método IDropTarget::D ragOver . Sus parámetros proporcionan al destino la ubicación del cursor y el estado de las teclas modificadoras de teclado, como la tecla CTRL. IDropTarget::D ragOver tiene muchas las mismas responsabilidades que IDropTarget::D ragEnter y las implementaciones suelen ser muy similares.

Si el destino usa el objeto auxiliar de arrastrar y colocar, IDropTarget::D ragOver debe llamar a IDropTargetHelper::D ragOver para reenviar la información contenida en los parámetros dragOver al objeto auxiliar de arrastrar y colocar.

Método Drop

El sistema llama al método IDropTarget::D rop para notificar al destino que el usuario ha quitado los datos, normalmente soltando el botón del mouse. IDropTarget::D rop tiene los mismos parámetros que IDropTarget::D ragEnter. Normalmente, el destino responde extrayendo uno o varios formatos del objeto de datos. Cuando termine, el destino debe establecer el parámetro pdwEffect en un valor DROPEFFECT que indica el resultado de la operación. Para algunos tipos de transferencia de datos de Shell, el destino también debe llamar a IDataObject::SetData para pasar un formato con información adicional sobre el resultado de la operación al objeto de datos. Para obtener una explicación detallada, consulte Control de escenarios de transferencia de datos de Shell.

Si el destino usa el objeto auxiliar de arrastrar y colocar, IDropTarget::D rop debe llamar a IDropTargetHelper::D rop para reenviar la información contenida en los parámetros IDropTargetHelper::D ragOver al objeto auxiliar de arrastrar y colocar.

Usar el objeto auxiliar de arrastrar y colocar

El shell exporta el objeto auxiliar de arrastrar y colocar (CLSID_DragDropHelper) para permitir que los destinos especifiquen la imagen de arrastre mientras se encuentra sobre la ventana de destino. Para usar el objeto auxiliar de arrastrar y colocar, cree un objeto de servidor en proceso llamando a CoCreateInstance con un identificador de clase (CLSID) de CLSID_DragDropHelper. El objeto auxiliar de arrastrar y colocar expone dos interfaces que se usan de la siguiente manera:

  • La interfaz IDragSourceHelper permite que el destino de colocación especifique un icono para representar el objeto de datos.
  • La interfaz IDropTargetHelper permite que el destino de colocación informe al objeto auxiliar de arrastrar y colocar de la ubicación del cursor y mostrar u ocultar el icono de datos.

Uso de la interfaz IDragSourceHelper

La interfaz IDragSourceHelper se expone mediante el objeto auxiliar de arrastrar y colocar para permitir que un destino de colocación proporcione la imagen que se mostrará mientras el cursor se encuentra sobre la ventana de destino. IDragSourceHelper proporciona dos maneras alternativas de especificar el mapa de bits que se usará como una imagen de arrastre:

  • Los destinos de colocación que tienen una ventana pueden registrar un mensaje de ventana de DI_GETDRAGIMAGE para ello inicializando el objeto auxiliar de arrastrar y colocar con IDragSourceHelper::InitializeFromWindow. Cuando el destino recibe un mensaje de DI_GETDRAGIMAGE, el controlador coloca la información del mapa de bits de la imagen de arrastre en la estructura SHDRAGIMAGE que se pasa como el valor lParam del mensaje.
  • Los destinos de colocación sin ventana especifican un mapa de bits cuando inicializan el objeto auxiliar de arrastrar y colocar con IDragSourceHelper::InitializeFromBitmap.

Usar la interfaz IDropTargetHelper

Esta interfaz permite que el destino de colocación notifique al objeto auxiliar de arrastrar y colocar cuando el cursor entra o sale del destino. Mientras el cursor se encuentra sobre la ventana de destino, IDropTargetHelper permite al destino proporcionar al objeto auxiliar de arrastrar y colocar la información que el destino recibe a través de su interfaz IDropTarget .

Cuatro de los métodos IDropTargetHelper:IDropTargetHelper::D ragEnter, IDropTargetHelper::D ragLeave, IDropTargetHelper::D ragOver e IDropTargetHelper::D rop están asociados con el método IDropTarget del mismo nombre. Para usar el objeto auxiliar de arrastrar y colocar, cada uno de los métodos IDropTarget debe llamar al método IDropTargetHelper correspondiente para reenviar la información al objeto auxiliar de arrastrar y colocar. El quinto método IDropTargetHelper , IDropTargetHelper::Show, notifica al objeto auxiliar de arrastrar y colocar para mostrar u ocultar la imagen de arrastre. Este método se usa al arrastrar sobre una ventana de destino en un modo de vídeo de profundidad de color bajo. Permite que el destino oculte la imagen de arrastre mientras pinta la ventana.