Integrar um aplicativo da área de trabalho empacotado ao Explorador de Arquivos

Alguns aplicativos do Windows definem as extensões do Explorador de Arquivos que adicionam entradas do menu de contexto que permitem que os clientes executem opções relacionadas ao aplicativo. Tecnologias de implantação de aplicativo Windows mais antigas, como MSI e ClickOnce, definem as extensões do Explorador de Arquivos por meio do Registro. O Registro tem uma série de hives que controlam as extensões do Explorador de Arquivos e outros tipos de extensões de shell. Esses instaladores normalmente criam uma série de chaves do Registro para configurar os vários itens a serem incluídos no menu de contexto.

Se você empacotar seu aplicativo do Windows usando o MSIX, o Registro será virtualizado e, portanto, seu aplicativo não poderá registrar as extensões do Explorador de Arquivos por meio do Registro. Em vez disso, você precisa definir as extensões do Explorador de Arquivos por meio de extensões de pacote, que você define no manifesto do pacote. Este artigo descreve várias maneiras de fazer isso.

Você pode encontrar o código de exemplo completo usado neste artigo no GitHub.

Adicionar uma entrada de menu de contexto que dá suporte a parâmetros de inicialização

Uma das maneiras mais simples de se integrar com o Explorador de Arquivos é definir uma extensão de pacote que adiciona seu aplicativo à lista de aplicativos disponíveis no menu de contexto quando um usuário clica com o botão direito do mouse em um tipo de arquivo específico no Explorador de Arquivos. Se o usuário abrir o aplicativo, a extensão poderá passar parâmetros para esse aplicativo.

Esse cenário tem várias limitações:

  • Ele funciona apenas em combinação com o recurso de associação de tipo de arquivo. Você pode exibir opções adicionais no menu de contexto somente para tipos de arquivo que estão associados ao aplicativo principal (por exemplo, seu aplicativo dá suporte à abertura de um arquivo clicando duas vezes nele no Explorador de Arquivos).
  • As opções no menu de contexto serão exibidas somente se seu aplicativo estiver definido como padrão para esse tipo de arquivo.
  • A única ação compatível é iniciar o executável principal do aplicativo (ou seja, o mesmo executável que está conectado à entrada do menu Iniciar). No entanto, cada ação pode especificar parâmetros diferentes, que você pode usar quando os aplicativos começam a entender qual ação disparou a execução e executam tarefas diferentes.

Apesar dessas limitações, essa abordagem é suficiente para muitos cenários. Por exemplo, se você estiver criando um editor de imagens, poderá adicionar facilmente uma entrada no menu de contexto para redimensionar uma imagem, o que iniciará o editor de imagem diretamente com um assistente para iniciar o processo de redimensionamento.

Implementar a entrada do menu de contexto

Para dar suporte a esse cenário, adicione um elemento de extensão com a categoria windows.fileTypeAssociation ao manifesto do pacote. Esse elemento deve ser adicionado como um filho do elemento Extensions sob o elemento Application.

O exemplo a seguir demonstra um registro para um aplicativo que habilita menus de contexto para arquivos com a extensão .foo. Este exemplo especifica a extensão .foo porque essa é uma extensão falsa que normalmente não está registrada em outros aplicativos em um determinado computador. Se você precisar gerenciar um tipo de arquivo que já pode ser usado (como .txt ou .jpg), lembre-se de que você não poderá ver a opção até que o aplicativo seja definido como padrão para esse tipo de arquivo. Este exemplo é um trecho do arquivo Package.appxmanifest no exemplo relacionado no GitHub.

<Extensions>
  <uap3:Extension Category="windows.fileTypeAssociation">
    <uap3:FileTypeAssociation Name="foo" Parameters="&quot;%1&quot;">
      <uap:SupportedFileTypes>
        <uap:FileType>.foo</uap:FileType>
      </uap:SupportedFileTypes>
      <uap2:SupportedVerbs>
        <uap3:Verb Id="Resize" Parameters="&quot;%1&quot; /p">Resize file</uap3:Verb>
      </uap2:SupportedVerbs>
    </uap3:FileTypeAssociation>
  </uap3:Extension>
</Extensions>

Esse exemplo pressupõe que os namespaces e aliases a seguir são declarados no elemento raiz <Package> no manifesto.

