Partilhar via


Manipulando cenários de transferência de dados do Shell

O documento Shell Data Object discutiu a abordagem geral usada para transferir dados do Shell com o recurso de arrastar e largar ou a Área de Transferência. No entanto, para implementar a transferência de dados do Shell em seu aplicativo, você também deve entender como aplicar esses princípios e técnicas gerais à variedade de maneiras pelas quais os dados do Shell podem ser transferidos. Este documento apresenta cenários comuns de transferência de dados do Shell e discute como implementar cada um deles em seu aplicativo.

Observação

Embora cada um desses cenários discuta uma operação de transferência de dados específica, muitos deles se aplicam a uma variedade de cenários relacionados. Por exemplo, a principal diferença entre a maioria das transferências por área de transferência e por arrastar e soltar está na forma como o objeto de dados chega ao destino. Quando o destino tem um ponteiro para a interface IDataObject do objeto de dados, os procedimentos para extrair informações são basicamente os mesmos para ambos os tipos de transferência de dados. No entanto, alguns dos cenários estão limitados a um tipo específico de operação. Consulte o cenário individual para obter detalhes.

 

Orientações Gerais

Cada uma das seções a seguir discute um único cenário de transferência de dados bastante específico. No entanto, as transferências de dados são frequentemente mais complexas e podem envolver aspetos de vários cenários. Você normalmente não sabe, com antecedência, qual cenário você realmente precisará lidar. Aqui estão algumas diretrizes gerais para ter em mente.

Para as fontes de dados:

  • Os formatos da Área de Transferência do Shell, com exceção do CF_HDROP, não são predefinidos. Cada formato que você deseja usar deve ser registrado chamando RegisterClipboardFormat.
  • Os formatos nos objetos de dados são fornecidos na ordem de preferência da fonte. Enumere o objeto de dados e escolha o primeiro que possa ser consumido.
  • Inclua tantos formatos quantos puder suportar. Você geralmente não sabe onde o objeto de dados será largado. Essa prática melhora as chances de que o objeto de dados contenha um formato que o destino de descarte possa aceitar.
  • Os arquivos existentes devem ser oferecidos com o formato CF_HDROP.
  • Disponibilize dados tipo arquivo com formatos CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Essa abordagem permite que o destino crie um arquivo a partir de um objeto de dados sem precisar saber nada sobre o armazenamento de dados subjacente. Normalmente, você deve apresentar os dados como um IStream interface. Este mecanismo de transferência de dados é mais flexível do que um objeto de memória global e usa muito menos memória.
  • Fontes de arrasto devem fornecer o formato CFSTR_SHELLIDLIST ao arrastar itens do Shell. Os objetos de dados para itens podem ser adquiridos por meio dos métodos IShellFolder::GetUIObjectOf ou IShellItem::BindToHandler. As fontes de dados podem criar uma implementação de objeto de dados padrão que ofereça suporte ao formato CFSTR_SHELLIDLIST usando SHCreateDataObject.
  • Os alvos de soltura que desejam raciocinar sobre os itens que estão a ser arrastados, usando o modelo de programação de item de shell, podem converter um IDataObject num IShellItemArray usando SHCreateShellItemArrayFromDataObject.
  • Use cursores de feedback padrão.
  • Suporte o arrasto para a esquerda e para a direita.
  • Use o próprio objeto de dados de um objeto incorporado. Essa abordagem permite que seu aplicativo recupere quaisquer formatos extras que o objeto de dados tenha a oferecer e evita a criação de uma camada extra de contenção. Por exemplo, um objeto incorporado do servidor A é arrastado do servidor/contêiner B e solto no contêiner C. C deve criar um objeto incorporado do servidor A, não um objeto incorporado do servidor B contendo um objeto incorporado do servidor A.
  • Lembre-se de que o Shell pode usar movimentos otimizados ou executar operações de exclusão durante a colagem ao mover arquivos. Seu aplicativo deve ser capaz de reconhecer essas operações e responder adequadamente.

Para os objetivos de dados:

  • Os formatos de Shell Clipboard, com exceção do CF_HDROP, não são predefinidos. Cada formato que você deseja usar deve ser registrado chamando RegisterClipboardFormat.
  • Implemente e registre um destino de descarte OLE. Evite usar destinos do Windows 3.1 ou a mensagem WM_DROPFILES, se possível.
  • Os formatos contidos por um objeto de dados variam, dependendo de onde o objeto vem. Como você geralmente não sabe com antecedência de onde vem um objeto de dados, não assuma que um determinado formato estará presente. O objeto de dados deve enumerar os formatos em ordem de qualidade, começando com o melhor. Assim, para obter o melhor formato disponível, os aplicativos normalmente enumeram os formatos disponíveis e usam o primeiro formato na enumeração que eles podem suportar.
  • Suporte para arrastar com o botão direito. Você pode personalizar o menu de atalho de arrastar e soltar configurando um manipulador de arrastar e soltar .
  • Se o seu aplicativo aceitar arquivos existentes, ele deve ser capaz de lidar com o formato CF_HDROP.
  • Em geral, os aplicativos que aceitam arquivos também devem lidar com os formatos CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Enquanto os arquivos do sistema de arquivos têm o formato CF_HDROP, os arquivos de provedores como extensões de namespace geralmente usam CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Os exemplos incluem pastas do Windows CE, pastas FTP (File Transfer Protocol), pastas da Web e pastas CAB. A fonte normalmente implementa uma interface IStream para apresentar dados de seu armazenamento como um arquivo.
  • Lembre-se de que o Shell pode usar movimentos otimizados ou operações de de exclusão ao colar ao mover arquivos. Seu aplicativo deve ser capaz de reconhecer essas operações e responder adequadamente.

