将未打包的应用与 Windows 共享集成

本文介绍如何将未打包的应用与 Windows 共享功能集成。 此“共享”功能允许用户将内容从一个 Windows 应用共享到另一应用。 必须为未打包的应用提供包标识,以便它能注册为共享目标。 注册后,此应用便可接收和处理共享文件。

如何将未打包的应用添加为共享目标:

  • 为应用提供包标识
  • 实现“共享”约定

为未打包的应用提供包标识

应用可通过两种方式获取包标识:

  • 创建新的 MSIX 安装包(首选方法),或是
  • 使通过外部位置打包的应用与当前的安装程序兼容。 这仅适用于具有现有安装程序且无法切换到 MSIX 安装的应用。

创建新的 MSIX 安装包

建议在 Visual Studio 中使用 Windows 应用程序打包项目模板并通过 MSIX 来打包此应用。 此操作将包括 MSIX 包中的所有二进制文件,并提供清洁且受信任的安装体验。

打包桌面应用之前需注意的事项:准备打包桌面应用程序 (MSIX)

按照在 Visual Studio 中为 MSIX 打包设置桌面应用程序的步骤,为现有应用的项目生成一个包。

注意

创建打包项目时,选择 Windows 10 2004 版(10.0;内部版本 19041) 或更高版本以作为最低版本

完成后,按照在 Visual Studio 中打包桌面或 UWP 应用的说明来创建包。

使通过外部位置进行打包与当前的安装程序兼容

提供应用包标识的第二种方法是向应用程序添加具有外部位置的包,并将其注册到现有安装程序。 具有外部位置的包是一个空的 MSIX 包,其中包含具有标识、共享目标注册和视觉资产的 .appxmanifest。 此应用的二进制文件仍由该应用的现有安装程序进行管理。 注册包时,需在 API 中提供此应用的安装位置。 请务必将 MSIX 包清单和 Win32 应用清单中的标识与用于对此应用进行签名的证书保持同步。

向未打包的应用授予包标识的步骤

此处提供了有关如何创建具有外部位置的包的文档,其中包括有关待使用模板的信息:通过使用外部位置进行打包来授予包标识

GitHub 上提供了完整示例应用:SparsePackages(使用外部位置进行打包)

注册为共享目标

应用具有包标识后,下一步便是实现“共享”合约。 “共享”合约允许你的应用从另一应用接收数据。

你可按本文档中注册为共享目标一节所述的步骤进行操作,以便打包的应用与共享工作表集成。

示例 PhotoStore 应用的演练

在有关未打包 Win32 应用程序的包标识、注册和共享激活的此演练中,你将了解如何通过创建具有外部位置的包以向未打包的 Win32 应用程序授予包标识。 此应用具有包标识后,它便可作为共享目标来进行注册和处理激活。 使用 PhotoStoreDemo 示例执行以下步骤:

  • 生成 AppxManifest.xml 文件
  • 创建包
  • 对包进行签名
  • 注册此包
  • 处理应用激活

首先创建 AppxManifest.xml 文件,其中包括必要的属性,例如 <AllowExternalContent>、标识、功能和共享目标扩展。 请确保 AppxManifest.xml 文件中的 PublisherPackageNameApplicationId 值与 PhotoStoreDemo.exe.manifest 文件中的值匹配。 Publisher 值还应与用于对包进行签名的证书中的值匹配。 按需添加视觉资产,并在 AppxManifest.xml 中进行引用。 在 Visual Studio 中,可在于“应用程序打包”项目中编辑 package.manifest 时使用视觉资产节点来生成所需的视觉资产。

允许接收外部内容的示例 AppxManifest.xml 代码片段如下:

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

Application.exe.manifest 文件的示例如下:

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

接下来,若要使用外部位置来创建包,请使用 MakeAppx.exe 工具和 /nv 命令来创建包含 AppxManifest.xml 文件的包。

示例:

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

注意

具有外部位置的包包含程序包清单,但不包含其他应用二进制文件和内容。 具有外部位置的包的清单可以在预定的外部位置引用包外部的文件。

使用 SignTool.exe 并通过受信任的证书对此包进行签名。

示例:

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

用于对此包进行签名的证书应安装在计算机上的受信任位置。

首次运行应用程序时,将此包注册到 Windows。 当应用具有自己的安装程序时,它还应包含已签名的 MSIX 以作为有效负载,同时还应将其置于指定位置(例如,此应用的安装位置)。 此应用在运行时必须知道该位置,因为此应用需要 MSIX 的绝对路径才能注册它。 此外,将资产和 resources.pri 置于此应用的安装位置。

此应用的 Main 方法的未打包执行示例的代码如下:

[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);
        }
    }
    ...

此示例演示了如何在首次运行该应用时注册 MSIX:

[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);
        }
    }
    ...

最后,处理此应用的激活:

[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 共享”演示

以下视频演示了未打包的应用在被授予包标识并注册为共享目标后如何成为共享目标:

另请参阅