<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
  xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap uap2 uap3 rescap">
  ...
</Package>

O elemento FileTypeAssociation associa o aplicativo aos tipos de arquivo aos quais você deseja dar suporte. Para obter mais detalhes, confira Associar o aplicativo empacotado a um conjunto de tipos de arquivo. Aqui estão os itens mais importantes relacionados a esse elemento.

Atributo ou elemento Descrição
Atributo Name Faz a correspondência do nome da extensão que você deseja registrar menos o ponto (no exemplo anterior, foo).
Atributo Parameters Contém os parâmetros que você deseja passar para o aplicativo quando o usuário clica duas vezes em um arquivo com essa extensão. Normalmente, pelo menos, você passa %1, que é um parâmetro especial que contém o caminho do arquivo selecionado. Dessa forma, quando você clicar duas vezes em um arquivo, o aplicativo saberá o caminho completo desse arquivo e poderá carregá-lo.
Elemento SupportedFileTypes Especifica os nomes da extensão que você deseja registrar, incluindo o ponto (neste exemplo, .foo). Você pode especificar várias entradas <FileType> caso deseje dar suporte a mais tipos de arquivo.

Para definir a integração do menu de contexto, você também precisa adicionar o elemento filho SupportedVerbs. Este elemento contém um ou mais elementos Verb que definem as opções que serão listadas quando um usuário clicar com o botão direito do mouse em um arquivo com a extensão .foo no Explorador de Arquivos. Para obter mais detalhes, confira Adicionar opções aos menus de contexto dos arquivos que têm um determinado tipo de arquivo. Aqui estão os itens mais importantes relacionados ao elemento Verb.

Atributo ou elemento Descrição
Atributo Id Especifica o identificador exclusivo da ação.
Atributo Parameters Semelhante ao elemento FileTypeAssociation, esse atributo para o elemento Verb contém os parâmetros que são passados para seu aplicativo quando o usuário clica na entrada do menu de contexto. Normalmente, além do parâmetro especial %1 para obter o caminho do arquivo selecionado, você também passa um ou mais parâmetros a fim de obter o contexto. Isso permite que seu aplicativo entenda que ele foi aberto de uma entrada de menu de contexto.
Valor do elemento O valor do elemento Verb contém o rótulo a ser exibido na entrada do menu de contexto (neste exemplo, Redimensionar arquivo).

Acessar os parâmetros de inicialização no código do aplicativo

A maneira como o aplicativo recebe os parâmetros depende do tipo de aplicativo que você criou. Por exemplo, um aplicativo WPF normalmente processa argumentos de evento de inicialização no método OnStartup da classe App. Você pode verificar se há parâmetros de inicialização e, com base no resultado, tomar a ação mais apropriada (como abrir uma janela específica do aplicativo em vez da principal).

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        if (e.Args.Contains("Resize"))
        {
            // Open a specific window of the app.
        }
        else
        {
            MainWindow main = new MainWindow();
            main.Show();
        }
    }
}

A captura de tela a seguir demonstra a entrada de menu de contexto Redimensionar Arquivo criada pelo exemplo anterior.

Screenshot of Resize file command in the shortcut menu

Suporte a arquivos ou pastas genéricos e execução de tarefas complexas

Embora o uso da extensão FileTypeAssociation no manifesto do pacote, conforme descrito na seção anterior, seja suficiente para muitos cenários, você pode considerá-lo limitador. Os dois maiores desafios são:

  • Você pode manipular apenas os tipos de arquivo aos quais está associado. Por exemplo, você não pode manipular uma pasta genérica.
  • Você pode iniciar o aplicativo apenas com uma série de parâmetros. Você não pode executar operações avançadas, como iniciar outro executável ou executar uma tarefa, sem abrir o aplicativo principal.

Para atingir essas metas, você precisa criar uma extensão de shell, que fornece maneiras mais poderosas de se integrar com o Explorador de Arquivos. Nesse cenário, você cria uma DLL que contém tudo o que é necessário para gerenciar o menu de contexto do arquivo, incluindo o rótulo, o ícone, o estado e as tarefas a serem executadas. Como essa funcionalidade é implementada em uma DLL, você pode fazer praticamente tudo o que poderia fazer com um aplicativo normal. Depois de implementar a DLL, você precisa registrá-la por meio de extensões definidas no manifesto do pacote.