Copiando nomes de arquivo da área de transferência para um aplicativo

Cenário: Um utilizador seleciona um ou mais ficheiros no Explorador do Windows e copia-os para o Clipboard. Seu aplicativo extrai os nomes de arquivo e os cola no documento.

Esse cenário pode ser usado, por exemplo, para permitir que um usuário crie um link HTML cortando e colando o arquivo em seu aplicativo. Seu aplicativo pode extrair o nome do arquivo do objeto de dados e processá-lo para criar uma marca âncora.

Quando um utilizador seleciona um ficheiro no Windows Explorer e o copia para a Área de Transferência, o Shell cria um objeto de dados. Em seguida, ele chama OleSetClipboard para colocar um ponteiro para a interface IDataObject IDataObject do objeto de dados na área de transferência.

Quando o utilizador seleciona o comando Colar no menu ou na barra de ferramentas da aplicação:

  1. Chame OleGetClipboard para recuperar a interface IDataObject do objeto de dados.
  2. Chame IDataObject::EnumFormatEtc para solicitar um objeto enumerador.
  3. Use a interface deIEnumFORMATETC do objeto enumeradorpara enumerar os formatos contidos pelo objeto de dados.

Observação

As duas etapas finais deste procedimento estão incluídas para serem completas. Normalmente, não são necessários para transferências de ficheiros simples. Todos os objetos de dados usados para esse tipo de transferência de dados devem conter o formato CF_HDROP, que pode ser usado para determinar os nomes dos arquivos contidos pelo objeto. No entanto, para transferências de dados mais gerais, você deve enumerar os formatos e selecionar o melhor que seu aplicativo pode lidar.

 

Extraindo os nomes de arquivo do objeto de dados

A próxima etapa é extrair um ou mais nomes de arquivo do objeto de dados e colá-los em seu aplicativo. Observe que o procedimento discutido nesta seção para extrair um nome de arquivo de um objeto de dados se aplica igualmente bem a transferências de arrastar e soltar.

A maneira mais simples de recuperar nomes de arquivo de um objeto de dados é o formato CF_HDROP:

  1. Invoque IDataObject::GetData. Defina o membro cfFormat da estrutura FORMATETC como CF_HDROP e o membro tymed como TYMED_HGLOBAL. O dwAspect membro é normalmente definido como DVASPECT_CONTENT. No entanto, se você precisar ter o caminho do arquivo no formato curto (8.3), defina dwAspect como DVASPECT_SHORT.

    Quando IDataObject::GetData retorna, o membro hGlobal da estrutura STGMEDIUM aponta para um objeto de memória global que contém os dados.

  2. Crie uma variável HDROP e defina-a para o membro hGlobal da estrutura STGMEDIUM. A variável HDROP agora é um identificador para uma estrutura DROPFILES seguida por uma string terminada por dois nulos contendo os caminhos de arquivo totalmente qualificados dos arquivos copiados.

  3. Determine quantos caminhos de arquivo estão na lista chamando DragQueryFile com o parâmetro iFile definido como 0xFFFFFFFF. A função retorna o número de caminhos de arquivo na lista. O índice baseado em zero do caminho do arquivo nesta lista é usado na próxima etapa para identificar um caminho específico.

  4. Extraia os caminhos de arquivo do objeto de memória global chamando DragQueryFile uma vez para cada arquivo, com iFile definido como o índice do arquivo.

  5. Processe os caminhos de arquivo conforme necessário e cole-os em seu aplicativo.

  6. Chame ReleaseStgMedium e passe o ponteiro para a estrutura de STGMEDIUM que você passou para IDataObject::GetData na etapa 1. Depois de liberar a estrutura, o valor HDROP criado na etapa 2 não é mais válido e não deve ser usado.

Copiando o conteúdo de um arquivo descartado em um aplicativo

Cenário: Um usuário arrasta um ou mais arquivos do Windows Explorer e os solta na janela do seu aplicativo. Seu aplicativo extrai o conteúdo do(s) arquivo(s) e o cola no aplicativo.

Este cenário usa arrastar e soltar para transferir os arquivos do Windows Explorer para o aplicativo. Antes da operação, o seu pedido deve:

  1. Chame RegisterClipboardFormat para registrar os formatos da Área de Transferência do Shell necessários.
  2. Chame RegisterDragDrop para registar uma janela de destino e a interface IDropTarget do seu aplicativo.

