C++/WinRT의 Agile 개체Agile objects in C++/WinRT

대부분 경우 표준 C++ 개체 같은 Windows 런타임 클래스 인스턴스는(대부분의 표준 C++ 개체와 마찬가지로) 모든 스레드에서 액세스할 수 있습니다.In the vast majority of cases, an instance of a Windows Runtime class can be accessed from any thread (just like most standard C++ objects can). 이러한 Windows 런타임 클래스는 Agile입니다.Such a Windows Runtime class is agile. Windows와 함께 제공되는 소수의 Windows 런타임 클래스는 Agile이 아닙니다. 하지만 이러한 클래스를 사용할 때는 스레딩 모델과 마샬링 동작을 고려해야 합니다. 여기에서 마샬링이란 아파트 경계에서 데이터를 전달하는 것을 말합니다.Only a small number of Windows Runtime classes that ship with Windows are non-agile, but when you consume them you need to take into consideration their threading model and marshaling behavior (marshaling is passing data across an apartment boundary). 모든 Windows 런타임 개체가 Agile이도록 설정하면 기본적으로 사용자 고유의 C++/WinRT 형식도 Agile이기 때문에 매우 바람직합니다.It's a good default for every Windows Runtime object to be agile, so your own C++/WinRT types are agile by default.

하지만 옵트아웃으로 Agile을 선택하지 않을 수도 있습니다. 예를 들어, 단일 스레드 아파트처럼 경우에 따라 형식 개체가 상주해야 하는 이유가 존재하기 때문입니다.But you can opt out. You might have a compelling reason to require an object of your type to reside, for example, in a given single-threaded apartment. 이는 일반적으로 다시 표시 요구 사항과 관련이 있습니다.This typically has to do with reentrancy requirements. 하지만 점차 UI(사용자 인터페이스) API 조차도 Agile 개체를 제공하고 있습니다.But increasingly, even user interface (UI) APIs offer agile objects. 일반적으로 Agility는 가장 간단하면서 성능이 뛰어난 옵션입니다.In general, agility is the simplest and most performant option. 또한 활성화 팩터리를 구현할 때 해당하는 런타임 클래스가 Agile하지 않더라도 Agile을 설정해야 합니다.Also, when you implement an activation factory, it must be agile even if your corresponding runtime class isn't.

참고

Windows 런타임은 COM을 기반으로 합니다.The Windows Runtime is based on COM. COM 용어로 Agile 클래스는 ThreadingModel = Both로 등록됩니다.In COM terms, an agile class is registered with ThreadingModel = Both. COM 스레딩 모델 및 아파트에 대한 자세한 내용은 COM 스레딩 모델의 이해와 사용을 참조하세요.For more info about COM threading models, and apartments, see Understanding and Using COM Threading Models.

코드 예제Code examples

C++/WinRT가 Agility를 어떻게 지원하는지 살펴보기 위해 아래와 같은 런타임 클래스의 예제 구현을 사용합니다.Let's use an example implementation of a runtime class to illustrate how C++/WinRT supports agility.

#include <winrt/Windows.Foundation.h>

using namespace winrt;
using namespace Windows::Foundation;

struct MyType : winrt::implements<MyType, IStringable>
{
    winrt::hstring ToString(){ ... }
};

옵트아웃을 설정하지 않았기 때문에 이 구현은 Agile입니다.Because we haven't opted out, this implementation is agile. winrt::implements 기본 구조체는 IAgileObjectIMarshal을 구현합니다.The winrt::implements base struct implements IAgileObject and IMarshal. IMarshal 구현은 CoCreateFreeThreadedMarshaler를 사용하여 IAgileObject에 대해 알지 못하는 레거시 코드에 올바른 작업을 수행합니다.The IMarshal implementation uses CoCreateFreeThreadedMarshaler to do the right thing for legacy code that doesn't know about IAgileObject.

이 코드는 개체의 Agility 여부를 확인합니다.This code checks an object for agility. IUnknown::as 호출은 myimpl이 Agile이 아닐 경우 예외를 발생시킵니다.The call to IUnknown::as throws an exception if myimpl is not agile.

winrt::com_ptr<MyType> myimpl{ winrt::make_self<MyType>() };
winrt::com_ptr<IAgileObject> iagileobject{ myimpl.as<IAgileObject>() };