Observação

O processo descrito nesta seção tem uma limitação. Depois que o pacote MSIX que contém a extensão é instalado em um computador de destino, o Explorador de Arquivos precisa ser reiniciado para que a extensão do shell possa ser carregada. Para fazer isso, o usuário pode reiniciar o computador ou pode reiniciar o processo de explorer.exe usando o Gerenciador de Tarefas.

Implementar a extensão do shell

As extensões do shell são baseadas em COM (Component Object Model). Sua DLL expõe um ou mais objetos COM que são registrados no Registro do sistema. O Windows descobre esses objetos COM e integra sua extensão ao Explorador de Arquivos. Já que você está integrando seu código com o shell do Windows, o desempenho e o volume de memória são importantes. Por isso, esses tipos de extensões normalmente são criados com o C++.

Para obter um exemplo de código que ilustra como implementar extensões de Shell, confira o projeto ExplorerCommandVerb no exemplo relacionado no GitHub. Este projeto se baseia neste exemplo, que está entre os exemplos de área de trabalho do Windows e tem várias revisões para facilitar o uso do exemplo com as versões mais recentes do Visual Studio.

Este projeto contém muitos códigos clichê para tarefas diferentes, como menus dinâmicos vs estáticos e o registro manual da DLL. A maior parte desse código não será necessária se você estiver empacotando seu aplicativo usando o MSIX, pois o suporte a empacotamento cuidará dessas tarefas para você. O arquivo ExplorerCommandVerb.cpp contém a implementação do menu de contexto e é o principal arquivo de código de interesse para este passo a passo.

A função principal é CExplorerCommandVerb::Invoke. Essa é a função que é invocada quando um usuário clica na entrada no menu de contexto. No exemplo, a operação é executada em outro thread para minimizar o impacto sobre o desempenho, então você encontrará a implementação real em CExplorerCommandVerb::_ThreadProc.

DWORD CExplorerCommandVerb::_ThreadProc()
{
	IShellItemArray* psia;
	HRESULT hr = CoGetInterfaceAndReleaseStream(_pstmShellItemArray, IID_PPV_ARGS(&psia));
	_pstmShellItemArray = NULL;
	if (SUCCEEDED(hr))
	{
		DWORD count;
		psia->GetCount(&count);

		IShellItem2* psi;
		HRESULT hr = GetItemAt(psia, 0, IID_PPV_ARGS(&psi));
		if (SUCCEEDED(hr))
		{
			PWSTR pszName;
			hr = psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszName);
			if (SUCCEEDED(hr))
			{
				WCHAR szMsg[128];
				StringCchPrintf(szMsg, ARRAYSIZE(szMsg), L"%d item(s), first item is named %s", count, pszName);

				MessageBox(_hwnd, szMsg, L"ExplorerCommand Sample Verb", MB_OK);

				CoTaskMemFree(pszName);
			}

			psi->Release();
		}
		psia->Release();
	}

	return 0;
}

Quando um usuário clica com o botão direito do mouse em um arquivo ou pasta, essa função exibe uma caixa de mensagem com o caminho completo do arquivo ou da pasta selecionada. Se você quiser personalizar a extensão do shell de outras maneiras, poderá estender as seguintes funções no exemplo:

  • Você pode alterar a função GetTitle para personalizar o rótulo da entrada no menu de contexto.
  • Você pode alterar a função GetIcon para personalizar o ícone exibido próximo da entrada no menu de contexto.
  • Você pode alterar a função GetTooltip para personalizar a dica de ferramenta que é exibida quando você focaliza a entrada no menu de contexto.

Registrar a extensão do shell

Como a extensão do shell se baseia em COM, a DLL de implementação deve ser exposta como um servidor COM para que o Windows possa integrá-lo ao Explorador de Arquivos. Normalmente, isso é feito atribuindo uma ID exclusiva (chamada CLSID) ao servidor COM e registrando-a em um hive específico do Registro do sistema. No projeto ExplorerCommandVerb, o CLSID para a extensão CExplorerCommandVerb é definido no arquivo Dll.h.

class __declspec(uuid("CC19E147-7757-483C-B27F-3D81BCEB38FE")) CExplorerCommandVerb;

Ao empacotar uma DLL de extensão de shell em um pacote MSIX, você segue uma abordagem semelhante. No entanto, o GUID deve ser registrado dentro do manifesto do pacote, em vez do Registro, conforme explicado aqui.