Depois que o usuário inicia a operação, selecionando um ou mais arquivos e começando a arrastá-los:

  1. O Windows Explorer cria um objeto de dados e carrega os formatos suportados nele.
  2. O Windows Explorer chama DoDragDrop para iniciar o loop de arrastar.
  3. Quando a imagem arrastada atinge a janela de destino, o sistema notifica IDropTarget::D ragEnter.
  4. Para determinar o que o objeto de dados contém, invoque o método IDataObject::EnumFormatEtcdo objeto de dados. Use o objeto enumerador retornado pelo método para enumerar os formatos contidos pelo objeto de dados. Se a sua candidatura não quiser aceitar nenhum destes formatos, devolva DROPEFFECT_NONE. Para os fins desse cenário, seu aplicativo deve ignorar quaisquer objetos de dados que não contêm formatos usados para transferir arquivos, como CF_HDROP.
  5. Quando o utilizador descarta os dados, o sistema chama IDropTarget::Drop.
  6. Use o IDataObject interface para extrair o conteúdo dos arquivos.

Há várias maneiras diferentes de extrair o conteúdo de um objeto Shell de um objeto de dados. Em geral, use a seguinte ordem:

  • Se o arquivo contiver um formato CF_TEXT, os dados serão texto ANSI. Você pode usar o formato CF_TEXT para extrair os dados, em vez de abrir o arquivo em si.
  • Se o arquivo contiver um objeto OLE vinculado ou incorporado, o objeto de dados conterá um formato CF_EMBEDDEDOBJECT. Use técnicas OLE padrão para extrair os dados. Os ficheiros de lixo contêm sempre um formato CF_EMBEDDEDOBJECT.
  • Se o objeto Shell for do sistema de arquivos, o objeto de dados conterá um formato CF_HDROP com os nomes dos arquivos. Extraia o nome do arquivo do CF_HDROP e chame OleCreateFromFile para criar um novo objeto vinculado ou incorporado. Para obter uma discussão sobre como recuperar um nome de arquivo de um formato CF_HDROP, consulte Copiando nomes de arquivo da área de transferência para um aplicativo.
  • Se o objeto de dados contiver um formato CFSTR_FILEDESCRIPTOR, você poderá extrair o conteúdo de um arquivo do formato CFSTR_FILECONTENTS do arquivo. Para obter uma discussão sobre este procedimento, consulte Usando o formato CFSTR_FILECONTENTS para extrair dados de um arquivo.
  • Antes do Shell versão 4.71, uma aplicação indicava que estava a transferir um tipo de arquivo de atalho definindo FD_LINKUI no membro dwFlags da estrutura FILEDESCRIPTOR. Para versões posteriores do Shell, a maneira preferida de indicar que os atalhos estão sendo transferidos é usar o formato CFSTR_PREFERREDDROPEFFECT definido como DROPEFFECT_LINK. Essa abordagem é muito mais eficiente do que extrair a estrutura FILEDESCRIPTOR apenas para verificar um sinalizador.

Se o processo de extração de dados for demorado, convém fazer a operação de forma assíncrona em um thread em segundo plano. Seu thread principal pode prosseguir sem atrasos desnecessários. Para obter uma discussão sobre como lidar com a extração assíncrona de dados, consulte Arrastando e soltando objetos do shell de forma assíncrona.

Usando o formato CFSTR_FILECONTENTS para extrair dados de um arquivo

O formato CFSTR_FILECONTENTS fornece uma maneira muito flexível e poderosa de transferir o conteúdo de um arquivo. Nem sequer é necessário que os dados sejam armazenados como um único ficheiro. Tudo o que é necessário para esse formato é que o objeto de dados apresente os dados ao destino como se fosse um arquivo. Por exemplo, os dados reais podem ser uma seção de um documento de texto ou um bloco de dados extraídos de um banco de dados. O destino pode tratar os dados como um arquivo e não precisa saber nada sobre o mecanismo de armazenamento subjacente.

As extensões de namespace normalmente usam CFSTR_FILECONTENTS para transferir dados porque esse formato não assume nenhum mecanismo de armazenamento específico. Uma extensão de namespace pode usar qualquer mecanismo de armazenamento conveniente e usar esse formato para apresentar seus objetos aos aplicativos como se fossem arquivos.

O mecanismo de transferência de dados para CFSTR_FILECONTENTS é normalmente TYMED_ISTREAM. Transferir um ponteiro de interface IStream consome muito menos memória do que carregar os dados num objeto de memória global, e IStream é uma forma mais simples de representar dados do que IStorage.

Um formato CFSTR_FILECONTENTS é sempre acompanhado por um formato CFSTR_FILEDESCRIPTOR. Você deve examinar o conteúdo deste formato primeiro. Se mais de um arquivo estiver sendo transferido, o objeto de dados realmente conterá vários formatos CFSTR_FILECONTENTS, um para cada arquivo. O formato CFSTR_FILEDESCRIPTOR contém o nome e os atributos de cada arquivo e fornece um valor de índice para cada arquivo necessário para extrair o formato CFSTR_FILECONTENTS de um arquivo específico.

