Compartir a través de


Integración de una aplicación de escritorio empaquetada con el Explorador de archivos

Algunas aplicaciones de Windows definen extensiones del Explorador de archivos que agregan entradas de menú contextual que permiten a los clientes realizar opciones relacionadas con la aplicación. Las tecnologías de implementación de aplicaciones de Windows anteriores, como MSI y ClickOnce, definen extensiones del Explorador de archivos a través del Registro. El registro tiene una serie de subárboles que controlan las extensiones del Explorador de archivos y otros tipos de extensiones de Shell. Estos instaladores suelen crear una serie de claves del Registro para configurar los distintos elementos que se van a incluir en el menú contextual.

Si empaquetas la aplicación de Windows con MSIX, el registro se virtualiza y, por lo tanto, la aplicación no puede registrar extensiones del Explorador de archivos a través del registro. En su lugar, debe definir las extensiones del Explorador de archivos a través de extensiones de paquete, que se definen en el manifiesto del paquete. En este artículo se describen varias maneras de hacerlo.

Puede encontrar el código de ejemplo completo que se usa en este artículo en GitHub.

Agregar una entrada de menú contextual que admita parámetros de inicio

Una de las formas más sencillas de integrar con el Explorador de archivos es definir una extensión de paquete que agrega la aplicación a la lista de aplicaciones disponibles en el menú contextual cuando un usuario hace clic con el botón derecho en un tipo de archivo específico en el Explorador de archivos. Si el usuario abre la aplicación, la extensión puede pasar parámetros a la aplicación.

Este escenario tiene varias limitaciones:

  • Solo funciona en combinación con la característica de asociación de tipo de archivo . Puedes mostrar opciones adicionales en el menú contextual solo para los tipos de archivo asociados a la aplicación principal (por ejemplo, la aplicación admite abrir un archivo haciendo doble clic en él en el Explorador de archivos).
  • Las opciones del menú contextual solo se mostrarán si la aplicación está establecida como predeterminada para ese tipo de archivo.
  • La única acción admitida es iniciar el archivo ejecutable principal de la aplicación (es decir, el mismo ejecutable que está conectado a la entrada del menú Inicio). Sin embargo, cada acción puede especificar parámetros diferentes, que puede usar cuando las aplicaciones empiecen a comprender qué acción desencadenó la ejecución y realizar diferentes tareas.

A pesar de estas limitaciones, este enfoque es suficiente para muchos escenarios. Por ejemplo, si va a crear un editor de imágenes, puede agregar fácilmente una entrada en el menú contextual para cambiar el tamaño de una imagen, que iniciará el editor de imágenes directamente con un asistente para iniciar el proceso de cambio de tamaño.

Implementación de la entrada del menú contextual

Para admitir este escenario, agregue un elemento extensión de con la categoría al manifiesto del paquete. Este elemento debe agregarse como elemento secundario del elemento Extensions dentro del elemento Application.

En el ejemplo siguiente se muestra un registro para una aplicación que habilita menús contextuales para archivos con la extensión .foo. En este ejemplo se especifica la extensión .foo porque se trata de una extensión falsa que normalmente no está registrada en otras aplicaciones de ningún equipo determinado. Si necesita administrar un tipo de archivo que ya se puede tomar (como .txt o .jpg), recuerde que no podrá ver la opción hasta que la aplicación esté establecida como predeterminada para ese tipo de archivo. Este ejemplo es un extracto del archivo Package.appxmanifest del ejemplo relacionado en 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>

En este ejemplo se supone que los siguientes espacios de nombres y alias se declaran en el elemento raíz <Package> del manifiesto.

<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>

El elemento FileTypeAssociation asocia la aplicación con los tipos de archivo que quiere admitir. Para obtener más información, consulte Asociar la aplicación empaquetada con un conjunto de tipos de archivo. Estos son los elementos más importantes relacionados con este elemento.

Atributo o elemento Descripción
atributo Name Coincide con el nombre de la extensión que desea registrar menos el punto (en el ejemplo anterior, foo).
atributo Parameters Contiene los parámetros que desea pasar a la aplicación cuando el usuario hace doble clic en un archivo con dicha extensión. Normalmente, al menos, se pasa %1, que es un parámetro especial que contiene la ruta de acceso del archivo seleccionado. De este modo, al hacer doble clic en un archivo, la aplicación conoce su ruta de acceso completa y puede cargarla.
elemento SupportedFileTypes Especifica los nombres de la extensión que desea registrar, incluido el punto (en este ejemplo, .foo). Puede especificar varias entradas <FileType> para admitir más tipos de archivo.

