이 항목에서는 C++/WinRT API를 사용하는 방법을 보여 줍니다. C++/WinRT API는 Windows 속해 있거나 타사 구성 요소 공급업체에서 구현하거나 직접 구현합니다.
중요합니다
이 항목의 코드 예제는 짧고 쉽게 사용해 볼 수 있도록 새 Windows 콘솔 애플리케이션(C++/WinRT) 프로젝트를 만들고 코드를 복사하여 재현할 수 있습니다. 그러나 패키지되지 않은 앱에서는 임의의 사용자 지정(타사) Windows 런타임 형식을 사용할 수 없습니다. 이러한 방식으로 Windows 형식만 사용할 수 있습니다.
콘솔 앱에서 사용자 지정(타사) Windows 런타임 형식을 사용하려면 사용된 사용자 지정 형식의 등록을 확인할 수 있도록 앱에 패키지 ID를 제공해야 합니다. 자세한 내용은 Windows 애플리케이션 패키징 Project 참조하세요.
또는 C++ 또는 Windows 런타임 구성 요소(C++/WinRT) 프로젝트 템플릿용 빈 앱, 패키지된 프로젝트(데스크톱의 WinUI 3)에서 새 프로젝트를 만듭니다. 이러한 앱 유형에는 이미 패키지 ID가 있습니다.
API가 Windows 네임스페이스에 있는 경우
Windows 런타임 API를 사용하는 가장 일반적인 경우입니다. 메타데이터에 정의된 Windows 네임스페이스의 모든 형식에 대해 C++/WinRT는 C++친화적인 동등한 형식(프로젝션된 형식이라고 함)을 정의합니다. 프로젝션된 형식은 Windows 형식과 정규화된 이름이 동일하지만 C++ 구문을 사용하여 C++ winrt 네임스페이스에 배치됩니다. 예를 들어 Windows::Foundation::Uri는 winrt::Windows::Foundation::Uri로 C++/WinRT로 프로젝션됩니다.
다음은 간단한 코드 예제입니다. 다음 코드 예제를 복사하여 Windows 콘솔 애플리케이션(C++/WinRT) 프로젝트의 주 소스 코드 파일에 직접 붙여넣으려면 먼저 프로젝트 속성에서 미리 컴파일된 헤더를 사용하지 않도록 설정합니다.
// main.cpp
#include <winrt/Windows.Foundation.h>
using namespace winrt;
using namespace Windows::Foundation;
int main()
{
winrt::init_apartment();
Uri contosoUri{ L"http://www.contoso.com" };
Uri combinedUri = contosoUri.CombineUri(L"products");
}
포함된 헤더 winrt/Windows.Foundation.h 는 폴더 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt\내에 있는 SDK의 일부입니다. 해당 폴더의 헤더에는 C++/WinRT로 프로젝션된 Windows 네임스페이스 형식이 포함됩니다. 이 예제 winrt/Windows.Foundation.h 에서는 런타임 클래스 Windows::Foundation::Uri에 대해 프로젝션된 형식인 winrt::Windows::Foundation::Uri를 포함합니다.
팁 (조언)
Windows 네임스페이스의 형식을 사용하려는 경우 해당 네임스페이스에 해당하는 C++/WinRT 헤더를 포함합니다.
using namespace 지시문은 선택 사항이지만 편리합니다.
위의 코드 예제에서 C++/WinRT를 초기화한 후 공개적으로 문서화된 생성자(이 예제에서는 Uri(String)) 중 하나를 통해 winrt::Windows::Foundation::Uri 프로젝션된 형식의 값을 스택 할당합니다. 이 가장 일반적인 사용 사례의 경우, 보통 그것만 하면 됩니다. C++/WinRT 프로젝션된 형식 값이 있으면 모든 멤버가 동일하므로 실제 Windows 런타임 형식의 인스턴스인 것처럼 처리할 수 있습니다.
실제로 그 프로젝션된 값은 프록시이며, 본질적으로 기반 객체를 가리키는 스마트 포인터일 뿐입니다. 프로젝터된 값의 생성자는 RoActivateInstance를 호출하여 백업 Windows 런타임 클래스(Windows)의 인스턴스를 만듭니다. Foundation.Uri(이 경우)) 해당 개체의 기본 인터페이스를 새 프로젝션된 값 내에 저장합니다. 아래 그림과 같이 프로젝티드 값의 멤버 호출은 실제로 스마트 포인터를 통해 기반 객체로 위임되며, 상태 변경은 바로 그 객체에서 일어납니다.
값이 contosoUri 범위를 벗어나면 해당 값이 소멸되고 기본 인터페이스에 대한 참조가 해제됩니다. 해당 참조가 기반이 되는 Windows 런타임 Windows.Foundation.Uri 개체에 대한 마지막 참조인 경우, 해당 기반 개체도 함께 소멸됩니다.
팁 (조언)
프로젝션 형식은 API를 사용하기 위한 Windows 런타임 형식을 감싸는 래퍼입니다. 예를 들어 프로젝션된 인터페이스는 Windows 런타임 인터페이스에 대한 래퍼입니다.
C++/WinRT 프로젝션 헤더
C++/WinRT에서 Windows 네임스페이스 API를 사용하려면 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt 폴더의 헤더를 포함해야 합니다. 사용하는 각 네임스페이스에 해당하는 헤더를 포함해야 합니다.
예를 들어 Windows::Security::Cryptography::Certificates 네임스페이스의 경우 해당하는 C++/WinRT 형식 정의가 있습니다winrt/Windows.Security.Cryptography.Certificates.h. 이 헤더를 포함하면 Windows::Security::Cryptography::Certificates 네임스페이스의 모든 형식에 액세스할 수 있습니다.
경우에 따라 하나의 네임스페이스 헤더에 관련 네임스페이스 헤더의 일부가 포함되지만 이 구현 세부 정보에 의존해서는 안 됩니다. 사용하는 네임스페이스의 헤더를 명시적으로 포함합니다.
예를 들어 Certificate::GetCertificateBlob 메서드는 Windows::Storage::Streams::IBuffer 인터페이스를 반환합니다.
Certificate::GetCertificateBlob 메서드를 호출하기 전에 반환된 winrt/Windows.Storage.Streams.h에서 수신하고 작동할 수 있도록 네임스페이스 헤더 파일을 포함 해야 합니다.
해당 네임스페이스에서 형식을 사용하기 전에 필요한 네임스페이스 헤더를 포함하는 것을 잊어버리는 것은 빌드 오류의 일반적인 원인입니다.
개체를 통해, 인터페이스를 통해 또는 ABI를 통해 멤버에 액세스
C++/WinRT 프로젝션을 사용하면 Windows 런타임 클래스의 런타임 표현이 기본 ABI 인터페이스에 지나지 않습니다. 그러나 편의상 작성자가 의도한 방식으로 클래스에 대해 코딩할 수 있습니다. 예를 들어 Uri의 ToString 메서드를 클래스의 메서드인 것처럼 호출할 수 있습니다(실제로는 별도의 IStringable 인터페이스의 메서드임).
WINRT_ASSERT 는 매크로 정의이며 _ASSERTE 확장됩니다.
Uri contosoUri{ L"http://www.contoso.com" };
WINRT_ASSERT(contosoUri.ToString() == L"http://www.contoso.com/"); // QueryInterface is called at this point.
이 편의성은 적절한 인터페이스에 대한 쿼리를 통해 수행됩니다. 하지만 당신은 항상 통제할 수 있습니다. 원한다면 IStringable 인터페이스를 직접 가져와 바로 사용함으로써 약간의 성능 향상을 위해 그 편의성의 일부를 포기할 수 있습니다. 아래 코드 예제에서는 런타임에(일회성 쿼리를 통해) 실제 IStringable 인터페이스 포인터를 가져옵니다. 그 후 ToString 에 대한 호출은 직접이며 QueryInterface에 대한 추가 호출을 방지합니다.
...
IStringable stringable = contosoUri; // One-off QueryInterface.
WINRT_ASSERT(stringable.ToString() == L"http://www.contoso.com/");
동일한 인터페이스에서 여러 메서드를 호출할 것을 알고 있는 경우 이 기술을 선택할 수 있습니다.
참고로, 정말 ABI 수준에서 멤버에 접근하고 싶다면 그렇게 할 수 있습니다. 아래 코드 예제는 그 방법을 보여 주며, C++/WinRT와 ABI 간 상호 운용에 더 자세한 내용과 코드 예제가 나와 있습니다.
#include <Windows.Foundation.h>
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
using namespace winrt::Windows::Foundation;
int main()
{
winrt::init_apartment();
Uri contosoUri{ L"http://www.contoso.com" };
int port{ contosoUri.Port() }; // Access the Port "property" accessor via C++/WinRT.
winrt::com_ptr<ABI::Windows::Foundation::IUriRuntimeClass> abiUri{
contosoUri.as<ABI::Windows::Foundation::IUriRuntimeClass>() };
HRESULT hr = abiUri->get_Port(&port); // Access the get_Port ABI function.
}
지연된 초기화
C++/WinRT에서 프로젝션된 각 형식에는 특수 C++/WinRT std::nullptr_t 생성자가 있습니다. 그 하나를 제외하면, 기본 생성자를 포함한 모든 프로젝션 형식 생성자는 기반 Windows 런타임 개체를 생성하고 그 개체에 대한 스마트 포인터를 제공합니다. 따라서 이 규칙은 초기화되지 않은 지역 변수, 초기화되지 않은 전역 변수 및 초기화되지 않은 멤버 변수와 같이 기본 생성자가 사용되는 모든 위치에 적용됩니다.
반면에 프로젝션된 형식의 변수를 생성하지 않고 백업 Windows 런타임 개체를 생성하려는 경우(나중에 해당 작업을 지연할 수 있도록) 그렇게 할 수 있습니다. 해당 특수 C++/WinRT std::nullptr_t 생성자(C++/WinRT 프로젝션이 모든 런타임 클래스에 삽입)를 사용하여 변수 또는 필드를 선언합니다. 아래 코드 예제에서 해당 특수 생성자를 m_gamerPicBuffer 사용합니다.
#include <winrt/Windows.Storage.Streams.h>
using namespace winrt::Windows::Storage::Streams;
#define MAX_IMAGE_SIZE 1024
struct Sample
{
void DelayedInit()
{
// Allocate the actual buffer.
m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
}
private:
Buffer m_gamerPicBuffer{ nullptr };
};
int main()
{
winrt::init_apartment();
Sample s;
// ...
s.DelayedInit();
}
투영된 형식의 모든 생성자는 std::nullptr_t 생성자를 제외하고 기반이 되는 Windows 런타임 개체를 생성합니다. std::nullptr_t 생성자는 기본적으로 no-op. 프로젝스된 개체는 이후에 초기화될 것으로 예상됩니다. 따라서 런타임 클래스에 기본 생성자가 있는지 여부에 관계없이 이 기술을 사용하여 초기화가 지연될 수 있습니다.
이 고려 사항은 기본 생성자를 호출하는 다른 위치(예: 벡터 및 맵)에 영향을 줍니다. C++ 프로젝트에 대해 빈 앱 패키지(데스크톱의 WinUI 3) 가 필요한 이 코드 예제를 살펴보겠습니다.
std::map<int, TextBlock> lookup;
lookup[2] = value;
할당문은 새 TextBlock을 만든 다음, 즉시 value로 덮어씁니다. 여기에 치료법이 있습니다.
std::map<int, TextBlock> lookup;
lookup.insert_or_assign(2, value);
기본 생성자가 컬렉션에 미치는 영향도 참조하세요.
실수로 지연 초기화를 하지 마세요.
실수로 std::nullptr_t 생성자를 호출하지 않도록 주의하세요. 컴파일러의 충돌 해결은 팩터리 생성자보다 우선합니다. 예를 들어 이러한 두 런타임 클래스 정의를 고려합니다.
// GiftBox.idl
runtimeclass GiftBox
{
GiftBox();
}
// Gift.idl
runtimeclass Gift
{
Gift(GiftBox giftBox); // You can create a gift inside a box.
}
상자 안에 없는 선물(초기화되지 않은 GiftBox로 생성된 선물)을 만들고 싶다고 가정해 보겠습니다. 먼저 잘못된 방법을 살펴보겠습니다 . GiftBox를 사용하는 Gift 생성자가 있다는 것을 알고 있습니다. 그러나 null GiftBox 를 전달하려는 경우(아래와 같이 균일한 초기화를 통해 Gift 생성자를 호출) 원하는 결과를 얻지 못합니다 .
// These are *not* what you intended. Doing it in one of these two ways
// actually *doesn't* create the intended backing Windows Runtime Gift object;
// only an empty smart pointer.
Gift gift{ nullptr };
auto gift{ Gift(nullptr) };
여기서 얻을 수 있는 것은 초기화되지 않은 선물입니다. 초기화되지 않은 GiftBox로는 Gift를 받을 수 없습니다. 이 작업을 수행하는 올바른 방법은 다음과 같습니다.
// Doing it in one of these two ways creates an initialized
// Gift with an uninitialized GiftBox.
Gift gift{ GiftBox{ nullptr } };
auto gift{ Gift(GiftBox{ nullptr }) };
잘못된 예제에서는 nullptr 리터럴을 전달하면 지연 초기화 생성자가 선택됩니다. 팩터리 생성자가 선택되도록 하려면 매개 변수의 형식은 GiftBox여야 합니다. 올바른 예제에 나와 있듯이, 명시적으로 지연 초기화하도록 한 GiftBox를 전달하는 것도 가능합니다.
다음 예제도 매개변수의 형식이 GiftBox이고 std::nullptr_t가 아니기 때문에 올바릅니다.
GiftBox giftBox{ nullptr };
Gift gift{ giftBox }; // Calls factory constructor.
리터럴을 nullptr 전달할 때만 모호성이 발생합니다.
실수로 복사 생성하지 마세요.
이 주의는 위의 실수에 의한 지연 초기화 방지 섹션에 설명된 것과 유사합니다.
지연 초기화 생성자 외에도 C++/WinRT 프로젝션은 모든 런타임 클래스에 복사 생성자를 삽입합니다. 생성되는 개체와 동일한 형식을 허용하는 단일 매개 변수 생성자입니다. 결과 스마트 포인터는 생성자 매개 변수가 가리키는 것과 동일한 백업 Windows 런타임 개체를 가리킵니다. 그 결과 동일한 기반 개체를 가리키는 두 개의 스마트 포인터 개체가 생성됩니다.
다음은 코드 예제에서 사용할 런타임 클래스 정의입니다.
// GiftBox.idl
runtimeclass GiftBox
{
GiftBox(GiftBox biggerBox); // You can place a box inside a bigger box.
}
더 큰 GiftBox 내에 GiftBox 를 생성한다고 가정 해 보겠습니다.
GiftBox bigBox{ ... };
// These are *not* what you intended. Doing it in one of these two ways
// copies bigBox's backing-object-pointer into smallBox.
// The result is that smallBox == bigBox.
GiftBox smallBox{ bigBox };
auto smallBox{ GiftBox(bigBox) };
이 작업을 수행하는 올바른 방법은 활성화 팩터리를 명시적으로 호출하는 것입니다.
GiftBox bigBox{ ... };
// These two ways call the activation factory explicitly.
GiftBox smallBox{
winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
auto smallBox{
winrt::get_activation_factory<GiftBox, IGiftBoxFactory>().CreateInstance(bigBox) };
API가 Windows 런타임 구성 요소에서 구현되는 경우
이 섹션은 구성 요소를 직접 작성했는지 또는 공급업체에서 제공했는지에 관계없이 적용됩니다.
메모
C++/WinRT Visual Studio 확장(VSIX) 및 NuGet 패키지(프로젝트 템플릿 및 빌드 지원을 함께 제공)를 설치하고 사용하는 방법에 대한 자세한 내용은 C++/WinRT에 대한 Visual Studio 지원을 참조하세요.
애플리케이션 프로젝트에서 Windows 런타임 구성 요소의 Windows 런타임 메타데이터(.winmd) 파일을 참조하고 빌드합니다. 빌드하는 동안 도구는 cppwinrt.exe 구성 요소에 대한 API 표면을 완전히 설명하는 표준 C++ 라이브러리 또는 프로젝트를 생성합니다. 즉, 생성된 라이브러리에는 구성 요소에 대해 프로젝트된 형식이 포함됩니다.
그런 다음, Windows 네임스페이스 형식과 마찬가지로 헤더를 포함하고 해당 생성자 중 하나를 통해 프로젝션된 형식을 생성합니다. 애플리케이션 프로젝트의 시작 코드는 런타임 클래스를 등록하고 프로젝션된 형식의 생성자는 RoActivateInstance 를 호출하여 참조된 구성 요소에서 런타임 클래스를 활성화합니다.
#include <winrt/ThermometerWRC.h>
struct App : AppT<App>
{
ThermometerWRC::Thermometer thermometer;
...
};
자세한 내용, 코드 및 Windows 런타임 구성 요소에 구현된 API 사용에 관한 단계별 설명은 C++/WinRT를 사용한 Windows 런타임 구성 요소 및 C++/WinRT에서 이벤트 작성을 참조하세요.
API가 소비 프로젝트에서 구현되는 경우
이 섹션의 코드 예제는 항목 XAML 컨트롤에서 가져온 것입니다. C++/WinRT 속성에 바인딩합니다. 해당 항목을 사용하는 동일한 프로젝트에서 구현된 런타임 클래스를 사용하는 자세한 내용, 코드 및 연습은 해당 항목을 참조하세요.
XAML UI에서 사용되는 형식은 XAML과 동일한 프로젝트에 있더라도 런타임 클래스여야 합니다. 이 시나리오에서는 런타임 클래스의 Windows 런타임 메타데이터(.winmd)에서 프로젝션된 형식을 생성합니다. 헤더를 포함하지만 런타임 클래스의 인스턴스를 생성하는 C++/WinRT 버전 1.0 또는 버전 2.0 방법 중에서 선택할 수 있습니다. 버전 1.0 메서드는 winrt::make;를 사용합니다. 버전 2.0 메서드를 균일한 생성이라고 합니다. 차례로 각각을 살펴보겠습니다.
winrt::make를 사용하여 생성
기본값(C++/WinRT 버전 1.0) 메서드로 시작해 보겠습니다. 적어도 해당 패턴에 익숙한 것이 좋습니다. std::nullptr_t 생성자를 통해 프로젝션된 형식을 생성합니다. 해당 생성자는 초기화를 수행하지 않으므로 다음으로 winrt::make 도우미 함수를 통해 인스턴스에 값을 할당하고 필요한 생성자 인수를 전달해야 합니다. 사용하는 코드와 동일한 프로젝트에서 구현된 런타임 클래스는 등록하거나 Windows 런타임/COM 활성화를 통해 인스턴스화할 필요가 없습니다.
자세한 전체 안내는 XAML 컨트롤, C++/WinRT 속성에 바인딩하기를 참조하세요. 이 섹션에서는 해당 단계별 안내의 발췌 내용을 보여 줍니다.
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
BookstoreViewModel MainViewModel{ get; };
}
}
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel{ nullptr };
};
...
// MainPage.cpp
...
#include "BookstoreViewModel.h"
MainPage::MainPage()
{
m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();
...
}
균일한 구조
C++/WinRT 버전 2.0 이상에서는 균일한 생성 이라고 하는 최적화된 형태의 생성을 사용할 수 있습니다( C++/WinRT 2.0의 뉴스 및 변경 내용 참조).
전체 단계별 안내는 XAML 컨트롤, C++/WinRT 속성에 바인딩을 참조하세요. 이 섹션에서는 해당 단계별 안내에서 발췌한 내용을 보여 줍니다.
winrt::make 대신 일관된 초기화를 사용하려면 활성화 팩터리가 필요합니다. 생성할 수 있는 좋은 방법은 IDL에 생성자를 추가하는 것입니다.
// MainPage.idl
import "BookstoreViewModel.idl";
namespace Bookstore
{
runtimeclass MainPage : Microsoft.UI.Xaml.Controls.Page
{
MainPage();
BookstoreViewModel MainViewModel{ get; };
}
}
그런 다음 아래 MainPage.h 와 같이 한 단계에서만 m_mainViewModel 선언하고 초기화합니다.
// MainPage.h
...
struct MainPage : MainPageT<MainPage>
{
...
private:
Bookstore::BookstoreViewModel m_mainViewModel;
...
};
}
...
그리고 나서 MainPage.cpp의 MainPage 생성자에서 코드 m_mainViewModel = winrt::make<Bookstore::implementation::BookstoreViewModel>();는 필요하지 않습니다.
균일한 생성 및 코드 예제에 대한 자세한 내용은 균일한 생성 및 직접 구현 액세스에 대한 옵트인(Opt in)을 참조하세요.
프로젝션된 형식 및 인터페이스 인스턴스화 및 반환
다음은 소비 프로젝트에서 프로젝션된 형식 및 인터페이스가 어떻게 표시되는지에 대한 예입니다. 프로젝션된 형식(예: 이 예제의 형식)은 도구로 생성되며 직접 작성하는 형식이 아닙니다.
struct MyRuntimeClass : MyProject::IMyRuntimeClass, impl::require<MyRuntimeClass,
Windows::Foundation::IStringable, Windows::Foundation::IClosable>
MyRuntimeClass 는 프로젝션된 형식입니다. 프로젝션된 인터페이스에는 IMyRuntimeClass, IStringable 및 IClosable이 포함됩니다. 이 항목에서는 프로젝션된 형식을 인스턴스화할 수 있는 다양한 방법을 보여 했습니다. 다음은 MyRuntimeClass 를 예로 사용하여 미리 알림 및 요약입니다.
// The runtime class is implemented in another compilation unit (it's either a Windows API,
// or it's implemented in a second- or third-party component).
MyProject::MyRuntimeClass myrc1;
// The runtime class is implemented in the same compilation unit.
MyProject::MyRuntimeClass myrc2{ nullptr };
myrc2 = winrt::make<MyProject::implementation::MyRuntimeClass>();
- 프로젝션된 형식의 모든 인터페이스의 멤버에 액세스할 수 있습니다.
- 프로젝션된 형식을 호출자에게 반환할 수 있습니다.
- 프로젝션된 형식 및 인터페이스는 winrt::Windows::Foundation::IUnknown에서 파생됩니다. 따라서 프로젝션된 형식 또는 인터페이스에서 IUnknown::as 를 호출하여 다른 프로젝션된 인터페이스를 쿼리할 수 있으며 호출자를 사용하거나 호출자로 돌아갈 수도 있습니다. as 멤버 함수는 QueryInterface처럼 작동합니다.
void f(MyProject::MyRuntimeClass const& myrc)
{
myrc.ToString();
myrc.Close();
IClosable iclosable = myrc.as<IClosable>();
iclosable.Close();
}
활성화 팩터리
C++/WinRT 개체를 만드는 편리하고 직접적인 방법은 다음과 같습니다.
using namespace winrt::Windows::Globalization::NumberFormatting;
...
CurrencyFormatter currency{ L"USD" };
그러나 활성화 팩터리를 직접 만든 다음, 필요할 때 해당 팩터리에서 개체를 생성하고 싶을 때도 있습니다. 다음은 winrt::get_activation_factory 함수 템플릿을 사용하는 방법을 보여 주는 몇 가지 예입니다.
using namespace winrt::Windows::Globalization::NumberFormatting;
...
auto factory = winrt::get_activation_factory<CurrencyFormatter, ICurrencyFormatterFactory>();
CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");
using namespace winrt::Windows::Foundation;
...
auto factory = winrt::get_activation_factory<Uri, IUriRuntimeClassFactory>();
Uri uri = factory.CreateUri(L"http://www.contoso.com");
위의 두 예제의 클래스는 Windows 네임스페이스의 형식입니다. 다음 예제에서 ThermometerWRC::Thermometer는 Windows 런타임 구성 요소에 구현된 사용자 지정 형식입니다.
auto factory = winrt::get_activation_factory<ThermometerWRC::Thermometer>();
ThermometerWRC::Thermometer thermometer = factory.ActivateInstance<ThermometerWRC::Thermometer>();
멤버/형식 모호성
멤버 함수의 이름이 형식과 같으면 모호성이 있습니다. 멤버 함수에서 C++ 비한정 이름 조회 규칙은 네임스페이스보다 클래스를 먼저 검색하게 합니다. 치환 실패는 오류가 아님(SFINAE) 규칙은 적용되지 않습니다(이 규칙은 함수 템플릿의 오버로드 결정 중에 적용됩니다). 따라서 클래스 내의 이름이 이해가 되지 않는 경우 컴파일러는 더 나은 일치 항목을 계속 찾지 않고 단순히 오류를 보고합니다.
struct MyPage : Page
{
void DoWork()
{
// This doesn't compile. You get the error
// "'winrt::Windows::Foundation::IUnknown::as':
// no matching overloaded function found".
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Style>() };
}
}
위에서 컴파일러는 FrameworkElement.Style()(C ++/WinRT에서 멤버 함수)을 IUnknown::as에 템플릿 매개 변수로 전달한다고 생각합니다. 해결 방법은 이름을 StyleMicrosoft::UI::Xaml::Style 형식으로 해석하도록 하는 것입니다.
struct MyPage : Page
{
void DoWork()
{
// One option is to fully-qualify it.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Microsoft::UI::Xaml::Style>() };
// Another is to force it to be interpreted as a struct name.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<struct Style>() };
// If you have "using namespace Windows::UI;", then this is sufficient.
auto style{ Application::Current().Resources().
Lookup(L"MyStyle").as<Xaml::Style>() };
// Or you can force it to be resolved in the global namespace (into which
// you imported the Microsoft::UI::Xaml namespace when you did
// "using namespace Microsoft::UI::Xaml;".
auto style = Application::Current().Resources().
Lookup(L"MyStyle").as<::Style>();
}
}
정규화되지 않은 이름 조회에는 이름 뒤에 ::함수, 변수 및 열거형 값이 무시되는 특별한 예외가 있습니다. 이렇게 하면 다음과 같은 작업을 수행할 수 있습니다.
struct MyPage : Page
{
void DoSomething()
{
Visibility(Visibility::Collapsed); // No ambiguity here (special exception).
}
}
Visibility() 호출은 UIElement.Visibility 멤버 함수 이름으로 확인됩니다. 그러나 매개변수 Visibility::Collapsed가 ::와 함께 단어 Visibility 뒤에 오므로 메서드 이름은 무시되고 컴파일러는 enum 클래스를 찾습니다.
중요 API
- QueryInterface 함수
- RoActivateInstance 함수
- Windows::Foundation::Uri 클래스
- winrt::get_activation_factory 함수 템플릿
- winrt::make 함수 템플릿
- winrt::Windows::Foundation::IUnknown 구조체
관련 항목
Windows developer