Para extrair um formato CFSTR_FILECONTENTS:

  1. Extraia o formato CFSTR_FILEDESCRIPTOR como um valor TYMED_HGLOBAL.
  2. O membro hGlobal da estrutura STGMEDIUM retornada referencia um objeto de memória global. Bloqueie esse objeto passando o valor hGlobal para GlobalLock.
  3. Converta o ponteiro devolvido por GlobalLock num ponteiroFILEGROUPDESCRIPTOR. Apontará para uma estrutura de FILEGROUPDESCRIPTOR, seguida por uma ou mais estruturas FILEDESCRIPTOR. Cada estrutura FILEDESCRIPTOR contém uma descrição de um ficheiro que está contido por um dos formatos de CFSTR_FILECONTENTS que o acompanham.
  4. Examine o FILEDESCRIPTOR estruturas para determinar qual corresponde ao arquivo que você deseja extrair. O índice baseado em zero dessa estrutura FILEDESCRIPTOR é usado para identificar o formato CFSTR_FILECONTENTS do arquivo. Como o tamanho de um bloco de memória global não é preciso em bytes, use o da estrutura nFileSizeLow e membros do nFileSizeHigh para determinar quantos bytes representam o arquivo no objeto de memória global.
  5. Chame IDataObject::GetData com o membro cfFormat da estrutura FORMATETC definido para o valor CFSTR_FILECONTENTS e o membro lIndex definido para o índice determinado na etapa anterior. O membro vinculado é normalmente definido como TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE. O objeto de dados pode então escolher seu mecanismo de transferência de dados preferido.
  6. A estrutura STGMEDIUM que IDataObject::GetData retorna conterá um ponteiro para os dados do arquivo. Examine o tymed elemento da estrutura para examinar o mecanismo de transferência de dados.
  7. Se tymed estiver definido como TYMED_ISTREAM ou TYMED_ISTORAGE, use a interface de para extrair os dados. Se tymed for definido como TYMED_HGLOBAL, os dados estarão contidos em um objeto global de memória. Para obter uma discussão sobre como extrair dados de um objeto de memória global, consulte a seção Extraindo um objeto de memória global de um objeto de dados de Shell Data Object.
  8. Chame GlobalLock para desbloquear o objeto de memória global que você bloqueou na etapa 2.

Lidando com operações de movimentação otimizadas

Cenário: Um ficheiro é movido do sistema de ficheiros para uma extensão de namespace usando uma transferência otimizada.

Em uma operação de movimentação convencional, o destino faz uma cópia dos dados e a fonte exclui o original. Este procedimento pode ser ineficiente porque requer duas cópias dos dados. Com objetos grandes, como bancos de dados, uma operação de movimentação convencional pode nem ser prática.

Com um movimento otimizado, o alvo usa sua compreensão de como os dados são armazenados para lidar com toda a operação de movimento. Nunca há uma segunda cópia dos dados e não há necessidade de a fonte excluir os dados originais. Os dados do Shell são adequados para movimentos otimizados porque o destino pode lidar com toda a operação usando a API do Shell. Um exemplo típico é a movimentação de arquivos. Uma vez que o destino tem o caminho de um arquivo a ser movido, ele pode usar SHFileOperation para movê-lo. Não há necessidade de a fonte excluir o arquivo original.

Observação

O Shell normalmente usa uma movimentação otimizada para mover arquivos. Para lidar com a transferência de dados do Shell corretamente, seu aplicativo deve ser capaz de detetar e lidar com uma movimentação otimizada.

 

Os movimentos otimizados são tratados da seguinte maneira:

  1. A origem chama DoDragDrop com o parâmetro dwEffect definido como DROPEFFECT_MOVE para indicar que os objetos de origem podem ser movidos.

  2. O destino recebe o valor DROPEFFECT_MOVE por meio de um de seus métodos IDropTarget, indicando que uma movimentação é permitida.

  3. O destino copia o objeto (movimento não otimizado) ou move o objeto (movimento otimizado).

  4. O destino informa a fonte se precisa apagar os dados originais.

    Um movimento otimizado é a operação padrão, com os dados excluídos pelo destino. Para informar a fonte de que uma movimentação otimizada foi executada:

      • O destino define o valor pdwEffect que recebeu por meio de seu método IDropTarget::Drop para algum valor diferente de DROPEFFECT_MOVE. Normalmente, é definido como DROPEFFECT_NONE ou DROPEFFECT_COPY. O valor será retornado à fonte por DoDragDrop.
      • O destino também chama o método IDataObject::SetData do objeto de dados e passa para ele um identificador de formato CFSTR_PERFORMEDDROPEFFECT definido como DROPEFFECT_NONE. Essa chamada de método é necessária porque alguns alvos de soltar podem não definir o parâmetro pdwEffect de DoDragDrop corretamente. O formato CFSTR_PERFORMEDDROPEFFECT é a maneira confiável de indicar que um movimento otimizado ocorreu.

    Se o alvo fez um movimento não otimizado, os dados devem ser apagados pela origem. Para informar a fonte de que uma movimentação não otimizada foi executada:

      • O destino define o valor pdwEffect que recebeu por meio do seu método IDropTarget::Drop para DROPEFFECT_MOVE. O valor será retornado à fonte por DoDragDrop.
      • O destino também chama o método IDataObject::SetData do objeto de dados e passa a ele um identificador de formato CFSTR_PERFORMEDDROPEFFECT definido como DROPEFFECT_MOVE. Esta chamada de método é necessária porque alguns alvos de soltar podem não definir corretamente o parâmetro pdwEffect de DoDragDrop. O formato CFSTR_PERFORMEDDROPEFFECT é a maneira confiável de indicar que um movimento não otimizado ocorreu.
  5. A origem inspeciona os dois valores que podem ser retornados pelo alvo. Se ambos estiverem definidos como DROPEFFECT_MOVE, ele concluirá a movimentação não otimizada excluindo os dados originais. Caso contrário, o alvo fez um movimento otimizado e os dados originais foram excluídos.