Para definir la integración del menú contextual, también debe agregar el elemento secundario SupportedVerbs. Este elemento contiene uno o varios elementos Verb que definen las opciones que se mostrarán cuando un usuario haga clic con el botón derecho en un archivo con la extensión .foo en el Explorador de Archivos. Para obtener más información, vea Agregar opciones a los menús contextuales de los archivos que tienen un tipo de archivo determinado. Aquí están los elementos más importantes relacionados con el elemento verbo .

Atributo o elemento Descripción
atributo Id Especifica el identificador único de la acción.
atributo Parameters Al igual que el elemento FileTypeAssociation, este atributo para el elemento Verb contiene los parámetros que se pasan a la aplicación cuando el usuario hace clic en la entrada del menú contextual. Aparte del parámetro especial %1 para obtener la ruta del archivo seleccionado, se suelen pasar también uno o más parámetros para obtener el contexto. Esto permite a la aplicación comprender que se abrió desde una entrada de menú contextual.
Valor del elemento El valor del elemento Verb contiene la etiqueta que se va a mostrar en la entrada del menú contextual (en este ejemplo, Cambiar tamaño del archivo).

Acceso a los parámetros de inicio en el código de la aplicación

La forma en que la aplicación recibe los parámetros depende del tipo de aplicación que haya creado. Por ejemplo, una aplicación WPF normalmente procesa argumentos de eventos de inicio en el método OnStartup de la clase App. Puede comprobar si hay parámetros de inicio y, en función del resultado, realizar la acción más adecuada (como abrir una ventana específica de la aplicación en lugar de la 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();
        }
    }
}

En la captura de pantalla siguiente se muestra la entrada del menú contextual Cambiar tamaño de archivo creada por el ejemplo anterior.

Captura de pantalla del comando Cambiar tamaño del archivo en el menú contextual

Compatibilidad con archivos o carpetas genéricos y realizar tareas complejas

Aunque el uso de la extensión FileTypeAssociation de en el manifiesto del paquete, como se describe en la sección anterior, es suficiente para muchos escenarios, es posible que lo encuentre limitante. Los dos mayores desafíos son:

  • Solo puede controlar los tipos de archivo con los que está asociado. Por ejemplo, no puede controlar una carpeta genérica.
  • Solo puede iniciar la aplicación con una serie de parámetros. No puede realizar operaciones avanzadas, como iniciar otro ejecutable o realizar una tarea sin abrir la aplicación principal.

Para lograr estos objetivos, debe crear una extensión de shell , que proporciona formas más eficaces de integrar con el Explorador de archivos. En este escenario, creará un archivo DLL que contiene todo lo necesario para administrar el menú contextual del archivo, incluida la etiqueta, el icono, el estado y las tareas que se van a realizar. Dado que esta funcionalidad se implementa en un archivo DLL, puede hacer casi todo lo que puede hacer con una aplicación normal. Después de implementar el archivo DLL, debe registrarlo a través de extensiones que defina en el manifiesto del paquete.

Nota:

El proceso descrito en esta sección tiene una limitación. Después de instalar el paquete MSIX que contiene la extensión en un equipo de destino, se debe reiniciar el Explorador de archivos antes de que se pueda cargar la extensión de Shell. Para ello, el usuario puede reiniciar el equipo o puede reiniciar el proceso de explorer.exe mediante Administrador de tareas.

Implementación de la extensión de Shell

Las extensiones de shell se basan en COM (modelo de objetos componentes). El archivo DLL expone uno o varios objetos COM registrados en el registro del sistema. Windows detecta estos objetos COM e integra la extensión con el Explorador de archivos. Dado que va a integrar el código con el Shell de Windows, es importante el rendimiento y la superficie de memoria. Por lo tanto, estos tipos de extensiones normalmente se compilan con C++.

Para obtener código de ejemplo que muestra cómo implementar extensiones de Shell, consulte el proyecto ExplorerCommandVerb en el ejemplo relacionado de GitHub. Este proyecto se basa en este ejemplo en los ejemplos de escritorio de Windows y tiene varias revisiones para que el ejemplo sea más fácil de usar con las versiones más recientes de Visual Studio.

Este proyecto contiene una gran cantidad de código reutilizable para diferentes tareas, como menús dinámicos frente a estáticos y registro manual del archivo DLL. La mayoría de este código no es necesario si empaquetas tu aplicación mediante MSIX, ya que el soporte de empaquetado se encargará de estas tareas. El archivo ExplorerCommandVerb.cpp contiene la implementación del menú contextual y este es el archivo de código principal de interés para este tutorial.

La función clave es CExplorerCommandVerb::Invoke. Esta es la función que se invoca cuando un usuario hace clic en la entrada del menú contextual. En el ejemplo, para minimizar el impacto en el rendimiento, la operación se realiza en otro subproceso, por lo que realmente encontrará la implementación real en 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;
}