따라서 예외를 처리하지 않으려면 IUnknown::try_as를 호출하는 것이 좋습니다.Rather than handle an exception, you can call IUnknown::try_as instead.

winrt::com_ptr<IAgileObject> iagileobject{ myimpl.try_as<IAgileObject>() };
if (iagileobject) { /* myimpl is agile. */ }

IAgileObject는 자체적인 메서드가 없기 때문에 사용자가 많은 작업을 할 수 없습니다.IAgileObject has no methods of its own, so you can't do much with it. 이때는 아래 변형을 사용하는 것이 더욱 일반적입니다.This next variant, then, is more typical.

if (myimpl.try_as<IAgileObject>()) { /* myimpl is agile. */ }

IAgileObject마커 인터페이스입니다.IAgileObject is a marker interface. IAgileObject에 대한 쿼리의 성공 또는 실패 여부가 여기에서 얻을 수 있는 정보 및 유용성의 전부입니다.The mere success or failure of querying for IAgileObject is the extent of the information and utility you get from it.

Agile 개체 지원의 옵트아웃Opting out of agile object support

winrt::non_agile 마커 구조체를 템플릿 인수로 기본 클래스에 전달하여 명시적으로 Agile 개체 지원을 사용하지 않도록 옵트아웃을 선택할 수 있습니다.You can choose explicitly to opt out of agile object support by passing the winrt::non_agile marker struct as a template argument to your base class.

winrt::implements에서 직접 파생되는 경우If you derive directly from winrt::implements.

struct MyImplementation: implements<MyImplementation, IStringable, winrt::non_agile>
{
    ...
}

런타임 클래스를 작성하는 경우If you're authoring a runtime class.

struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, winrt::non_agile>
{
    ...
}

variadic 매개 변수 팩에서 마커 구조체가 표시되는 경우는 중요하지 않습니다.It doesn't matter where in the variadic parameter pack the marker struct appears.

사용자가 Agility를 옵트아웃하든, 혹은 하지 않든 IMarshal을 직접 구현할 수 있습니다.Whether or not you opt out of agility, you can implement IMarshal yourself. 예를 들어, winrt::non_agile 마커를 사용하여 기본 Agility 구현을 방지하거나, 혹은—아마도 값에 따른 마샬링(marshal-by-value) 의미 체계를 지원할 목적으로 직접 IMarshal을 구현할 수도 있습니다.For example, you can use the winrt::non_agile marker to avoid the default agility implementation, and implement IMarshal yourself—perhaps to support marshal-by-value semantics.

Agile 참조(winrt::agile_ref)Agile references (winrt::agile_ref)

Agile이 아닌 개체를 사용하지만 일부 Agile 가능성이 있는 컨텍스트일 때 개체를 전달해야 한다면 한 가지 옵션으로 winrt::agile_ref 구조체 템플릿을 사용하여 Agile 참조를 non-Agile 형식 인스턴스로, 혹은 non-Agile 개체의 인터페이스로 가져오는 방법이 있습니다.If you're consuming an object that isn't agile, but you need to pass it around in some potentially agile context, then one option is to use the winrt::agile_ref struct template to get an agile reference to an instance of a non-agile type, or to an interface of a non-agile object.

NonAgileType nonagile_obj;
winrt::agile_ref<NonAgileType> agile{ nonagile_obj };

그 밖에 winrt::make_agile 도우미 함수를 사용하는 방법도 있습니다.Or, you can use the use the winrt::make_agile helper function.

NonAgileType nonagile_obj;
auto agile{ winrt::make_agile(nonagile_obj) };

어떤 경우가 되었든 agile을 자유롭게 다른 아파트의 스레드로 전달하여 사용할 수 있습니다.In either case, agile may now be freely passed to a thread in a different apartment, and used there.

co_await resume_background();
NonAgileType nonagile_obj_again{ agile.get() };
winrt::hstring message{ nonagile_obj_again.Message() };

agile_ref::get 호출은 get이 호출되는 스레드 컨텍스트에서 안전하게 사용될 수 있도록 프록시를 반환합니다.The agile_ref::get call returns a proxy that may safely be used within the thread context in which get is called.

중요 APIImportant APIs