Manipulando operações de exclusão ao colar

Cenário: Um ou mais arquivos são cortados de uma pasta no Windows Explorer e colados em uma extensão de namespace. O Windows Explorer deixa os arquivos realçados até receber resposta sobre o resultado da operação de colagem.

Tradicionalmente, quando um usuário corta dados, eles desaparecem imediatamente da visualização. Isso pode não ser eficiente e pode levar a problemas de usabilidade se o usuário ficar preocupado com o que aconteceu com os dados. Uma abordagem alternativa é usar uma operação de colar e eliminar.

Com uma operação de exclusão ao colar, os dados selecionados não são removidos imediatamente da exibição. Em vez disso, o aplicativo de origem o marca como selecionado, talvez alterando a fonte ou a cor do plano de fundo. Depois que o aplicativo de destino cola os dados, ele notifica a fonte sobre o resultado da operação. Se o alvo executou um de movimento otimizado, a fonte pode simplesmente atualizar a sua exibição. Se o alvo executou uma movimentação normal, a fonte também deve eliminar a sua cópia dos dados. Se a colagem falhar, o aplicativo de origem restaurará os dados selecionados para sua aparência original.

Observação

O Shell normalmente usa excluir ao colar quando uma operação cortar/colar é usada para mover arquivos. As operações de exclusão durante a colagem com objetos do Shell normalmente usam uma operação de movimentação otimizada para mover os arquivos. Para lidar com a transferência de dados do Shell corretamente, a sua aplicação deve ser capaz de detectar e manipular operações de eliminação ao colar.

 

O requisito essencial para a exclusão ao colar é que o destino deve informar sobre o resultado da operação à fonte. No entanto, as técnicas padrão da área de transferência não podem ser usadas para implementar a exclusão ao colar porque elas não fornecem uma maneira para o destino se comunicar com a fonte. Em vez disso, o aplicativo de destino usa o método IDataObject::SetData do objeto de dados para relatar o resultado para o objeto de dados. O objeto de dados pode então se comunicar com a fonte por meio de uma interface privada.

O procedimento básico para uma operação de eliminação ao colar é o seguinte:

  1. A fonte marca a exibição na tela dos dados selecionados.
  2. A fonte cria um objeto de dados. Ele indica uma operação de corte adicionando o formato CFSTR_PREFERREDDROPEFFECT com um valor de dados de DROPEFFECT_MOVE.
  3. A fonte coloca o objeto de dados na área de transferência usando OleSetClipboard.
  4. O destino recupera o objeto de dados da área de transferência usando OleGetClipboard.
  5. O destino extrai os dados CFSTR_PREFERREDDROPEFFECT. Se estiver definido como apenas DROPEFFECT_MOVE, o destino pode fazer um movimento otimizado ou apenas copiar os dados.
  6. Se o destino não realizar uma movimentação otimizada, chamará o método IDataObject::SetData com o formato CFSTR_PERFORMEDDROPEFFECT definido como DROPEFFECT_MOVE.
  7. Quando a pasta estiver concluída, o destino chamará o métodoIDataObject::SetDatacom o formato CFSTR_PASTESUCCEEDED definido como DROPEFFECT_MOVE.
  8. Quando o métodoIDataObject::SetData da fonteé chamado com o formato CFSTR_PASTESUCCEEDED definido como DROPEFFECT_MOVE, ele deve verificar se também recebeu o formato CFSTR_PERFORMEDDROPEFFECT definido como DROPEFFECT_MOVE. Se ambos os formatos forem enviados pelo destino, a fonte terá que excluir os dados. Se apenas o formato CFSTR_PASTESUCCEEDED for recebido, a fonte pode simplesmente remover os dados de sua exibição. Se a transferência falhar, a fonte atualiza a exibição para sua aparência original.

Transferindo dados para e de pastas virtuais

Cenário: Um usuário arrasta um objeto ou o solta em uma pasta virtual.

