애플리케이션 수명 주기 기능 마이그레이션

이 항목에는 응용 프로그램 수명 주기 영역에 대한 마이그레이션 참고 자료가 포함되어 있습니다.

중요 API

API 및/또는 기능 차이점 요약

UWP(유니버설 Windows 플랫폼) 앱은 기본적으로 단일 인스턴스화됩니다. WinUI 3(Windows 앱 SDK) 앱은 기본적으로 다중 인스턴스화됩니다.

UWP 앱에는 앱 활성화 방법을 암시적으로 알려주는 OnFileActivated, OnSearchActivatedOnActivated와 같은 App 메서드가 포함되어 있습니다. Windows 앱 SDK 앱의 App.OnLaunched(또는 모든 메서드)에서 (AppInstance.GetActivatedEventArgs)를 호출하여 활성화된 이벤트 인수를 검색하고 앱이 활성화된 방법을 확인합니다.

단일 인스턴스 앱

UWP(유니버설 Windows 플랫폼) 앱은 기본적으로 단일 인스턴스화됩니다(여러 인스턴스를 지원하도록 옵트인할 수 있음-인스턴스 UWP 앱 만들기 참조).

따라서 단일 인스턴스 UWP 앱이 동작하는 방식은 시작하는 두 번째(및 이후) 시간에 현재 인스턴스가 활성화되는 것입니다. 예를 들어 UWP 앱에서 파일 형식 연결 기능을 구현했다고 가정해 보겠습니다. 파일 탐색기에서 파일(앱이 파일 형식 연결을 등록한 형식)을 열면 앱이 이미 실행 중인 경우 이미 실행 중인 인스턴스가 활성화됩니다.

반면에 WinUI 3(Windows 앱 SDK) 앱은 기본적으로 다중 인스턴스화됩니다. 따라서 기본적으로 WinUI 3(Windows 앱 SDK) 앱을 시작하는 두 번째(및 후속) 시간에 앱의 새 인스턴스가 시작됩니다. 예를 들어 WinUI 3(Windows 앱 SDK) 앱이 파일 형식 연결을 구현하고 파일 탐색기에서 해당 앱이 이미 실행 중인 동안 올바른 형식의 파일을 열면 기본적으로 앱의 새 인스턴스가 시작됩니다.

UWP 앱처럼 WinUI 3(Windows 앱 SDK) 앱을 단일 인스턴스화하려는 경우 위에서 설명한 기본 동작을 재정의할 수 있습니다. AppInstance.FindOrRegisterForKeyAppInstance.IsCurrent를 사용하여 현재 인스턴스가 기본 인스턴스인지 확인합니다. 그렇지 않은 경우 AppInstance.RedirectActivationToAsync를 호출하여 활성화를 이미 실행 중인 기본 인스턴스로 리디렉션한 다음 주 창을 만들거나 활성화하지 않고 현재 인스턴스에서 종료합니다.

자세한 내용은 앱 수명 주기 API를 통해 앱 인스턴스화를 참조하세요.

중요

아래 표시된 코드는 x64 아키텍처를 대상으로 하는 경우 예상대로 작동합니다. 이는 C# 및 C++/WinRT 모두에 적용됩니다.

Main 또는 wWinMain의 단일 인스턴스화

앱 실행 시 활성화를 최대한 빨리 리디렉션해야 하는 필요성을 확인하는 것이 가장 좋습니다. 따라서 앱의 Main(또는 C++/WinRT의 경우 wWinMain) 함수에서 단일 인스턴스화 로직을 수행하는 것이 좋습니다. 이 섹션에서 그 방법을 보여 줍니다.

일반적으로 앱의 Main 함수는 빌드 시스템에서 자동 생성되어 숨겨진 파일에 저장됩니다. 따라서 첫 번째 단계는 해당 함수를 자동으로 생성하지 않도록 프로젝트를 구성하는 것입니다. 이를 수행하려면 속성 프로젝트에서 DISABLE_XAML_GENERATED_MAIN 기호를 정의합니다.

