在应用中接收内容 - 集成Windows共享

Windows 共享界面允许你的应用接收来自其他应用共享的内容。 本指南介绍如何将应用注册为共享目标,并跨打包的应用(MSIX)、渐进式Web 应用(PWA)和未打包的 Win32 应用处理共享内容。

在本文中

Section 你将找到的内容
声明功能之前 仅声明应用可处理的文件类型和格式
实现打包应用的共享目标(UWP 和打包桌面) UWP 和打包桌面应用的清单声明和激活处理
实现 PWA 的共享目标 share_target 清单和 POST 处理
在未打包的 Win32 应用中接收共享内容 授予包标识并注册为共享目标
最佳实践 关于可靠接收流程的建议
报告接收进度 针对大型或长时间运行的共享任务的状态报告
故障排除 常见 Share Target 问题修复

在声明能力之前

大多数共享目标集成中的问题都源于 声明了超出你的应用实际处理能力的内容。 如果你的应用声明 <uap:SupportsAnyFileType />,它将出现在 每种 文件类型的共享工作表中,包括它无法处理的文件(例如,当用户共享电子表格时显示的照片编辑器)。

始终仅声明应用可以处理的特定文件类型和数据格式。 例如:

<!-- ✓ Correct: declare only what you support -->
<uap:SupportedFileTypes>
  <uap:FileType>.jpg</uap:FileType>
  <uap:FileType>.png</uap:FileType>
</uap:SupportedFileTypes>

<!-- ✗ Incorrect: declares everything -->
<!-- <uap:SupportsAnyFileType /> -->

仅将 <uap:SupportsAnyFileType /> 用于通用文件传输工具(如云存储、文件传输应用)。 请参阅 DataFormat 和 FileType 参考 ,了解按应用类别的声明。

实现打包应用的共享目标(UWP 和打包桌面)

本部分适用于 UWP 应用打包的桌面应用(WinUI 3、WPF、WinForms)。 这两个包都作为具有程序包标识的 MSIX 包提供,因此它们以相同的方式声明共享目标,并且仅在处理激活的方式(步骤 2 中所示)有所不同。

1. 在清单文件中声明

编辑你的 package.appxmanifest,以将其注册为共享目标。 声明应用可处理的文件类型和数据格式:

<Extensions>
  <uap:Extension Category="windows.shareTarget">
    <uap:ShareTarget>
      <uap:SupportedFileTypes>
        <uap:FileType>.jpg</uap:FileType>
        <uap:FileType>.jpeg</uap:FileType>
        <uap:FileType>.png</uap:FileType>
        <uap:FileType>.gif</uap:FileType>
        <uap:FileType>.bmp</uap:FileType>
      </uap:SupportedFileTypes>
      <uap:DataFormat>Bitmap</uap:DataFormat>
    </uap:ShareTarget>
  </uap:Extension>
</Extensions>

2. 处理共享激活

当你的应用被激活为共享目标时,请处理 OnShareTargetActivated 事件:

注释

OnShareTargetActivated 是 UWP 应用的激活重写(Windows.UI.Xaml.Application)。 打包的桌面应用(WinUI 3、WPF、WinForms)通过 AppInstance.GetActivatedEventArgs 接收共享激活,并检查 ExtendedActivationKind.ShareTarget。 请参阅 获取打包应用的激活信息

protected override async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    ShareOperation shareOperation = args.ShareOperation;
    shareOperation.ReportStarted();

    try
    {
        if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))
        {
            IReadOnlyList<IStorageItem> items = await shareOperation.Data.GetStorageItemsAsync();
            
            // Validate: check count, types, and sizes
            if (items.Count == 0)
            {
                shareOperation.ReportError("No items received.");
                return;
            }

            var file = (IStorageFile)items[0];
            
            // Process the file
            await ProcessImageAsync(file);
        }
        
        shareOperation.ReportCompleted();
    }
    catch (Exception ex)
    {
        shareOperation.ReportError($"Error: {ex.Message}");
    }
}

private async Task ProcessImageAsync(IStorageFile file)
{
    // Your processing logic here
}