As pastas virtuais contêm objetos que geralmente não fazem parte do sistema de arquivos. Algumas pastas virtuais, como a Lixeira, podem representar dados armazenados no disco rígido, mas não como objetos comuns do sistema de arquivos. Alguns podem representar dados armazenados que estão em um sistema remoto, como um PC portátil ou um site FTP. Outros, como a pasta Impressoras, contêm objetos que não representam dados armazenados. Embora algumas pastas virtuais façam parte do sistema, os desenvolvedores também podem criar e instalar pastas virtuais personalizadas implementando uma extensão de namespace.

Independentemente do tipo de dados ou de como eles são armazenados, os objetos de pasta e arquivo contidos por uma pasta virtual são apresentados pelo Shell como se fossem arquivos e pastas normais. É responsabilidade da pasta virtual pegar quaisquer dados que ela contenha e apresentá-los ao Shell adequadamente. Este requisito significa que as pastas virtuais normalmente suportam arrastar e soltar e transferências de dados da área de transferência.

Há, portanto, dois grupos de desenvolvedores que precisam se preocupar com a transferência de dados de e para pastas virtuais:

  • Desenvolvedores cujos aplicativos precisam aceitar dados transferidos de uma pasta virtual.
  • Desenvolvedores cujas extensões de namespace precisam suportar adequadamente a transferência de dados.

Aceitando dados de uma pasta virtual

As pastas virtuais podem representar praticamente qualquer tipo de dados e podem armazenar esses dados da maneira que quiserem. Algumas pastas virtuais podem realmente conter arquivos e pastas normais do sistema de arquivos. Outros poderiam, por exemplo, agrupar todos os seus objetos num único documento ou base de dados.

Quando um objeto de sistema de arquivos é transferido para um aplicativo, o objeto de dados normalmente contém um formato de CF_HDROP com o caminho totalmente qualificado do objeto. Seu aplicativo pode extrair essa cadeia de caracteres e usar as funções normais do sistema de arquivos para abrir o arquivo e extrair seus dados. No entanto, como as pastas virtuais normalmente não contêm objetos normais do sistema de arquivos, elas geralmente não usam CF_HDROP.

Em vez de CF_HDROP, os dados são normalmente transferidos de pastas virtuais com os formatos CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS. O formato CFSTR_FILECONTENTS tem duas vantagens em relação CF_HDROP:

  • Nenhum método específico de armazenamento de dados é assumido.
  • O formato é mais flexível. Ele suporta três mecanismos de transferência de dados: um objeto de memória global, uma interfaceIStreamou uma interfaceIStorage.

Os objetos de memória global raramente são usados para transferir dados de ou para objetos virtuais porque os dados devem ser copiados para a memória em sua totalidade. A transferência de um ponteiro de interface quase não requer memória e é muito mais eficiente. Com arquivos muito grandes, um ponteiro de interface pode ser o único mecanismo prático de transferência de dados. Normalmente, os dados são representados por um ponteiro IStream, porque essa interface é um pouco mais flexível do que IStorage. O alvo extrai o ponteiro do objeto de dados e utiliza os métodos da interface para extrair o dado.

Para obter mais discussões sobre como lidar com os formatos de CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS, consulte Usando o formato CFSTR_FILECONTENTS para extrair dados de um arquivo.

Transferindo dados de e para uma extensão NameSpace

Quando você implementa uma extensão de namespace, normalmente desejará oferecer suporte a recursos de arrastar e soltar. Siga as recomendações para fontes de queda e metas discutidas em Diretrizes Gerais. Em particular, uma extensão de namespace deve:

  • Ser capaz de lidar com os formatos CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS. Esses dois formatos são normalmente usados para transferir objetos de e para extensões de namespace.
  • Ser capaz de lidar com movimentos otimizados . O Shell espera que os objetos do Shell sejam movidos com um movimento otimizado.
  • Ser capaz de lidar com uma operação de eliminação ao colar . O Shell usa delete-on-paste quando os objetos são movidos do Shell com uma operação de cortar/colar.
  • Ser capaz de lidar com a transferência de dados através de uma interface IStream ou IStorage . A transferência de dados de ou para uma pasta virtual é normalmente tratada através da transferência de um destes dois ponteiros de interface, normalmente um ponteiro IStream . Em seguida, o destino chama os métodos de interface para extrair os dados:
      • Como uma fonte de drop, a extensão de namespace deve extrair os dados do armazenamento e passá-los através dessa interface para o destino.
      • Como um alvo de arrasto, uma extensão de namespace deve aceitar dados de uma fonte por meio desta interface e armazená-los corretamente.

Soltar arquivos na Lixeira

Cenário: O usuário solta um arquivo na Lixeira . Seu aplicativo ou extensão de namespace exclui o arquivo original.

A Lixeira é uma pasta virtual que é usada como um repositório para arquivos que não são mais necessários. Contanto que a Lixeira não tenha sido esvaziada, o usuário pode recuperar o arquivo mais tarde e devolvê-lo ao sistema de arquivos.