C#: 속성>(모든 구성모든 플랫폼 선택) >빌드>조건부 컴파일 기호로 이동하여 DISABLE_XAML_GENERATED_MAIN 기호를 붙여넣습니다.

C++/WinRT: 속성>(모든 구성모든 플랫폼 선택) >구성 속성>C/C++>전처리기>전처리기 정의 파일로 이동하여 값을 편집한 후 DISABLE_XAML_GENERATED_MAIN 기호를 추가합니다.

프로젝트에서 Main 함수를 자동 생성하지 못하도록 하기 때문에 프로젝트는 현재 빌드되지 않습니다. 따라서 두 번째 및 마지막 단계는 소스 코드 파일에서 해당 함수의 고유한 버전을 구현하는 것입니다.

C#: Class 유형의 새 프로젝트 항목을 프로젝트에 추가하고 이름을 Program.cs로 지정합니다. Program.cs 내에서 코드 class Program {}를 다음 목록으로 바꿉니다.

C++/WinRT: Microsoft.Windows.ImplementationLibrary NuGet 패키지에 대한 참조를 추가하고 아래 코드 목록에 표시된 대로 프로젝트 소스 코드 파일을 업데이트합니다(특정 프로젝트에 맞게 winrt::MYPROJECT::implementation::App의 네임스페이스를 변경해야 함).

#if DISABLE_XAML_GENERATED_MAIN
public static class Program
{
    [global::System.Runtime.InteropServices.DllImport("Microsoft.ui.xaml.dll")]
    private static extern void XamlCheckProcessRequirements();

    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.UI.Xaml.Markup.Compiler", " 1.0.0.0")]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.STAThreadAttribute]
    // Replaces the standard App.g.i.cs.
    // Note: We can't declare Main to be async because in a WinUI app
    // that prevents Narrator from reading XAML elements.
    static void Main(string[] args)
    {
        XamlCheckProcessRequirements();

        global::WinRT.ComWrappersSupport.InitializeComWrappers();

        bool this_is_the_first_instance = true;

        // If this is the first instance launched, then register it as the "main" instance.
        // If this isn't the first instance launched, then "main" will already be registered,
        // so retrieve it.
        var mainInstance = Microsoft.Windows.AppLifecycle.AppInstance.FindOrRegisterForKey("main");

        // If the instance that's executing the OnLaunched handler right now
        // isn't the "main" instance.
        if (!mainInstance.IsCurrent)
        {
            this_is_the_first_instance = false;

            // Redirect the activation (and args) to the "main" instance, and exit.
            var activatedEventArgs =
                Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
            await mainInstance.RedirectActivationToAsync(activatedEventArgs);
        }

        if (this_is_the_first_instance)
        {
            global::Microsoft.UI.Xaml.Application.Start((p) =>
            {
                var context = new global::Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext(global::Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread());
                global::System.Threading.SynchronizationContext.SetSynchronizationContext(context);
                new App();
            });
        }
    }
}
#endif
// pch.h
...
#include <winrt/Microsoft.Windows.AppLifecycle.h>
#include <wil/resource.h>

// App.xaml.cpp (paste the following code immediately after App::OnLaunched)
... 
#ifdef DISABLE_XAML_GENERATED_MAIN
// wil requires the Microsoft.Windows.ImplementationLibrary nuget.
// https://github.com/Microsoft/wil
wil::unique_event redirectEventHandle;