Cuando un usuario hace clic con el botón derecho en un archivo o carpeta, esta función muestra un cuadro de mensaje con la ruta de acceso completa del archivo o carpeta seleccionado. Si desea personalizar la extensión de Shell de otras maneras, puede ampliar las siguientes funciones en el ejemplo:

  • Puede cambiar la función GetTitle para personalizar la etiqueta de la entrada en el menú contextual.
  • Puede cambiar la función getIcon para personalizar el icono que se muestra cerca de la entrada en el menú contextual.
  • Puede cambiar la función GetTooltip para personalizar el tooltip que se muestra al mantener el puntero sobre la entrada en el menú contextual.

Registro de la extensión de Shell

Dado que la extensión shell se basa en COM, el archivo DLL de implementación debe exponerse como un servidor COM para que Windows pueda integrarlo con el Explorador de archivos. Normalmente, esto se hace asignando un identificador único (denominado CLSID) al servidor COM y registrándolo en un subárbol específico del registro del sistema. En el proyecto de ExplorerCommandVerb, el CLSID de la extensión CExplorerCommandVerb se define en el archivo Dll.h.

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

Al empaquetar un archivo DLL de extensión de Shell en un paquete MSIX, sigue un enfoque similar. Sin embargo, el GUID debe registrarse dentro del manifiesto del paquete en lugar del registro, como se explica aquí.

En el manifiesto del paquete, empieza agregando los siguientes Namespaces al 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 el CLSID, agregue un elemento com.Extension con la categoría windows.comServer al manifiesto del paquete. Este elemento debe agregarse como elemento secundario del elemento Extensions dentro del elemento Application. Este ejemplo es un extracto del archivo Package.appxmanifest del ejemplo relacionado en 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>

Hay dos atributos críticos para configurar en el elemento com:Class.

Atributo Descripción
atributo Id Debe coincidir con el CLSID del objeto que desea registrar. En este ejemplo, este es el CLSID declarado en el archivo Dll.h asociado a la clase CExplorerCommandVerb.
atributo Path Debe contener el nombre del archivo DLL que expone el objeto COM. En este ejemplo se incluye el archivo DLL en la raíz del paquete, por lo que solo puede especificar el nombre del archivo DLL generado por el proyecto de ExplorerCommandVerb.

A continuación, agregue otra extensión que registre el menú contextual del archivo. Para ello, agregue un elemento desktop4:Extension con la categoría windows.fileExplorerContextMenus al manifiesto del paquete. Este elemento también debe agregarse como elemento secundario del elemento Extensiones en el elemento Aplicación.

<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>

Hay dos atributos críticos para configurar en el elemento desktop4:Extension.

Atributo o elemento Descripción
atributo Type de desktop5:ItemType Esto define el tipo de elementos que desea asociar al menú contextual. Podría ser una estrella (*) si desea mostrarla para todos los archivos; podría ser una extensión de archivo específica (.foo); o puede estar disponible para carpetas (Directory).
Clsid atributo de desktop5:Verb Debe coincidir con el CLSID que ha registrado previamente como servidor COM en el archivo de manifiesto del paquete.

Configuración del archivo DLL en el paquete

Incluya el archivo DLL que implementa la extensión shell (en este ejemplo, ExplorerCommandVerb.dll) en la raíz del paquete MSIX. Si estás utilizando el Proyecto de empaquetado de aplicaciones de Windows , la solución más sencilla es copiar y pegar el archivo DLL en el proyecto y asegurarte de que la opción 'Copiar al directorio de salida' para las propiedades del archivo DLL esté establecida en 'Copiar si es más reciente' .

Para asegurarse de que el paquete siempre incluye la versión más reciente del archivo DLL, puede agregar un evento posterior a la compilación al proyecto de extensión shell para que, cada vez que lo compile, el archivo DLL se copia en el proyecto de empaquetado de aplicaciones de Windows.

Reiniciar el Explorador de archivos

Después de instalar el paquete de extensión de Shell, debe reiniciar el Explorador de archivos antes de que se pueda cargar la extensión de Shell. Se trata de una limitación de las extensiones de Shell que se implementan y registran a través de paquetes MSIX.

Para probar la extensión shell, reinicie el equipo o reinicie el proceso de explorer.exe mediante administrador de tareas. Después de hacerlo, debería poder ver la entrada en el menú contextual.

Captura de pantalla de la entrada del menú contextual personalizado

Si hace clic en él, se llamará a la función CExplorerCommandVerb::_ThreadProc para mostrar el cuadro de mensaje con la ruta de acceso de la carpeta seleccionada.

Captura de pantalla del popup personalizado