Integrate unpackaged apps with Windows Share

This article explains how to integrate unpackaged apps with the Windows Share feature. The Share feature allows users to share content from one Windows app to another. An unpackaged app must be provided with package identity so it can register as a Share Target. Once registered, the app can receive and process shared files.

How to onboard an unpackaged app as a Share Target:

  • Provide the app with package identity
  • Implement the Share contract

Provide unpackaged apps with package identity

An app can get package identity in two ways:

  • Make a new MSIX installation package (preferred method) OR
  • Make apps packaged with external location compatible with the current installer. This is only recommended for apps that have an existing installer and which can't switch to MSIX installation.

Make a new MSIX installation package

It's recommended to package the app with MSIX using the Windows Application Packaging Project template in Visual Studio. This will include all the binaries in the MSIX package and provide a clean and trusted install experience.

Things to note before packaging desktop apps: Prepare to package a desktop application (MSIX).

Follow the steps in Set up your desktop application for MSIX packaging in Visual Studio to generate a package for your existing app's project.

Note

When creating the packaging project, select Windows 10, version 2004 (10.0; Build 19041) or later as the Minimum version.

When that is completed, you will create the package by following Package a desktop or UWP app in Visual Studio.

Make packaging with external location compatible with the current installer

The second way to give your app package identity is to add a package with external location to your application and register it with your existing installer. The package with external location is an empty MSIX package that contains the .appxmanifest having identity, share target registration, and visual assets. The app's binaries are still managed by app's existing installer. When registering the package, you need to provide the app's install location in the API. It is important to keep the identity in the MSIX package manifest and the Win32 app manifest in sync with the certificate used for signing the app.

Steps to grant package identity to an unpackaged app

Documentation on how to create a package with external location is available here, including information on templates to use: Grant package identity by packaging with external location.

The full sample app is available on GitHub: SparsePackages (Packaged with External Location).

Register as a Share Target

Once the app has package identity, the next step is to implement the Share contract. The Share contract allows your app to receive data from another app.

You can follow the same steps in the Register as a Share Target section of the documentation for packaged apps to integrate with Share Sheet.

Walkthrough of the sample PhotoStore app

In this walkthrough on package identity, registration & share activation for unpackaged Win32 Applications, you will learn how to grant package identity to an unpackaged Win32 application by creating a package with external location. Once the app has package identity, it can register and handle activation as a Share Target. You will take the following steps using the PhotoStoreDemo sample:

  • Generate the AppxManifest.xml file
  • Create a package
  • Sign the package
  • Register the package
  • Handle app activation

Start by creating the AppxManifest.xml file, which includes necessary properties like <AllowExternalContent>, identity, and capabilities & share target extension. Make sure the Publisher, PackageName & ApplicationId values in the AppxManifest.xml file match the values in the PhotoStoreDemo.exe.manifest file. The Publisher value should also match the value in the certificate used to sign the package. Add visual assets as required and referenced in AppxManifest.xml. In Visual Studio, you can use the Visual Assets node when editing package.manifest in the Application Packaging project to generate required the visual assets.

This is a sample AppxManifest.xml snippet with external content allowed:

<Identity Name="PhotoStoreDemo" ProcessorArchitecture="neutral" Publisher="CN=YourPubNameHere" Version="1.0.0.0" />
  <Properties>
    <DisplayName>PhotoStoreDemo</DisplayName>
    <PublisherDisplayName>Sparse Package</PublisherDisplayName>
    <Logo>Assets\storelogo.png</Logo>
    <uap10:AllowExternalContent>true</uap10:AllowExternalContent>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.19041.0" />
  </Dependencies>
  <Capabilities>
    <rescap:Capability Name="runFullTrust" />
    <rescap:Capability Name="unvirtualizedResources"/>
  </Capabilities>
  <Applications>
    <Application Id="PhotoStoreDemo" Executable="PhotoStoreDemo.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
      <uap:VisualElements AppListEntry="none" DisplayName="PhotoStoreDemo" Description="PhotoStoreDemo" BackgroundColor="transparent" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png">
        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square310x310Logo="Assets\LargeTile.png" Square71x71Logo="Assets\SmallTile.png"></uap:DefaultTile>
        <uap:SplashScreen Image="Assets\SplashScreen.png" />
      </uap:VisualElements>
      <Extensions>
        <uap:Extension Category="windows.shareTarget">
          <uap:ShareTarget Description="Send to PhotoStoreDemo">
            <uap:SupportedFileTypes>
              <uap:FileType>.jpg</uap:FileType>
              <uap:FileType>.png</uap:FileType>
              <uap:FileType>.gif</uap:FileType>
            </uap:SupportedFileTypes>
            <uap:DataFormat>StorageItems</uap:DataFormat>
            <uap:DataFormat>Bitmap</uap:DataFormat>
          </uap:ShareTarget>
        </uap:Extension>
        ...