winrt::fire_and_forget Redirect(winrt::Microsoft::Windows::AppLifecycle::AppInstance const& mainInstance)
{
    auto activatedEventArgs{
        winrt::Microsoft::Windows::AppLifecycle::AppInstance::GetCurrent().GetActivatedEventArgs() };

    // Using this type of event ensures that it gets signaled when it 
    // goes out of scope, even if the RedirectActivationToAsync fails.
    wil::event_set_scope_exit ensure_signaled =
        wil::SetEvent_scope_exit(redirectEventHandle.get());
    co_await mainInstance.RedirectActivationToAsync(activatedEventArgs);
}

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    winrt::init_apartment();
    bool this_is_the_first_instance{ true };

    // If this is the first instance launched, then register it as the "main" instance.
    // If this isn't the first instance launched, then "main" will already be registered,
    // so retrieve it.
    auto mainInstance{
        winrt::Microsoft::Windows::AppLifecycle::AppInstance::FindOrRegisterForKey(L"main") };

    // If the instance that's executing right now isn't the "main" instance.
    if (!mainInstance.IsCurrent())
    {
        this_is_the_first_instance = false;

        // We're in an STA so we must not block the thread by
        // waiting on the async call. Instead, we'll move the call
        // to a separate thread, and use an event to synchronize.
        redirectEventHandle.create();
        Redirect(mainInstance);
        DWORD handleIndex = 0;
        HANDLE rawHandle = redirectEventHandle.get();
        if (::CoWaitForMultipleObjects(CWMO_DEFAULT, INFINITE, 1, &rawHandle, &handleIndex) != 0)
        {
            ::OutputDebugString(L"Error waiting on event");
        }
    }

    if (this_is_the_first_instance)
    {
        ::winrt::Microsoft::UI::Xaml::Application::Start(
            [](auto&&)
            {
                ::winrt::make<::winrt::MYPROJECT::implementation::App>();
            });
    }

    return 0;
}
#endif

C++/WinRT: "오류 C2872: 'Microsoft': 모호한 기호"를 해결하려면 using namespace Microsoft::UI::Xaml;을(를) using namespace winrt::Microsoft::UI::Xaml;(으)로 변경합니다. 그리고 using 지시문에 유사한 추가 변경을 수행합니다.

Application.OnLaunched의 단일 인스턴스화

Main 또는 wWinMain 사용에 대한 대안은 클래스의 Application.OnLaunched 메소드에서 단일 인스턴스화 로직을 수행하는 것입니다.

중요

Application.OnLaunched에서 이 작업을 수행하면 앱을 단순화할 수 있습니다. 그러나 앱이 수행하는 다른 작업에 따라 많은 것이 달라집니다. 리디렉션한 다음 현재 인스턴스를 종료하려는 경우, 버리기 작업(또는 명시적으로 실행 취소해야 하는 작업)을 수행하지 않는 것이 좋습니다. 이와 같은 경우 Application.OnLaunched가 너무 늦을 수 있으며 앱의 Main 또는 wWinMain 함수에서 작업을 수행하는 것이 좋습니다.

// App.xaml.cs in a Windows App SDK (WinUI 3) app
...
protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
    // If this is the first instance launched, then register it as the "main" instance.
    // If this isn't the first instance launched, then "main" will already be registered,
    // so retrieve it.
    var mainInstance = Microsoft.Windows.AppLifecycle.AppInstance.FindOrRegisterForKey("main");

    // If the instance that's executing the OnLaunched handler right now
    // isn't the "main" instance.
    if (!mainInstance.IsCurrent)
    {
        // Redirect the activation (and args) to the "main" instance, and exit.
        var activatedEventArgs =
            Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
        await mainInstance.RedirectActivationToAsync(activatedEventArgs);
        System.Diagnostics.Process.GetCurrentProcess().Kill();
        return;
    }

    m_window = new MainWindow();
    m_window.Activate();
}
// pch.h in a Windows App SDK (WinUI 3) app
...
#include <winrt/Microsoft.Windows.AppLifecycle.h>
...

// App.xaml.h
...
struct App : AppT<App>
{
    ...
    winrt::fire_and_forget OnLaunched(Microsoft::UI::Xaml::LaunchActivatedEventArgs const&);
    ...
}