对于使用Windows 应用 SDK生成的打包桌面应用(WinUI 3、WPF、WinForms),没有OnShareTargetActivated替代。 请改为检查 Main 方法中的激活情况,并查看是否存在 ExtendedActivationKind.ShareTarget

using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.DataTransfer;

[STAThread]
static void Main(string[] args)
{
    AppActivationArguments activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
    if (activatedArgs.Kind == ExtendedActivationKind.ShareTarget)
    {
        HandleShareAsync(activatedArgs.Data as ShareTargetActivatedEventArgs);
    }
    else
    {
        // Normal launch path
    }
}

static async void HandleShareAsync(ShareTargetActivatedEventArgs args)
{
    ShareOperation shareOperation = args.ShareOperation;
    shareOperation.ReportStarted();

    if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))
    {
        IReadOnlyList<IStorageItem> items = await shareOperation.Data.GetStorageItemsAsync();
        // Process the shared items.
    }

    shareOperation.ReportCompleted();
}

3.选择要声明的数据格式

使用此引用可决定声明的内容:

格式 何时使用 示例应用
StorageItems 你的应用接收文件 照片编辑器、文档阅读器
Bitmap 你的应用接收图像 图像查看器,设计应用
Text 你的应用接收纯文本 备注应用、文本编辑器
Html 你的应用接收 RTF 内容 电子邮件客户端,富文本编辑器
Uri / WebLink 您的应用可处理链接 浏览器,链接管理器
Rtf 你的应用接收带格式的文本 Word处理器

有关详细信息,请参阅 DataFormat 和 FileType 参考

为渐进式 Web 应用(PWA)实现共享目标

Windows 上的 PWA 通过 Web 应用清单注册为共享目标。 添加条目 share_target

{
  "name": "My PWA",
  "short_name": "MyPWA",
  "share_target": {
    "action": "/share",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "title",
      "text": "text",
      "url": "url",
      "files": [
        {
          "name": "media",
          "accept": ["image/*", "video/*"]
        }
      ]
    }
  }
}

/share 路由中,处理 POST 请求:

app.post('/share', async (req, res) => {
  const { title, text, url, files } = req.body;

  // Validate and process
  if (files && files.length > 0) {
    const file = files[0];
    // Process the file
    console.log('Received file:', file.originalname);
  }

  if (text) {
    console.log('Received text:', text);
  }

  res.redirect('/');
});

仅声明 PWA 可以处理的文件类型。 例如,除非应用真正处理所有文件,否则不要声明 * 为接受类型。

在未打包的 Win32 应用中接收共享内容

若要注册为共享目标,应用需要 包标识。 如果 Win32 应用已解压缩,请通过以下两种方式之一授予它包标识:

  • 使用 MSIX 重新打包(首选):使用Visual Studio中的Windows应用程序打包Project模板进行干净、受信任的安装。 请参阅 为桌面应用程序设置 MSIX 打包
  • 具有外部位置的包 (稀疏包):添加一个包含标识、共享目标注册和视觉资产的空 MSIX 包,而现有安装程序则继续管理应用二进制文件。 仅当你有一个无法迁移到 MSIX 的安装程序时,才使用此项。

本节其余内容将逐步介绍外部位置方法。

1. 编写包清单文件

创建一个 AppxManifest.xml,用于设置 <uap10:AllowExternalContent>、声明标识和功能,并注册共享目标。 使PublisherPackageNameApplicationId.exe.manifest及签名证书保持同步。

<Identity Name="PhotoStoreDemo" ProcessorArchitecture="neutral" Publisher="CN=YourPubNameHere" Version="1.0.0.0" />
<Properties>
  <uap10:AllowExternalContent>true</uap10:AllowExternalContent>
</Properties>
<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">
    <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:SupportedFileTypes>
          <uap:DataFormat>StorageItems</uap:DataFormat>
          <uap:DataFormat>Bitmap</uap:DataFormat>
        </uap:ShareTarget>
      </uap:Extension>
    </Extensions>
  </Application>
</Applications>