This is a sample Application.exe.manifest file:

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="PhotoStoreDemo.app"/>
  <msix xmlns="urn:schemas-microsoft-com:msix.v1"
          publisher="CN=YourPubNameHere"
          packageName="PhotoStoreDemo"
          applicationId="PhotoStoreDemo"
        />
</assembly>

Next, to create the package with external location, use the MakeAppx.exe tool with the /nv command to create a package containing the AppxManifest.xml file.​

Example:

MakeAppx.exe pack /d <Path to directory with AppxManifest.xml> /p <Output Path>\mypackage.msix /nv

Note

A package with external location contains a package manifest, but no other app binaries and content. The manifest of a package with external location can reference files outside the package in a predetermined external location.

Sign your package with a trusted certificate using SignTool.exe.​

Example:

SignTool.exe sign /fd SHA256 /a /f <path to cert>  /p <cert key> <Path to Package>​

The cert used for signing the package should be installed in a trusted location on the machine.

On first run of the application, register the package with Windows. When an app has its own installer, it should also contain the signed MSIX as the payload and should place it to a specified location (for example, the app's install location). This location must be known to the app at runtime because the app will need the absolute path of MSIX to register it. Place the assets and resources.pri in the app's install location as well.

The following code is an example of unpackaged execution of the app's Main method:

[STAThread]
public static void Main(string[] cmdArgs)
{
    //if app isn't running with identity, register its package with external identity
    if (!ExecutionMode.IsRunningWithIdentity())
    {
        //TODO - update the value of externalLocation to match the output location of your VS Build binaries and the value of 
        //externalPkgPath to match the path to your signed package with external identity (.msix). 
        //Note that these values cannot be relative paths and must be complete paths
        string externalLocation = Environment.CurrentDirectory;
        string externalPkgPath = externalLocation + @"\PhotoStoreDemo.package.msix";

        //Attempt registration
        bool bPackageRegistered = false;
        //bPackageRegistered = registerPackageWithExternalLocation(externalLocation, externalPkgPath);
        if (bPackageRegistered)
        {
            //Registration succeeded, restart the app to run with identity
            System.Diagnostics.Process.Start(Application.ResourceAssembly.Location, arguments: cmdArgs?.ToString());
        }
        else //Registration failed, run without identity
        {
            Debug.WriteLine("Package Registration failed, running WITHOUT Identity");
            SingleInstanceManager wrapper = new SingleInstanceManager();
            wrapper.Run(cmdArgs);
        }
    }
    ...

This example shows how to register the MSIX on first run of the app:

[STAThread]
public static void Main(string[] cmdArgs)
{
    //If app isn't running with identity, register its package with external identity
    if (!ExecutionMode.IsRunningWithIdentity())
    {
        //TODO - update the value of externalLocation to match the output location of your VS Build binaries and the value of 
        //externalPkgPath to match the path to your signed package with external identity (.msix). 
        //Note that these values cannot be relative paths and must be complete paths
        string externalLocation = Environment.CurrentDirectory;
        string externalPkgPath = externalLocation + @"\PhotoStoreDemo.package.msix";

        //Attempt registration
        if (registerPackageWithExternalLocation(externalLocation, externalPkgPath))
        {
            //Registration succeeded, restart the app to run with identity
            System.Diagnostics.Process.Start(Application.ResourceAssembly.Location, arguments: cmdArgs?.ToString());
        }
        else //Registration failed, run without identity
        {
            Debug.WriteLine("Package Registration failed, running WITHOUT Identity");
            SingleInstanceManager wrapper = new SingleInstanceManager();
            wrapper.Run(cmdArgs);
        }
    }
    ...

Finally, handle the app's activation:

[STAThread]
public static void Main(string[] cmdArgs)
{
    //if app isn't running with identity, register its sparse package
    if (!ExecutionMode.IsRunningWithIdentity())
    {
        ...
    }
    else //App is registered and running with identity, handle launch and activation
    {
        //Handle Sparse Package based activation e.g Share target activation or clicking on a Tile
        // Launching the .exe directly will have activationArgs == null
        var activationArgs = AppInstance.GetActivatedEventArgs();
        if (activationArgs != null)
        {
            switch (activationArgs.Kind)
            {
                case ActivationKind.Launch:
                    HandleLaunch(activationArgs as LaunchActivatedEventArgs);
                    break;
                case ActivationKind.ToastNotification:
                    HandleToastNotification(activationArgs as ToastNotificationActivatedEventArgs);
                    break;
                case ActivationKind.ShareTarget: // Handle the activation as a share target
                    HandleShareAsync(activationArgs as ShareTargetActivatedEventArgs);
                    break;
                default:
                    HandleLaunch(null);
                    break;
            }

        }
        //This is a direct exe based launch e.g. double click app .exe or desktop shortcut pointing to .exe
        else
        {
            SingleInstanceManager singleInstanceManager = new SingleInstanceManager();
            singleInstanceManager.Run(cmdArgs);
        }
    }

Windows Share with package identity demo

The following video demonstrates how an unpackaged app can be a share target after being granted package identity and registering as a share target:

See also