// App.xaml.cpp
...
using namespace winrt;
using namespace Microsoft::Windows::AppLifecycle;
...
winrt::fire_and_forget App::OnLaunched(LaunchActivatedEventArgs const&)
{
    // If this is the first instance launched, then register it as the "main" instance.
    // If this isn't the first instance launched, then "main" will already be registered,
    // so retrieve it.
    auto mainInstance{ AppInstance::FindOrRegisterForKey(L"main") };

    // If the instance that's executing the OnLaunched handler right now
    // isn't the "main" instance.
    if (!mainInstance.IsCurrent())
    {
        // Redirect the activation (and args) to the "main" instance, and exit.
        auto activatedEventArgs{ AppInstance::GetCurrent().GetActivatedEventArgs() };
        co_await mainInstance.RedirectActivationToAsync(activatedEventArgs);
        ::ExitProcess(0);
        co_return;
    }

    window = make<MainWindow>();
    window.Activate();
}

또는 AppInstance.GetInstances를 호출하여 실행 중인 AppInstance 개체의 컬렉션을 검색할 수 있습니다. 해당 컬렉션의 요소 수가 1보다 큰 경우 기본 인스턴스가 이미 실행 중이며 해당 인스턴스로 리디렉션해야 합니다.

파일 형식 연결

Windows 앱 SDK 프로젝트에서 파일 형식 연결에 대한 확장점을 지정하려면 Package.appxmanifest 파일에서 UWP 프로젝트와 동일한 설정을 지정합니다. 이러한 설정은 다음과 같습니다.

Package.appxmanifest를 엽니다. 선언에서 파일 형식 연결을 선택하고 추가를 클릭합니다. 다음 속성을 설정합니다.

표시 이름: MyFile 이름: myfile 파일 형식: .myf

파일 형식 연결을 등록하려면 앱을 빌드하고 시작한 후 닫습니다.

차이점은 명령적 코드에서 발생합니다. UWP 앱에서는 파일 활성화를 처리하기 위해 App::OnFileActivated를 구현합니다. 그러나 Windows 앱 SDK 앱에서는 App::OnLaunched에 코드를 작성하여 활성화된 이벤트 인수(AppInstance.GetActivatedEventArgs) 및 활성화 종류(ExtendedActivationKind)가 파일 활성화인지 확인합니다.

참고

App::OnLaunched에 전달된 Microsoft.UI.Xaml.LaunchActivatedEventArgs 개체를 사용하여 활성화 종류를 결정하면 무조건 "Launch"를 보고하기 때문에 이러한 방법을 사용하지 마세요.

앱에 탐색이 있는 경우 App::OnLaunched에 탐색 코드가 이미 있으며 해당 논리를 다시 사용할 수 있습니다. 자세한 내용은 페이지 탐색을 구현해야 하나요?를 참조하세요.

// App.xaml.cs in a Windows App SDK app
...
using Microsoft.Windows.AppLifecycle;
...
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
    var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
    if (activatedEventArgs.Kind == Microsoft.Windows.AppLifecycle.ExtendedActivationKind.File)
    {
        ...
    }
    ...
}
// pch.h in a Windows App SDK app
...
#include <winrt/Microsoft.Windows.AppLifecycle.h>

// App.xaml.cpp
...
using namespace Microsoft::Windows::AppLifecycle;
...
void App::OnLaunched(LaunchActivatedEventArgs const&)
{
    auto activatedEventArgs{ AppInstance::GetCurrent().GetActivatedEventArgs() };
    if (activatedEventArgs.Kind() == ExtendedActivationKind::File)
    {
        ...
    }
    ...
}

OnActivated, OnBackgroundActivated 및 기타 활성화 처리 메서드

UWP 앱에서 앱을 활성화할 수 있는 다양한 수단을 재정의하기 위해 OnFileActivated, OnSearchActivated 또는 보다 일반적인 OnActivated와 같은 클래스의 해당 메서드를 재정의할 수 있습니다.

Windows 앱 SDK 앱의 App.OnLaunched에서(또는 실제로는 언제든지) (AppInstance.GetActivatedEventArgs)를 호출하여 활성화된 이벤트 인수를 검색하고 앱이 활성화된 방법을 확인할 수 있습니다.

자세한 내용 및 코드 예제는 위의 파일 형식 연결 섹션을 참조하세요. ExtendedActivationKind 열거형으로 지정된 모든 활성화 종류에 동일한 기술을 적용할 수 있습니다.