Na maioria das vezes, a transferência de objetos do Shell para a Lixeira funciona como qualquer outra pasta. No entanto, quando um usuário solta um arquivo na da Lixeira, a fonte precisa excluir o original, mesmo que o feedback da pasta indique uma operação de cópia. Normalmente, uma fonte de descarte não tem como saber em qual pasta seu objeto de dados foi descartado. No entanto, para sistemas Windows 2000 e posteriores, quando um objeto de dados é solto na Lixeira , o Shell chamará o método IDataObject::SetData do objeto de dados com um formato CFSTR_TARGETCLSID definido como o identificador de classe (CLSID) da Lixeira (CLSID_RecycleBin). Para lidar com o caso da Lixeira corretamente, seu objeto de dados deve ser capaz de reconhecer esse formato e comunicar as informações à fonte por meio de uma interface privada.

Observação

Quando IDataObject::SetData é chamado com um formato CFSTR_TARGETCLSID definido como CLSID_RecycleBin, a fonte de dados deve fechar todos os handles abertos dos objetos que estão sendo transferidos antes de retornar do método. Caso contrário, você pode criar violações de compartilhamento.

 

Criando e importando arquivos de sucata

Cenário: Um usuário arrasta alguns dados do arquivo de dados de um aplicativo OLE e os solta na área de trabalho ou no Windows Explorer.

O Windows permite que os usuários arrastem um objeto do arquivo de dados de um aplicativo OLE e soltem-no na área de trabalho ou em uma pasta do sistema de arquivos. Esta operação cria um ficheiro temporário , que contém os dados ou uma ligação para os dados. O nome do ficheiro é obtido do nome abreviado registado para o CLSID do objeto e dos dados CF_TEXT. Para que o Shell crie um arquivo de sucata contendo dados, a interfaceIDataObject do aplicativodeve suportar o formato CF_EMBEDSOURCE Área de Transferência. Para criar um arquivo contendo um link, IDataObject deve suportar o formato CF_LINKSOURCE.

Há também três recursos opcionais que um aplicativo pode implementar para suportar arquivos de sucata:

  • Suporte de ida e volta
  • Formatos de dados armazenados em cache
  • Renderização atrasada

Suporte de ida e volta

Um de ida e volta envolve a transferência de um objeto de dados para outro contêiner e, em seguida, de volta para o documento original. Por exemplo, um usuário pode transferir um grupo de células de uma planilha para a área de trabalho, criando um arquivo de sucata com os dados. Se o usuário transferir a sucata de volta para a planilha, os dados precisam ser integrados ao documento como eram antes da transferência original.

Quando o Shell cria o ficheiro temporário, ele representa os dados como um objeto embutido. Quando a sucata é transferida para outro contêiner, ela é transferida como um objeto de incorporação, mesmo que esteja sendo devolvida ao documento original. Seu aplicativo é responsável por determinar o formato de dados contido na sucata e colocar os dados de volta em seu formato nativo, se necessário.

Para estabelecer o formato do objeto incorporado, determine seu CLSID recuperando o formato CF_OBJECTDESCRIPTOR do objeto. Se o CLSID indicar um formato de dados que pertença ao aplicativo, ele deverá transferir os dados nativos em vez de chamar OleCreateFromData.

Formatos de dados armazenados em cache

Quando o Shell cria um arquivo de sucata, ele verifica o registro para a lista de formatos disponíveis. Por padrão, há dois formatos disponíveis: CF_EMBEDSOURCE e CF_LINKSOURCE. No entanto, há vários cenários em que os aplicativos podem precisar ter arquivos de sucata em formatos diferentes:

  • Para permitir que sucatas sejam transferidas para contêineres não-OLE, que não podem aceitar formatos de objeto incorporados.
  • Para permitir que conjuntos de aplicativos se comuniquem com um formato privado.
  • Para tornar as viagens de ida e volta mais fáceis de gerir.

Os aplicativos podem adicionar formatos à sucata armazenando-os em cache no registro. Existem dois tipos de formatos em cache:

  • Formatos de cache prioritários. Para esses formatos, os dados são copiados na sua totalidade para a área temporária do objeto de dados.
  • Formatos renderizados com atraso. Para esses formatos, o objeto de dados não é copiado para a sucata. Em vez disso, a renderização é adiada até que um destino solicite os dados. A renderização tardia é discutida com mais detalhes na próxima seção.

Para adicionar um cache prioritário ou um formato renderizado com atraso, crie uma subchave DataFormat sob a chave CLSID do aplicativo que é a fonte dos dados. Sob essa subchave, crie uma subchave PriorityCacheFormats ou uma subchave DelayRenderFormats. Para cada cache prioritário ou formato renderizado com atraso, crie uma subchave numerada começando com zero. Defina o valor desta chave como uma cadeia de caracteres com o nome registado do formato ou um valor #X, onde X representa o número do formato de um formato padrão do Clipboard.