添加将可执行文件链接到包标识的应用程序清单(YourApp.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>

2.创建包并签名

使用 MakeAppx.exe/nv 开关生成仅包含清单的包,然后使用 SignTool.exe 和受信任的证书对其进行签名:

MakeAppx.exe pack /d <folder with AppxManifest.xml> /p <output>\mypackage.msix /nv
SignTool.exe sign /fd SHA256 /a /f <path to cert> /p <cert key> <path to package>

将签名证书安装到计算机上的受信任位置。

3.首次运行时注册包

首次运行时,请注册 external-location 程序包,以便应用能够以身份标识重新启动。 提供外部位置和已签名 .msix的绝对路径。

[STAThread]
public static void Main(string[] cmdArgs)
{
    if (!ExecutionMode.IsRunningWithIdentity())
    {
        string externalLocation = Environment.CurrentDirectory;
        string externalPkgPath = externalLocation + @"\PhotoStoreDemo.package.msix";

        if (registerPackageWithExternalLocation(externalLocation, externalPkgPath))
        {
            // Registration succeeded - restart so the app runs with identity.
            // Join the arguments into a single string; cmdArgs.ToString() would
            // return the array type name ("System.String[]"), not the arguments.
            string forwardedArgs = cmdArgs is null ? string.Empty : string.Join(" ", cmdArgs);
            Process.Start(Application.ResourceAssembly.Location, arguments: forwardedArgs);
        }
        else
        {
            // Registration failed - run without identity.
            new SingleInstanceManager().Run(cmdArgs);
        }
    }
}

4. 处理共享激活

应用使用标识重启后,处理 ExtendedActivationKind.ShareTarget 方式如 “处理共享激活”中所示。

有关完整示例,请参阅 PhotoStoreDemo 示例(采用外部位置打包)WinUI 共享目标示例

对于源端桌面共享,请按照从您的应用共享内容中的说明使用 IDataTransferManagerInterop

最佳做法

构建接收流程时,请使用此清单。

推荐 避免 它为何重要
仅声明特定的文件扩展名和数据格式 为非文件移动类应用声明 <uap:SupportsAnyFileType /> 防止无关的共享目标显示在共享菜单中
在处理之前验证格式、计数、文件类型和文件大小 假设传入数据始终符合预期 防止运行时故障和共享体验中断
为链接处理程序声明 Uri,为图像处理程序声明 Bitmap + StorageItems 通用共享负载的部分声明 确保您的应用仅针对其实际支持的内容显示
在长时间运行的接收流中使用 ReportStartedReportDataRetrievedReportCompleted 在不报告进度的情况下执行长时间运行的接收工作 保持共享操作的可靠性,并使系统处于正确状态

对于较大的负载或处理时间较长的情况,请由共享目标报告状态:

protected override async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
  ShareOperation shareOperation = args.ShareOperation;
  shareOperation.ReportStarted();

  try
  {
    // Acquire the data your app needs.
    var items = await shareOperation.Data.GetStorageItemsAsync();
    shareOperation.ReportDataRetrieved();

    // Process data.
    await ProcessAsync(items);

    shareOperation.ReportCompleted();
  }
  catch (Exception ex)
  {
    shareOperation.ReportError($"Share failed: {ex.Message}");
  }
}

如果您希望返回一个供后续分享使用的 QuickLink,请使用 ReportCompleted(QuickLink)

故障排除

我的应用未显示在“共享工作表”中:

  • 验证清单声明是否与共享的内容匹配(检查文件类型和数据格式)。
  • 对于打包的应用,请确保使用包标识运行应用。
  • 查看适用于您的应用类别的 DataFormat 和 FileType 参考文档

我的应用会针对它无法处理的内容显示出来:

  • 将你的 SupportedFileTypesDataFormat 列表缩小到你支持的内容。

共享面板因报错而关闭:

  • 请确保在进行任何异步工作之前调用 ReportStarted(),并在完成时调用 ReportCompleted()
  • 使用描述性消息处理异常和调用 ReportError()

我未收到我预期的文件:

  • 检查文件格式是否与已声明的 FileTypeDataFormat 相匹配。
  • 在激活处理程序中添加验证逻辑,以检查实际到达的内容。