앱에서 콘텐츠 받기 - Windows 공유 통합

Windows 공유 시트를 사용하면 앱을 통해 콘텐츠(다른 앱에서 공유)를 받을 수 있습니다. 이 가이드에서는 앱을 공유 대상으로 등록하고 MSIX(패키지 앱), PWA(프로그레시브 Web Apps) 및 패키지되지 않은 Win32 앱에서 공유 콘텐츠를 처리하는 방법을 설명합니다.

섹션 발견할 수 있는 것들
기능을 선언하기 전에 앱이 처리하는 파일 형식 및 형식만 선언합니다.
패키지된 앱에 대한 공유 대상 구현(UWP 및 패키지 데스크톱) UWP 및 패키지된 데스크톱 앱에 대한 매니페스트 선언 및 활성화 처리
PWA용 Share Target 구현하기 share_target 매니페스트 및 POST 처리
패키지되지 않은 Win32 앱에서 공유 받기 패키지 ID 부여 및 공유 대상으로 등록
모범 사례 신뢰할 수 있는 수신 흐름에 대한 권장 사항
보고서 수신 진행률 대규모 또는 오래 걸리는 공유 작업에 대한 상태 보고
Troubleshooting 일반적인 공유 대상 문제 해결

기능을 선언하기 전에

대부분의 공유 대상 통합 버그는 앱이 실제로 처리할 수 있는 것보다 더 많은 것을 선언하는 데서 비롯됩니다. 앱이 <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)에 적용됩니다. 둘 다 패키지 ID가 있는 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 앱이 리치 텍스트 콘텐츠를 수신합니다. 전자 메일 클라이언트, 풍부한 편집기
Uri / WebLink 앱이 링크를 처리합니다. 브라우저, 링크 관리자
Rtf 앱에서 서식이 지정된 텍스트를 받습니다. 워드 프로세서

자세한 내용은 DataFormat 및 FileType 참조를 참조하세요.

프로그레시브 웹 앱(PWA)용 Share Target 구현

Windows PWA는 웹앱 매니페스트를 통해 공유 대상으로 등록됩니다. 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 앱에서 공유 받기

공유 대상으로 등록하려면 앱에 패키지 ID가 필요합니다. Win32 앱이 패키지되지 않은 경우 다음 두 가지 방법 중 하나로 패키지 ID를 부여합니다.

  • MSIX를 사용하여 다시 패키징(기본 설정): Visual Studio Windows 애플리케이션 패키징 Project 템플릿을 사용하여 신뢰할 수 있는 깨끗한 설치를 수행합니다. MSIX 패키징에 대한 데스크톱 애플리케이션 설정을 참조하세요.
  • 외부 위치 (스파스 패키지)가 있는 패키지: ID, 공유 대상 등록 및 시각적 자산을 전달하는 빈 MSIX 패키지를 추가하고 기존 설치 관리자는 앱 이진 파일을 계속 관리합니다. MSIX로 이동할 수 없는 설치 관리자가 있는 경우에만 사용합니다.

이 섹션의 나머지 부분에서는 외부 위치 접근 방식을 안내합니다.

1. 패키지 매니페스트 작성

AppxManifest.xml를 설정하고, ID와 기능을 선언하며, 공유 대상을 등록하는 <uap10:AllowExternalContent>을 만듭니다. Publisher 및 서명 인증서와 PackageName, ApplicationId, .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>

실행 파일을 패키지 ID에 연결하는 애플리케이션 매니페스트(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. 처음 실행할 때 패키지 등록

처음 실행할 때 앱이 ID로 다시 시작되도록 외부 위치 패키지를 등록합니다. 외부 위치 및 서명 .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. 공유 활성화 처리

ID를 사용하여 앱을 다시 시작한 후 ExtendedActivationKind.ShareTarget에 표시된 대로 처리 합니다.

전체 예제는 PhotoStoreDemo 샘플(외부 위치로 패키지됨)WinUI 공유 대상 샘플을 참조하세요.

원본 쪽 데스크톱 공유의 경우 IDataTransferManagerInterop에 설명된 대로 사용합니다.

최선의 구현 방법

수신 흐름을 빌드할 때 이 검사 목록을 사용합니다.

권장 피하기 중요한 이유
특정 파일 확장자 및 데이터 형식만 선언 <uap:SupportsAnyFileType /> 파일 이동 앱이 아닌 앱에 대한 선언 Share Sheet에서 관련 없는 대상 표시 방지
처리하기 전에 형식, 개수, 파일 형식 및 파일 크기의 유효성을 검사합니다. 들어오는 데이터가 항상 예상과 일치한다고 가정 런타임 오류 및 중단된 공유 환경 방지
링크 처리기에 대해 Uri 선언하고 이미지 처리기에 대해 Bitmap + StorageItems 선언 공통 공유 페이로드에 대한 부분 선언 앱이 실제로 지원하는 콘텐츠에 대해 표시되는지 확인합니다.
장기 실행 수신 흐름에서 ReportStarted, ReportDataRetrievedReportCompleted 사용 진행률 보고 없이 장시간 실행되는 수신 작업 수행 공유 작업을 안정적으로 유지하고 시스템에 올바른 상태를 제공합니다.

큰 페이로드 또는 더 긴 처리의 경우 공유 대상에서 상태를 보고합니다.

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

Troubleshooting

내 앱이 공유 시트에 표시되지 않습니다.

  • 매니페스트 선언이 공유되는 콘텐츠와 일치하는지 확인합니다(파일 형식 및 데이터 형식 확인).
  • 패키지된 앱의 경우 패키지 ID를 사용하여 앱을 실행하고 있는지 확인합니다.
  • 앱 범주에 대한 DataFormat 및 FileType 참조 를 확인합니다.

내 앱이 처리할 수 없는 콘텐츠에 표시됨:

  • SupportedFileTypesDataFormat 목록을 지원하는 항목으로만 좁히세요.

공유 시트는 다음과 같은 오류로 해제됩니다.

  • 비동기 작업을 시작하기 전에 ReportStarted()를 호출하고, 완료되면 ReportCompleted()를 호출해야 합니다.
  • 예외를 처리하고 설명 메시지와 함께 ReportError()를 호출합니다.

예상한 파일이 수신되지 않습니다.

  • 파일 형식이 FileType 또는 DataFormat로 선언된 형식과 일치하는지 확인하세요.
  • 활성화 처리기에 유효성 검사 논리를 추가하여 실제로 도착하는 항목을 검사합니다.