No manifesto do pacote, comece adicionando os namespaces a seguir ao elemento Package.

<Package
  xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
  xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
  xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" 
  IgnorableNamespaces="desktop desktop4 desktop5 com">
    
    ...
</Package>

Para registrar o CLSID, adicione um elemento com.Extension com a categoria windows.comServer ao manifesto do pacote. Esse elemento deve ser adicionado como um filho do elemento Extensions sob o elemento Application. Este exemplo é um trecho do arquivo Package.appxmanifest no exemplo relacionado no GitHub.

<com:Extension Category="windows.comServer">
  <com:ComServer>
    <com:SurrogateServer DisplayName="ContextMenuSample">
      <com:Class Id="CC19E147-7757-483C-B27F-3D81BCEB38FE" Path="ExplorerCommandVerb.dll" ThreadingModel="STA"/>
    </com:SurrogateServer>
  </com:ComServer>
</com:Extension>

Há dois atributos críticos a serem configurados no elemento com:Class.

Atributo Descrição
Atributo Id Esse atributo precisa corresponder ao CLSID do objeto que você deseja registrar. Neste exemplo, este é o CLSID declarado no arquivo Dll.h associado à classe CExplorerCommandVerb.
Atributo Path Esse atributo precisa conter o nome da DLL que expõe o objeto COM. Este exemplo inclui a DLL na raiz do pacote, portanto, ele pode apenas especificar o nome da DLL gerada pelo projeto ExplorerCommandVerb.

Em seguida, adicione outra extensão que registra o menu de contexto do arquivo. Para fazer isso, adicione um elemento desktop4:Extension com a categoria windows.fileExplorerContextMenus ao manifesto de pacote. Esse elemento também precisa ser adicionado como um filho do elemento Extensions sob o elemento Application.

<desktop4:Extension Category="windows.fileExplorerContextMenus">
  <desktop4:FileExplorerContextMenus>
    <desktop5:ItemType Type="Directory">
      <desktop5:Verb Id="Command1" Clsid="CC19E147-7757-483C-B27F-3D81BCEB38FE" />
    </desktop5:ItemType>
  </desktop4:FileExplorerContextMenus>
</desktop4:Extension>

Há dois atributos críticos a serem configurados no elemento desktop4:Extension.

Atributo ou elemento Descrição
Atributo Type de desktop5:ItemType Isso define o tipo de itens que você deseja associar ao menu de contexto. Ele pode ser uma estrela (*) se você quiser exibi-lo para todos os arquivos; pode ser uma extensão de arquivo específica (.foo); ou pode estar disponível para pastas (Directory).
Atributo Clsid de desktop5:Verb Esse atributo precisa corresponder ao CLSID que você registrou anteriormente como servidor COM no arquivo de manifesto do pacote.

Configurar a DLL no pacote

Inclua a DLL que implementa a extensão do Shell (neste exemplo, ExplorerCommandVerb.dll) na raiz do pacote MSIX. Se você estiver usando o Projeto de Empacotamento de Aplicativo do Windows, a solução mais fácil é copiar e colar a dll no projeto e verificar se a opção Copiar para o Diretório de Saída para as propriedades do arquivo dll está definida para Copiar se for mais recente.

Para assegurar-se de que o pacote sempre inclua a versão mais recente da DLL, você pode adicionar um evento pós-build ao projeto de extensão do shell para que, toda vez que você compilá-lo, a dll seja copiada para o Projeto de Empacotamento de Aplicativo do Windows.

Reiniciar o Explorador de Arquivos

Depois de instalar o pacote de extensão do Shell, você precisa reiniciar o Explorador de Arquivos antes que a extensão do Shell possa ser carregada. Essa é uma limitação das extensões do shell que são implantadas e registradas por meio de pacotes MSIX.

Para testar a extensão do Shell, reinicie o computador ou reinicie o processo de explorer.exe usando o Gerenciador de Tarefas. Depois de fazer isso, você poderá ver a entrada no menu de contexto.

Screenshot of the custom context menu entry

Se você clicar nela, a função CExplorerCommandVerb::_ThreadProc será chamada para exibir a caixa de mensagem com o caminho da pasta selecionada.

Screenshot of the custom popup