Objeto de dados do Shell
O objeto de dados é central para todas as transferências de dados do Shell. É principalmente um contêiner para manter os dados transferidos. No entanto, o destino também pode se comunicar com o objeto de dados para facilitar alguns tipos especializados de transferência de dados do Shell, como movimentações otimizadas. Este tópico fornece uma discussão geral sobre como os objetos de dados do Shell funcionam, como eles são construídos por uma fonte e como eles são tratados por um destino. Para obter uma discussão detalhada sobre como usar objetos de dados para transferir diferentes tipos de dados do Shell, consulte Manipulando cenários de transferência de dados do Shell.
- Como os objetos de dados funcionam
- Como uma fonte cria um objeto de dados
- Como um destino manipula um objeto de dados
- Usando o objeto Auxiliar De Arrastar e Soltar
Como os objetos de dados funcionam
Os objetos de dados são objetos COM (Component Object Model), criados pela fonte de dados para transferir dados para um destino. Normalmente, eles carregam mais de um item de dados. Há dois motivos para essa prática:
- Embora quase qualquer tipo de dados possa ser transferido com um objeto de dados, a fonte normalmente não sabe que tipo de dados o destino pode aceitar. Por exemplo, os dados podem ser uma parte de um documento de texto formatado. Embora o destino possa ser capaz de lidar com informações de formatação complexas, ele também pode ser capaz de aceitar apenas texto ANSI. Por esse motivo, os objetos de dados geralmente incluem os mesmos dados em vários formatos diferentes. Em seguida, o destino pode extrair os dados em um formato que ele pode manipular.
- Os objetos de dados também podem conter itens de dados auxiliares que não são versões de dados de origem. Esse tipo de item de dados normalmente fornece informações adicionais sobre a operação de transferência de dados. Por exemplo, o Shell usa itens de dados auxiliares para indicar se um arquivo deve ser copiado ou movido.
Formatos de área de transferência
Cada item de dados em um objeto de dados tem um formato associado, geralmente chamado de formato de área de transferência. Há vários formatos de área de transferência padrão, declarados em Winuser.h, que correspondem a tipos de dados comumente usados. Os formatos da área de transferência são inteiros, mas normalmente são referenciados pelo nome equivalente, que tem o formulário CF_XXX. Por exemplo, o formato da área de transferência para texto ANSI é CF_TEXT.
Os aplicativos podem estender o intervalo de formatos de área de transferência disponíveis definindo formatos privados. Para definir um formato privado, um aplicativo chama RegisterClipboardFormat com uma cadeia de caracteres que identifica o formato. O inteiro sem sinal que a função retorna é um valor de formato válido que pode ser usado como um formato de área de transferência padrão. No entanto, tanto a origem quanto o destino devem registrar o formato para usá-lo. Com uma exceção, CF_HDROP, os formatos de área de transferência usados para transferir dados do Shell são definidos como formatos privados. Eles devem ser registrados pela origem e pelo destino antes que possam ser usados. Para obter uma descrição dos formatos de área de transferência do Shell disponíveis, consulte Formatos de área de transferência do Shell.
Embora haja algumas exceções, os objetos de dados normalmente contêm apenas um item de dados para cada formato de área de transferência compatível. Essa correlação um-para-um entre formato e dados permite que o valor de formato seja usado como um identificador para o item de dados associado. Na verdade, ao discutir o conteúdo de um objeto de dados, um determinado item de dados normalmente é chamado de "formato" e é referenciado pelo nome de formato. Por exemplo, frases como "Extrair o formato CF_TEXT..." normalmente são usados ao discutir o item de dados de texto ANSI de um objeto de dados.
Quando o destino de soltar recebe o ponteiro para o objeto de dados, o destino de soltar enumera os formatos disponíveis para determinar quais tipos de dados estão disponíveis. Em seguida, ele solicita um ou mais dos formatos disponíveis e extrai os dados. A maneira específica que o destino extrai dados do Shell de um objeto de dados varia de acordo com o formato; isso é discutido em detalhes em Como um destino manipula um objeto de dados.
Com transferências de dados de área de transferência simples, os dados são colocados em um objeto de memória global. O endereço desse objeto é colocado na Área de Transferência, juntamente com seu formato. O formato da área de transferência informa ao destino que tipo de dados ele encontrará no endereço associado. Embora transferências simples da área de transferência sejam fáceis de implementar:
- Os objetos de dados fornecem uma maneira muito mais flexível de transferir dados.
- Os objetos de dados são mais adequados para transferir grandes quantidades de dados.
- Os objetos de dados devem ser usados para transferir dados com uma operação de arrastar e soltar.
Por esses motivos, todas as transferências de dados do Shell usam objetos de dados. Com objetos de dados, os formatos da área de transferência não são usados diretamente. Em vez disso, os itens de dados são identificados com uma generalização do formato da área de transferência, uma estrutura FORMATETC .
Estrutura FORMATETC
A estrutura FORMATETC é uma versão estendida de um formato de área de transferência. Conforme usado para transferências de dados do Shell, a estrutura FORMATETC tem as seguintes características:
Um item de dados ainda é identificado por seu formato de área de transferência, no membro cfFormat .
A transferência de dados não se limita a objetos de memória global. O membro tymed é usado para indicar o mecanismo de transferência de dados contido na estrutura STGMEDIUM associada. Ele é definido como um dos valores TYMED_XXX .
O Shell usa o membro lIndex com seu formato CFSTR_FILECONTENTS para permitir que um objeto de dados contenha mais de um item de dados por formato. Para obter uma discussão sobre como usar esse formato, consulte a seção Usando o formato CFSTR_FILECONTENTS para extrair dados de um arquivo de manipulação de cenários de transferência de dados do Shell.
O membro dwAspect normalmente é definido como DVASPECT_CONTENT. No entanto, há três valores definidos em Shlobj.h que podem ser usados para transferência de dados do Shell.
Valor Descrição DVASPECT_COPY Usado para indicar que o formato representa uma cópia dos dados. DVASPECT_LINK Usado para indicar que o formato representa um atalho para os dados. DVASPECT_SHORTNAME Usado com o formato CF_HDROP para solicitar um caminho de arquivo com os nomes reduzidos para o formato 8.3. O membro ptd não é usado para transferências de dados do Shell e normalmente é definido como NULL.
Estrutura STGMEDIUM
A estrutura STGMEDIUM fornece acesso aos dados que estão sendo transferidos. Há suporte para três mecanismos de transferência de dados para dados do Shell:
O membro tymed da estrutura STGMEDIUM é um valor TYMED_XXX que identifica o mecanismo de transferência de dados. O segundo membro é um ponteiro usado pelo destino para extrair os dados. O ponteiro pode ser um de uma variedade de tipos, dependendo do valor tymed . Os três valores tymed usados para transferências de dados do Shell são resumidos na tabela a seguir, juntamente com seu nome de membro STGMEDIUM correspondente.
Valor de tymed | Nome do membro | Descrição |
---|---|---|
TYMED_HGLOBAL | hGlobal | Um ponteiro para um objeto de memória global. Esse tipo de ponteiro normalmente é usado para transferir pequenas quantidades de dados. Por exemplo, o Shell usa objetos de memória global para transferir cadeias de caracteres de texto curtas, como nomes de arquivo ou URLs. |
TYMED_ISTREAM | pstm | Um ponteiro para uma interface IStream . Esse tipo de ponteiro é preferencial para a maioria das transferências de dados do Shell porque requer relativamente pouca memória em comparação com TYMED_HGLOBAL. Além disso, o mecanismo de transferência de dados TYMED_ISTREAM não exige que a origem armazene seus dados de nenhuma maneira específica. |
TYMED_ISTORAGE | pstg | Um ponteiro para uma interface IStorage . O destino chama os métodos de interface para extrair os dados. Assim como TYMED_ISTREAM, esse tipo de ponteiro requer relativamente pouca memória. No entanto, como TYMED_ISTORAGE é menos flexível do que TYMED_ISTREAM, ela não é tão usada com frequência. |
Como uma fonte cria um objeto de dados
Quando um usuário inicia uma transferência de dados do Shell, a fonte é responsável por criar um objeto de dados e carregá-lo com dados. O procedimento a seguir resume o processo:
- Chame RegisterClipboardFormat para obter um valor de formato de área de transferência válido para cada formato shell que será incluído no objeto de dados. Lembre-se de que CF_HDROP já é um formato de área de transferência válido e não precisa ser registrado.
- Para que cada formato seja transferido, coloque os dados associados em um objeto de memória global ou crie um objeto que forneça acesso a esses dados por meio de uma interface IStream ou IStorage . As interfaces IStream e IStorage são criadas usando técnicas COM padrão. Para obter uma discussão sobre como lidar com objetos de memória global, consulte Como adicionar um objeto de memória global a um objeto de dados.
- Crie estruturas FORMATETC e STGMEDIUM para cada formato.
- Instanciar um objeto de dados.
- Carregue os dados no objeto de dados chamando o método IDataObject::SetData para cada formato com suporte e passando as estruturas FORMATETC e STGMEDIUM do formato.
- Com transferências de dados da área de transferência, chame OleSetClipboard para colocar um ponteiro para a interface IDataObject do objeto de dados na área de transferência. Para transferências de arrastar e soltar, inicie um loop de arrastar chamando DoDragDrop. O ponteiro IDataObject será passado para o destino de soltar quando os dados forem descartados, encerrando o loop de arrastar.
O objeto de dados agora está pronto para ser transferido para o destino. Para transferências de dados da área de transferência, o objeto é simplesmente mantido até que o destino o solicite chamando OleGetClipboard. Para transferências de dados de arrastar e soltar, o objeto de dados é responsável por criar um ícone para representar os dados e movê-los à medida que o usuário move o cursor. Enquanto o objeto está no loop de arrastar, a origem recebe status informações por meio de sua interface IDropSource. Para obter mais discussões, consulte Implementando o IDropSource.
A fonte não receberá nenhuma notificação se o objeto de dados for recuperado da Área de Transferência por um destino. Quando um objeto é descartado em um destino por uma operação de arrastar e soltar, a função DoDragDrop que foi chamada para iniciar o loop de arrastar retornará.
Como adicionar um objeto de memória global a um objeto de dados
Muitos dos formatos de dados do Shell estão na forma de um objeto de memória global. Use o procedimento a seguir para criar um formato que contém um objeto de memória global e carregá-lo no objeto de dados:
- Crie uma estrutura FORMATETC . Defina o membro cfFormat como o valor de formato da área de transferência apropriado e o membro tymed como TYMED_HGLOBAL.
- Crie uma estrutura STGMEDIUM . Defina o membro tymed como TYMED_HGLOBAL.
- Crie um objeto de memória global chamando GlobalAlloc para alocar um bloco de memória de tamanho adequado.
- Atribua o bloco de dados a ser transferido para o endereço retornado por GlobalAlloc.
- Atribua o endereço do objeto de memória global ao membro hGlobal da estrutura STGMEDIUM .
- Carregue o formato no objeto de dados chamando IDataObject::SetData e passando as estruturas FORMATETC e STGMEDIUM criadas nas etapas anteriores.
A função de exemplo a seguir cria um objeto de memória global que contém um valor DWORD e o carrega em um objeto de dados. O parâmetro pdtobj é um ponteiro para a interface IDataObject do objeto de dados, cf é o valor de formato da área de transferência e dw é o valor de dados.
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;
}
Implementando IDataObject
IDataObject é a interface primária de um objeto de dados. Ele deve ser implementado por todos os objetos de dados. Ele é usado pela origem e pelo destino para uma variedade de finalidades, incluindo:
- Carregando dados no objeto de dados.
- Extraindo dados do objeto de dados.
- Determinando quais tipos de dados estão no objeto de dados.
- Fornecer comentários ao objeto de dados sobre o resultado da transferência de dados.
IDataObject dá suporte a vários métodos. Esta seção discute como implementar os três métodos mais importantes para objetos de dados do Shell, SetData, EnumFormatEtc e GetData. Para obter uma discussão sobre os outros métodos, consulte a referência IDataObject .
Método SetData
A função primária do método IDataObject::SetData é permitir que a origem carregue dados no objeto de dados. Para cada formato a ser incluído, a origem cria uma estrutura FORMATETC para identificar o formato e uma estrutura STGMEDIUM para manter um ponteiro para os dados. Em seguida, a origem chama o método IDataObject::SetData do objeto e passa as estruturas FORMATETC e STGMEDIUM do formato. O método deve armazenar essas informações para que elas fiquem disponíveis quando o destino chamar IDataObject::GetData para extrair dados do objeto.
No entanto, ao transferir arquivos, o Shell geralmente coloca as informações para cada arquivo a ser transferido para um formato de CFSTR_FILECONTENTS separado. Para distinguir os diferentes arquivos, o membro lIndex da estrutura FORMATETC de cada arquivo é definido como um valor de índice que identifica o arquivo específico. Sua implementação IDataObject::SetData deve ser capaz de armazenar vários formatos de CFSTR_FILECONTENTS que diferem apenas por seus membros lIndex .
Enquanto o cursor estiver sobre a janela de destino, o destino pode usar o objeto auxiliar de arrastar e soltar para especificar a imagem de arrastar. O objeto auxiliar de arrastar e soltar chama IDataObject::SetData para carregar formatos privados no objeto de dados que são usados para suporte entre processos. Para dar suporte ao objeto auxiliar de arrastar e soltar, sua implementação IDataObject::SetData deve ser capaz de aceitar e armazenar formatos privados arbitrários.
Depois que os dados forem removidos, alguns tipos de transferência de dados do Shell exigem que o destino chame IDataObject::SetData para fornecer ao objeto de dados informações sobre o resultado da operação de remoção. Por exemplo, ao mover arquivos com uma operação de movimentação otimizada, o destino normalmente exclui os arquivos originais, mas não é necessário fazê-lo. O destino informa ao objeto de dados se ele excluiu os arquivos chamando IDataObject::SetData com um formato CFSTR_LOGICALPERFORMEDDROPEFFECT . Há vários outros formatos de área de transferência do Shell que também são usados pelo destino para passar informações para o objeto de dados. Sua implementação IDataObject::SetData deve ser capaz de reconhecer esses formatos e responder adequadamente. Para obter mais discussões, consulte Manipulando cenários de transferência de dados do Shell.
Método EnumFormatEtc
Quando o destino recebe um objeto de dados, ele normalmente chama FORMATETC para determinar quais formatos o objeto contém. O método cria um objeto de enumeração OLE e retorna um ponteiro para a interface IEnumFORMATETC do objeto. Em seguida, o destino usa a interface para enumerar os formatos disponíveis.
Um objeto de enumeração sempre deve enumerar os formatos disponíveis em ordem de qualidade, começando com o melhor. A qualidade relativa dos formatos é definida pela fonte de remoção. Em geral, os formatos de maior qualidade contêm os dados mais avançados e completos. Por exemplo, uma imagem colorida de 24 bits normalmente seria considerada de qualidade mais alta do que uma versão em escala de cinza dessa imagem. O motivo para enumerar formatos em ordem de qualidade é que os destinos normalmente enumeram até chegarem a um formato compatível e usem esse formato para extrair os dados. Para que este procedimento produza o melhor formato disponível ao qual o destino pode dar suporte, os formatos devem ser enumerados em ordem de qualidade.
Um objeto de enumeração para dados do Shell é implementado da mesma forma que para outros tipos de transferência de dados, com uma exceção notável. Como os objetos de dados normalmente contêm apenas um item de dados por formato, eles normalmente enumeram todos os formatos que são passados para IDataObject::SetData. No entanto, conforme discutido na seção método SetData , os objetos de dados shell podem conter vários formatos de CFSTR_FILECONTENTS .
Como a finalidade de IDataObject::EnumFormatEtc é permitir que o destino determine quais tipos de dados estão presentes, não é necessário enumerar mais de um formato CFSTR_FILECONTENTS . Se o destino precisar saber quantos desses formatos o objeto de dados contém, o destino poderá recuperar essas informações do formato de CFSTR_FILEDESCRIPTOR. Para obter mais discussões sobre como implementar IDataObject::EnumFormatEtc, consulte a documentação de referência do método.
Método GetData
O destino chama IDataObject::GetData para extrair um formato de dados específico. O destino especifica o formato passando a estrutura FORMATETC apropriada. IDataObject::GetData retorna a estrutura STGMEDIUM do formato.
O destino pode definir o membro tymed da estrutura FORMATETC para um valor específico TYMED_XXX para especificar qual mecanismo de transferência de dados ele usará para extrair os dados. No entanto, o destino também pode fazer uma solicitação mais genérica e permitir que o objeto de dados decida. Para solicitar que o objeto de dados selecione o mecanismo de transferência de dados, o destino define todos os valores TYMED_XXX aos quais ele dá suporte. IDataObject::GetData seleciona um desses mecanismos de transferência de dados e retorna a estrutura STGMEDIUM apropriada. Por exemplo, tymed normalmente é definido como TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE solicitar qualquer um dos três mecanismos de transferência de dados do Shell.
Observação
Como pode haver vários formatos CFSTR_FILECONTENTS , os membros cfFormat e tymed da estrutura FORMATETC não são suficientes para indicar qual estrutura STGMEDIUMIDataObject::GetData deve retornar. Para o formato CFSTR_FILECONTENTS, IDataObject::GetData também deve examinar o membro lIndex da estrutura FORMATETC para retornar a estrutura STGMEDIUM correta.
O formato CFSTR_INDRAGLOOP é colocado em objetos de dados para permitir que os destinos marcar o status do loop de arrastar e soltar, evitando a renderização intensiva de memória dos dados do objeto. Os dados do formato são um valor DWORD definido como um valor diferente de zero se o objeto de dados estiver dentro de um loop de arrastar. O valor de dados do formato será definido como zero se os dados tiverem sido descartados. Se um destino solicitar esse formato e não tiver sido carregado pela origem, IDataObject::GetData deverá responder como se a origem tivesse carregado o formato com um valor zero.
Enquanto o cursor estiver sobre a janela de destino, o destino pode usar o objeto auxiliar de arrastar e soltar para especificar a imagem de arrastar. O objeto auxiliar de arrastar e soltar chama IDataObject::SetData para carregar formatos privados no objeto de dados que são usados para suporte entre processos. Mais tarde, ele chama IDataObject::GetData para recuperá-los. Para dar suporte ao objeto auxiliar de arrastar e soltar, a implementação do Objeto de Dados do Shell deve ser capaz de retornar formatos privados arbitrários quando solicitados.
Implementando iDropSource
A origem deve criar um objeto que exponha uma interface IDropSource . Essa interface permite que a origem atualize a imagem de arrastar que indica a posição atual do cursor e forneça comentários ao sistema sobre como encerrar uma operação de arrastar e soltar. IDropSource tem dois métodos: GiveFeedback e QueryContinueDrag.
Método GiveFeedback
Enquanto estiver no loop de arrastar, uma fonte de soltar é responsável por manter o controle da posição do cursor e exibir uma imagem de arrastar apropriada. No entanto, em alguns casos, talvez você queira alterar a aparência da imagem de arrastar quando ela estiver sobre a janela do destino de soltar.
Quando o cursor entra ou sai da janela de destino e enquanto ele está se movendo pela janela de destino, o sistema chama periodicamente a interface IDropTarget do destino. O destino responde com um valor DROPEFFECT que é encaminhado para a origem por meio do método GiveFeedback . Se apropriado, a origem pode modificar a aparência do cursor com base no valor DROPEFFECT . Para obter mais detalhes, consulte as referências GiveFeedback e DoDragDrop .
Método QueryContinueDrag
Esse método será chamado se o botão do mouse ou o estado do teclado for alterado enquanto o objeto de dados estiver no loop de arrastar. Ele notifica a origem se a tecla ESC foi pressionada e fornece o estado atual das teclas modificadoras de teclado, como CTRL ou SHIFT. O valor retornado do método QueryContinueDrag especifica uma das três ações:
- S_OK. Continuar a operação de arrastar
- DRAGDROP_S_DROP. Remova os dados. Em seguida, o sistema chama o método IDropTarget::D rop do destino.
- DRAGDROP_S_CANCEL. Encerre o loop de arrastar sem soltar os dados. Esse valor normalmente será retornado se a tecla ESCAPE tiver sido pressionada.
Para obter mais discussões, consulte as referências QueryContinueDrag e DoDragDrop .
Como um destino manipula um objeto de dados
O destino recebe um objeto de dados quando recupera o objeto de dados da Área de Transferência ou o solta na janela de destino pelo usuário. Em seguida, o destino pode extrair dados do objeto de dados. Se necessário, o destino também pode notificar o objeto de dados do resultado da operação. Antes de uma transferência de dados do Shell, um destino de descarte deve se preparar para a operação:
- O destino deve chamar RegisterClipboardFormat para obter um valor de formato de área de transferência válido para todos os formatos de Shell, exceto CF_HDROP, que podem ser incluídos no objeto de dados. CF_HDROP já é um formato de área de transferência válido e não precisa ser registrado.
- Para dar suporte a uma operação de arrastar e soltar, o destino deve implementar uma interface IDropTarget e registrar uma janela de destino. Para registrar uma janela de destino, o destino chama RegisterDragDrop e passa o identificador da janela e o ponteiro da interface IDropTarget .
Para transferências da área de transferência, o destino não recebe nenhuma notificação de que um objeto de dados foi colocado na Área de Transferência. Normalmente, um aplicativo é notificado de que um objeto está na Área de Transferência por uma ação do usuário, como clicar no botão Colar na barra de ferramentas do aplicativo. Em seguida, o destino recupera o ponteiro IDataObject do objeto de dados da Área de Transferência chamando OleGetClipboard. Para transferências de dados de arrastar e soltar, o sistema usa a interface IDropTarget do destino para fornecer ao destino informações sobre o progresso da transferência de dados:
- O sistema chama IDropTarget::D ragEnter quando o cursor entra na janela de destino.
- O sistema chama periodicamente IDropTarget::D ragOver à medida que o cursor passa pela janela de destino, para dar ao destino a posição atual do cursor.
- O sistema chama IDropTarget::D ragLeave quando o cursor sai da janela de destino.
- O sistema chama IDropTarget::D rop quando o usuário descarta o objeto de dados na janela de destino.
Para obter uma discussão sobre como implementar esses métodos, consulte IDropTarget.
Quando os dados são descartados, IDropTarget::D rop fornece ao destino um ponteiro para a interface IDataObject do objeto de dados. Em seguida, o destino usa essa interface para extrair dados do objeto de dados.
Extraindo dados do shell de um objeto de dados
Depois que um objeto de dados for removido ou recuperado da Área de Transferência, o destino poderá extrair os dados necessários. A primeira etapa no processo de extração normalmente é enumerar os formatos contidos pelo objeto de dados:
- Chame IDataObject::EnumFormatEtc. O objeto de dados cria um objeto de enumeração OLE padrão e retorna um ponteiro para sua interface IEnumFORMATETC .
- Use os métodos IEnumFORMATETC para enumerar os formatos contidos pelo objeto de dados. Essa operação geralmente recupera uma estrutura FORMATETC para cada formato que o objeto contém. No entanto, o objeto de enumeração normalmente retorna apenas uma única estrutura FORMATETC para o formato CFSTR_FILECONTENTS , independentemente de quantos desses formatos estão contidos no objeto de dados.
- Selecione um ou mais formatos a serem extraídos e armazene suas estruturas FORMATETC .
Para recuperar um formato específico, passe a estrutura FORMATETC associada para IDataObject::GetData. Esse método retorna uma estrutura STGMEDIUM que fornece acesso aos dados. Para especificar um mecanismo de transferência de dados específico, defina o valor tymed da estrutura FORMATETC com o valor TYMED_XXX correspondente. Para solicitar que o objeto de dados selecione um mecanismo de transferência de dados, o destino define os valores TYMED_XXX para cada mecanismo de transferência de dados que o destino pode manipular. O objeto de dados seleciona um desses mecanismos de transferência de dados e retorna a estrutura STGMEDIUM apropriada.
Para a maioria dos formatos, o destino pode recuperar os dados passando a estrutura FORMATETC que recebeu quando enumerou os formatos disponíveis. Uma exceção a essa regra é CFSTR_FILECONTENTS. Como um objeto de dados pode conter várias instâncias desse formato, a estrutura FORMATETC retornada pelo enumerador pode não corresponder ao formato específico que você deseja extrair. Além de especificar os membros cfFormat e tymed , você também deve definir o membro lIndex como o valor de índice do arquivo. Para mais discussões, consulte a seção Usando o formato CFSTR_FILECONTENTS para extrair dados de um arquivo de manipulação de cenários de transferência de dados do Shell
O processo de extração de dados depende do tipo de ponteiro contido pela estrutura STGMEDIUM retornada. Se a estrutura contiver um ponteiro para uma interface IStream ou IStorage , use os métodos de interface para extrair os dados. O processo de extração de dados de um objeto de memória global é discutido na próxima seção.
Extraindo um objeto de memória global de um objeto de dados
Muitos dos formatos de dados do Shell estão na forma de um objeto de memória global. Use o procedimento a seguir para extrair um formato que contém um objeto de memória global de um objeto de dados e atribuir seus dados a uma variável local:
Crie uma estrutura FORMATETC . Defina o membro cfFormat como o valor de formato da área de transferência apropriado e o membro tymed como TYMED_HGLOBAL.
Crie uma estrutura STGMEDIUM vazia.
Chame IDataObject::GetData e passe ponteiros para as estruturas FORMATETC e STGMEDIUM .
Quando IDataObject::GetData retornar, a estrutura STGMEDIUM conterá um ponteiro para o objeto de memória global que contém os dados.
Atribua os dados a uma variável local chamando GlobalLock e passando o membro hGlobal da estrutura STGMEDIUM .
Chame GlobalUnlock para liberar o bloqueio no objeto de memória global.
Chame ReleaseStgMedium para liberar o objeto de memória global.
Observação
Você deve usar ReleaseStgMedium para liberar o objeto de memória global, não GlobalFree.
O exemplo a seguir mostra como extrair um valor DWORD armazenado como um objeto de memória global de um objeto de dados. O parâmetro pdtobj é um ponteiro para a interface IDataObject do objeto de dados, cf é o formato da área de transferência que identifica os dados desejados e pdwOut é usado para retornar o valor de dados.
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;
}
Implementando IDropTarget
O sistema usa a interface IDropTarget para se comunicar com o destino enquanto o cursor está sobre a janela de destino. As respostas do destino são encaminhadas para a origem por meio de sua interface IDropSource . Dependendo da resposta, a origem pode modificar o ícone que representa os dados. Se o destino de soltar precisar especificar o ícone de dados, ele poderá fazer isso criando um objeto auxiliar de arrastar e soltar.
Com operações convencionais de arrastar e soltar, o destino informa o objeto de dados do resultado da operação definindo o parâmetro pdwEffect de IDropTarget::D rop para o valor DROPEFFECT apropriado. Com objetos de dados shell, o destino também pode precisar chamar IDataObject::SetData. Para obter uma discussão sobre como os destinos devem responder para diferentes cenários de transferência de dados, consulte Manipulando cenários de transferência de dados do Shell.
As seções a seguir discutem brevemente como implementar os métodos IDropTarget::D ragEnter, IDropTarget::D ragOver e IDropTarget::D rop . Para obter mais detalhes, consulte a documentação de referência.
Método DragEnter
O sistema chama o método IDropTarget::D ragEnter quando o cursor entra na janela de destino. Seus parâmetros fornecem ao destino o local do cursor, o estado das teclas modificadoras de teclado, como a tecla CTRL, e um ponteiro para a interface IDataObject do objeto de dados. O destino é responsável por usar essa interface para determinar se ela pode aceitar qualquer um dos formatos contidos pelo objeto de dados. Se puder, normalmente deixará o valor de pdwEffect inalterado. Se ele não puder aceitar dados do objeto de dados, ele definirá o parâmetro pdwEffect como DROPEFFECT_NONE. O sistema passa o valor desse parâmetro para a interface IDropSource do objeto de dados para permitir que ele exiba a imagem de arrastar apropriada.
Os destinos não devem usar o método IDataObject::GetData para renderizar dados do Shell antes de serem descartados. Renderizar totalmente os dados do objeto para cada ocorrência desse tipo pode fazer com que o cursor de arrastar seja interrompido. Para evitar esse problema, alguns objetos Shell contêm um formato CFSTR_INDRAGLOOP . Ao extrair esse formato, os destinos podem marcar o status do loop de arrastar, evitando a renderização intensiva de memória dos dados do objeto. O valor de dados do formato é um DWORD definido como um valor diferente de zero se o objeto de dados estiver dentro de um loop de arrastar. O valor de dados do formato será definido como zero se os dados tiverem sido descartados.
Se o destino puder aceitar dados do objeto de dados, ele deverá examinar grfKeyState para determinar se alguma tecla modificadora foi pressionada para modificar o comportamento de descarte normal. Por exemplo, a operação padrão normalmente é uma movimentação, mas deprimir a tecla CTRL geralmente indica uma operação de cópia.
Enquanto o cursor está sobre a janela de destino, o destino pode usar o objeto auxiliar de arrastar e soltar para substituir a imagem de arrastar do objeto de dados por sua própria. Nesse caso, IDropTarget::D ragEnter deve chamar IDropTargetHelper::D ragEnter para passar as informações contidas nos parâmetros DragEnter para o objeto auxiliar drag-and-drop.
Método DragOver
À medida que o cursor se move dentro da janela de destino, o sistema chama periodicamente o método IDropTarget::D ragOver . Seus parâmetros fornecem ao destino o local do cursor e o estado das teclas modificadoras de teclado, como a tecla CTRL. IDropTarget::D ragOver tem as mesmas responsabilidades que IDropTarget::D ragEnter e as implementações geralmente são muito semelhantes.
Se o destino estiver usando o objeto auxiliar drag-and-drop, IDropTarget::D ragOver deverá chamar IDropTargetHelper::D ragOver para encaminhar as informações contidas nos parâmetros DragOver para o objeto auxiliar drag-and-drop.
Método Drop
O sistema chama o método IDropTarget::D rop para notificar o destino de que o usuário removeu os dados, normalmente liberando o botão do mouse. IDropTarget::D rop tem os mesmos parâmetros que IDropTarget::D ragEnter. O destino normalmente responde extraindo um ou mais formatos do objeto de dados. Quando terminar, o destino deverá definir o parâmetro pdwEffect como um valor DROPEFFECT que indica o resultado da operação. Para alguns tipos de transferência de dados do Shell, o destino também deve chamar IDataObject::SetData para passar um formato com informações adicionais sobre o resultado da operação para o objeto de dados. Para obter uma discussão detalhada, consulte Manipulando cenários de transferência de dados do Shell.
Se o destino estiver usando o objeto auxiliar drag-and-drop, IDropTarget::D rop deverá chamar IDropTargetHelper::D rop para encaminhar as informações contidas nos parâmetros IDropTargetHelper::D ragOver para o objeto auxiliar de arrastar e soltar.
Usando o objeto auxiliar de arrastar e soltar
O objeto auxiliar de arrastar e soltar (CLSID_DragDropHelper) é exportado pelo Shell para permitir que os destinos especifiquem a imagem de arrastar enquanto ela estiver sobre a janela de destino. Para usar o objeto auxiliar de arrastar e soltar, crie um objeto de servidor em processo chamando CoCreateInstance com um CLSID (identificador de classe) de CLSID_DragDropHelper. O objeto auxiliar de arrastar e soltar expõe duas interfaces que são usadas da seguinte maneira:
- A interface IDragSourceHelper permite que o destino de soltar especifique um ícone para representar o objeto de dados.
- A interface IDropTargetHelper permite que o destino de soltar informe o objeto auxiliar de arrastar e soltar do local do cursor e mostre ou oculte o ícone de dados.
Usando a interface IDragSourceHelper
A interface IDragSourceHelper é exposta pelo objeto auxiliar de arrastar e soltar para permitir que um destino de soltar forneça a imagem que será exibida enquanto o cursor estiver sobre a janela de destino. IDragSourceHelper fornece duas maneiras alternativas de especificar o bitmap a ser usado como uma imagem de arrastar:
- Soltar destinos que têm uma janela pode registrar uma mensagem de janela DI_GETDRAGIMAGE para ela inicializando o objeto auxiliar de arrastar e soltar com IDragSourceHelper::InitializeFromWindow. Quando o destino recebe uma mensagem DI_GETDRAGIMAGE, o manipulador coloca as informações de bitmap da imagem de arrastar na estrutura SHDRAGIMAGE que é passada como o valor lParam da mensagem.
- Os destinos de soltar sem janela especificam um bitmap quando inicializam o objeto auxiliar de arrastar e soltar com IDragSourceHelper::InitializeFromBitmap.
Usando a interface IDropTargetHelper
Essa interface permite que o destino de soltar notifique o objeto auxiliar de arrastar e soltar quando o cursor entra ou sai do destino. Enquanto o cursor está sobre a janela de destino, IDropTargetHelper permite que o destino forneça ao objeto auxiliar de arrastar e soltar as informações que o destino recebe por meio de sua interface IDropTarget .
Quatro dos métodos IDropTargetHelper — IDropTargetHelper::D ragEnter, IDropTargetHelper::D ragLeave, IDropTargetHelper::D ragOver e IDropTargetHelper::D rop— estão associados ao método IDropTarget de mesmo nome. Para usar o objeto auxiliar de arrastar e soltar, cada um dos métodos IDropTarget deve chamar o método IDropTargetHelper correspondente para encaminhar as informações para o objeto auxiliar de arrastar e soltar. O quinto método IDropTargetHelper , IDropTargetHelper::Show, notifica o objeto auxiliar de arrastar e soltar para mostrar ou ocultar a imagem de arrastar. Esse método é usado ao arrastar uma janela de destino em um modo de vídeo de baixa profundidade de cor. Ele permite que o destino oculte a imagem de arrastar enquanto está pintando a janela.