O exemplo a seguir mostra formatos armazenados em cache para dois aplicativos. O aplicativo MyProg1 tem o formato rich-text como um formato de cache prioritário e um formato privado "My Format" como um formato renderizado com atraso. O aplicativo MyProg2 tem o formato CF_BITMAP (#8") como um formato de cache prioritário.

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

Formatos adicionais podem ser adicionados criando subchaves numeradas adicionais.

Renderização atrasada

Um formato de renderização atrasado permite que um aplicativo crie um arquivo de sucata, mas atrase a despesa de renderização dos dados até que seja solicitado por um destino. A interface IDataObject de um objecto disponibilizará formatos de renderização atrasada ao destino, juntamente com dados nativos e armazenados em cache. Se o destino solicitar um formato de renderização atrasado, o Shell executará o aplicativo e fornecerá os dados para o destino a partir do objeto ativo.

Observação

Como a renderização atrasada é um pouco arriscada, ela deve ser usada com cautela. Ele não funcionará se o servidor não estiver disponível ou em aplicativos que não são habilitados para OLE.

 

Arrastar e soltar objetos do shell de forma assíncrona

Cenário: Um usuário transfere um grande bloco de dados da origem para o destino. Para evitar o bloqueio de ambos os aplicativos por um período significativo de tempo, o destino extrai os dados de forma assíncrona.

Normalmente, arrastar e soltar é uma operação síncrona. Em resumo:

  1. A fonte de soltar chama DoDragDrop e bloqueia o seu thread principal até que a função retorne. O bloqueio do thread primário normalmente bloqueia o processamento da interface do usuário.
  2. Depois que o método IDropTarget::Drop do destino é chamado, o destino extrai os dados do objeto de dados no seu thread primário. Esse procedimento normalmente bloqueia o processamento da interface de utilizador do alvo durante o processo de extração.
  3. Depois que os dados tiverem sido extraídos, o destino retorna a chamada IDropTarget::D rop, o sistema retorna DoDragDrop e ambos os threads podem continuar.

Em resumo, a transferência de dados síncrona pode bloquear os threads primários de ambos os aplicativos por um período significativo de tempo. Em particular, ambos os segmentos devem aguardar enquanto o alvo extrai os dados. Para pequenas quantidades de dados, o tempo necessário para extrair dados é pequeno e a transferência de dados síncrona funciona muito bem. No entanto, a extração síncrona de grandes quantidades de dados pode causar longos atrasos e interferir na interface do usuário do destino e da origem.

A interface IAsyncOperation/IDataObjectAsyncCapability é uma interface opcional que pode ser implementada por um objeto de dados. Ele dá ao destino de descarte a capacidade de extrair dados do objeto de dados de forma assíncrona em um thread em segundo plano. Depois que a extração de dados é transferida para a subrotina em segundo plano, as subrotinas principais de ambas as aplicações ficam livres para prosseguir.

Utilização do IASyncOperation/IDataObjectAsyncCapability

Observação

A interface foi originalmente nomeada IAsyncOperation, mas isso foi posteriormente alterado para IDataObjectAsyncCapability. Caso contrário, as duas interfaces são idênticas.

 

O objetivo do IAsyncOperation/IDataObjectAsyncCapability é permitir que a origem e o destino de descarte negociem se os dados podem ser extraídos de forma assíncrona. O procedimento a seguir descreve como a fonte de descarte usa a interface:

  1. Crie um objeto de dados que exponha IAsyncOperation/IDataObjectAsyncCapability.
  2. Chame SetAsyncMode com fDoOpAsync definido como VARIANT_TRUE para indicar que uma operação assíncrona é suportada.
  3. Depois que DoDragDrop retornam, chame InOperation:
    • Se InOperation falhar ou retornar VARIANT_FALSE, uma transferência de dados síncrona normal ocorreu e o processo de extração de dados foi concluído. A fonte deve fazer qualquer limpeza que seja necessária e prosseguir.
    • Se InOperation retornar VARIANT_TRUE, os dados estão sendo extraídos de forma assíncrona. As operações de limpeza devem ser tratadas por EndOperation.
  4. Solte o objeto de dados.
  5. Quando a transferência de dados assíncrona é concluída, o objeto de dados normalmente notifica a origem por meio de uma interface privada.

O procedimento a seguir descreve como o destino de descarte usa o IAsyncOperation/a interface de IDataObjectAsyncCapability para extrair dados de forma assíncrona:

  1. Quando o sistema chama IDropTarget::Drop, chame IDataObject::QueryInterface e solicite uma interface IAsyncOperation/IDataObjectAsyncCapability (IID_IAsyncOperation/IID_IDataObjectAsyncCapability) do objeto de dados.
  2. Chamar GetAsyncMode. Se o método retornar VARIANT_TRUE, o objeto de dados oferece suporte à extração assíncrona de dados.
  3. Crie um thread separado para manipular a extração de dados e chame StartOperation.
  4. Devolva a chamada IDropTarget::Drop, como faria para uma operação normal de transferência de dados. O DoDragDrop vai retornar e desbloquear a fonte do arrastamento. Não chame IDataObject::SetData para indicar o resultado de uma operação otimizada de mover ou de exclusão ao colar. Aguarde até que a operação seja concluída.
  5. Extraia os dados no thread em segundo plano. O thread principal do alvo é desbloqueado e livre para prosseguir.
  6. Se a transferência de dados foi uma de movimentação otimizada para ou operação de de exclusão ao colar, chame IDataObject::SetData para indicar o resultado.
  7. Notifique o objeto de dados de que a extração foi concluída chamando EndOperation.