C++/WinRT를 사용한 Windows 런타임 구성 요소

이 항목에서는 C++/WinRT를 사용하여 Windows 런타임 언어로 빌드된 유니버설 Windows 앱에서 호출할 수 있는 구성 요소인 Windows 런타임 구성 요소를 만들고 사용하는 방법을 보여 줍니다.

C++/WinRT에서 Windows 런타임 구성 요소를 빌드하는 데에는 몇 가지 이유가 있습니다.

  • 복잡하거나 많은 계산이 필요한 작업에서 C++의 성능 이점을 누릴 수 있습니다.
  • 이미 작성되고 테스트된 표준 C++ 코드를 재사용합니다.
  • 예를 들어 C#으로 작성된 UWP(유니버설 Windows 플랫폼) 앱에 Win32 기능을 노출합니다.

일반적으로 C++/WinRT 구성 요소를 제작할 때 표준 C++ 라이브러리의 형식과 기본 제공 형식을 사용할 수 있습니다. 단, 다른 .winmd 패키지의 코드와 데이터를 주고받는 ABI(애플리케이션 이진 인터페이스) 경계는 제외됩니다. ABI에서 Windows 런타임 유형을 사용합니다. 또한 C++/WinRT 코드에서 대리자 및 이벤트와 같은 형식을 사용하여 구성 요소에서 발생하고 다른 언어로 처리할 수 있는 이벤트를 구현합니다. C++/WinRT에 대한 자세한 내용은 C++/WinRT를 참조하세요.

이 항목의 나머지 부분에서는 C++/WinRT에서 Windows 런타임 구성 요소를 제작하는 방법과 애플리케이션에서 사용하는 방법을 안내합니다.

이 항목에서 빌드할 Windows 런타임 구성 요소에는 온도계를 나타내는 런타임 클래스가 포함되어 있습니다. 이 항목은 또한 온도계 런타임 클래스를 사용하고 온도를 조정하는 함수를 호출하는 Core 앱을 보여 줍니다.

참고 항목

프로젝트 템플릿 및 빌드 지원을 함께 제공하는 C++/WinRT Visual Studio 확장(VSIX) 및 NuGet 패키지를 설치하고 사용하는 방법에 대한 자세한 내용은 Visual Studio의 C++/WinRT 지원을 참조하세요.

Important

C++/WinRT를 사용해 런타임 클래스를 사용하거나 작성하는 방법을 더욱 쉽게 이해할 수 있는 필수 개념과 용어에 대해서는 C++/WinRT를 통한 API 사용C++/WinRT를 통한 API 작성을 참조하세요.

Windows 런타임 구성 요소 dll에 대한 명명 모범 사례

Important

이 섹션에서는 Windowns 런타임 구성 요소를 빌드하는 DLL(파일)에 사용하는 것을 .dll 추천하는 명명 관습에 대해 설명합니다. Windows 런타임 구성 요소에서 런타임 클래스를 사용할 때 C++/WinRT가 따르는 활성화 시퀀스에 관한 것입니다.

클래스 팩토리를 활성화할 때, C++/WinRT가 먼저 RoGetActivationFactory에 호출을 시도합니다. 실패 시 C++/WinRT는 직접 로드할 DLL을 찾게 됩니다. Windows 런타임 활성화는 항상 정규화된 클래스 이름을 기반으로 합니다. 이 논리는 정규화된 클래스 이름에서 클래스 이름을 제거한 다음 다시 기본 전체 네임스페이스의 이름이 지정된 DLL을 찾는 것입니다. 해당 이름을 찾을 수 없는 경우 가장 구체적인 세그먼트 이름을 제거하고 과정을 반복합니다.

예를 들어 활성화되는 클래스의 정규화된 이름이 Contoso.Instruments.ThermometerWRC.Thermometer이고 RoGetActivationFactory가 실패하는 경우, 먼저 Contoso.Instruments.ThermometerWRC.dll를 찾습니다. 찾을 수 없으면 Contoso.Instruments.dll를 찾아본 후 Contoso.dll를 찾습니다.

(해당 시퀀스에서) DLL을 발견하면 해당 DLL의 DllGetActivationFactory 진입점을 사용하여 팩토리를 직접 가져오려고 합니다 (처음 시도한 RoGetActivationFactory 함수를 통해 간접적으로 수행하지 않습니다). 그럼에도 불구하고 최종 결과 상으로 호출자와 DLL을 구별되지 않습니다.

이 프로세스는 완전 자동화 되었으며 등록 또는 도구가 필요하지 않습니다. Windows 런타임 구성 요소를 작성하는 경우 방금 설명한 프로세스에서 작동하는 DLL에 대한 명명 규칙을 사용해야 합니다. 또한 Windows 런타임 구성 요소를 사용하는데 이름이 올바로 지정되지 않은 경우 설명된 대로 이름을 바꿀 수 있습니다.

Windows 런타임 구성 요소 만들기(ThermometerWRC)

먼저 Microsoft Visual Studio에서 새 프로젝트를 만듭니다. Windows 런타임 구성 요소(C++/WinRT) 프로젝트를 만들고 이름을 ThermometerWRC("온도계 Windows 런타임 구성 요소"용)로 지정합니다. 솔루션 및 프로젝트를 같은 디렉터리에 배치를 선택하지 않아야 합니다. 일반적으로 사용 가능한 최신(미리 보기 아님) 버전의 Windows SDK를 대상으로 합니다. 프로젝트 이름을 ThermometerWRC로 지정하면 이 항목의 나머지 단계를 가장 쉽게 수행할 수 있습니다.

아직 프로젝트를 빌드하지 마세요.

새로 만든 프로젝트에는 Class.idl이라는 이름의 파일이 포함되어 있습니다. 솔루션 탐색기에서 Thermometer.idl 파일의 이름을 바꿉니다(.idl 파일의 이름을 바꾸면 자동으로 종속 .h.cpp 파일의 이름도 바뀜). Thermometer.idl의 콘텐츠를 아래 목록으로 바꿉니다.

// Thermometer.idl
namespace ThermometerWRC
{
    runtimeclass Thermometer
    {
        Thermometer();
        void AdjustTemperature(Single deltaFahrenheit);
    };
}

파일을 저장합니다. 현재는 프로젝트 빌드가 완료되지 않지만, 지금 빌드하는 것은 Thermometer 런타임 클래스를 구현할 소스 코드 파일을 생성하므로 유용한 작업입니다. 따라서 계속해서 지금 빌드합니다(이 단계에서 표시될 것으로 예상되는 빌드 오류는 발견되는 Class.hClass.g.h로 처리해야 함).

빌드 프로세스에서 midl.exe 도구가 실행되어 구성 요소의 Windows 런타임 메타데이터 파일(\ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd)을 생성합니다. 그런 다음, cppwinrt.exe 도구가 실행되어(-component 옵션과 함께) 구성 요소를 작성하도록 지원하는 원본 코드 파일을 생성합니다. 원본 코드 파일에는 IDL로 선언한 Thermometer 런타임 클래스의 구현을 시작할 수 있는 스텁이 포함됩니다. 이 스텁이 \ThermometerWRC\ThermometerWRC\Generated Files\sources\Thermometer.hThermometer.cpp입니다.

프로젝트 노드를 마우스 오른쪽 단추로 클릭하고 파일 탐색기에서 폴더 열기를 클릭합니다. 파일 탐색기에서 프로젝트 폴더가 열립니다. 여기에서 \ThermometerWRC\ThermometerWRC\Generated Files\sources\ 폴더에서 스텁 파일 Thermometer.hThermometer.cpp를 복사하여 프로젝트 파일을 포함하는 폴더인 \ThermometerWRC\ThermometerWRC\에 붙여넣고 대상에서 파일을 바꿉니다. 이제 Thermometer.hThermometer.cpp를 열고 런타임 클래스를 구현합니다. Thermometer.h에서 Thermometer의 구현(공장 구현이 아닌)에 새 프라이빗 멤버를 추가합니다.

// Thermometer.h
...
namespace winrt::ThermometerWRC::implementation
{
    struct Thermometer : ThermometerT<Thermometer>
    {
        ...

    private:
        float m_temperatureFahrenheit { 0.f };
    };
}
...

Thermometer.cpp에서 아래 목록과 같이 AdjustTemperature 메서드를 구현합니다.

// Thermometer.cpp
...
namespace winrt::ThermometerWRC::implementation
{
    void Thermometer::AdjustTemperature(float deltaFahrenheit)
    {
        m_temperatureFahrenheit += deltaFahrenheit;
    }
}

Thermometer.hThermometer.cpp의 맨 위에 static_assert가 표시되며, 제거해야 합니다. 이제 프로젝트를 빌드합니다.

경고 메시지로 인해 빌드가 중단되는 경우에는 경고를 해결하거나 프로젝트 속성 C/C++>일반>경고를 오류로 처리아니요(/WX-)로 설정한 후 프로젝트를 다시 빌드합니다.

Windows 런타임 구성 요소를 테스트하기 위한 핵심 앱(ThermometerCoreApp) 만들기

이제 새 프로젝트를 만듭니다(ThermometerWRC 솔루션 또는 새 솔루션에서). 핵심 앱(C++/WinRT) 프로젝트를 만들고 이름을 ThermometerCoreApp으로 지정합니다. 두 프로젝트가 동일한 솔루션에 있는 경우 ThermometerCoreApp을 시작 프로젝트로 설정합니다.

참고 항목

앞에서 설명한 것처럼 Windows 런타임 구성 요소에 대한 Windows 런타임 메타데이터 파일(ThermometerWRC라는 프로젝트)이 \ThermometerWRC\Debug\ThermometerWRC\ 폴더에 생성됩니다. 해당 경로의 첫 번째 세그먼트는 솔루션 파일이 포함된 폴더의 이름입니다. 다음 세그먼트는 Debug라는 하위 디렉터리입니다. 마지막 세그먼트는 Windows 런타임 구성 요소에 대해 이름이 지정된 하위 디렉터리입니다. 프로젝트 ThermometerWRC의 이름을 지정하지 않은 경우 메타데이터 파일은 \<YourProjectName>\Debug\<YourProjectName>\ 폴더에 있습니다.

이제 핵심 앱 프로젝트(ThermometerCoreApp)에서 참조를 추가하고 \ThermometerWRC\Debug\ThermometerWRC\ThermometerWRC.winmd로 이동하거나, 두 프로젝트가 동일한 솔루션에 있는 경우 프로젝트 간 참조를 추가합니다. 추가, 확인을 차례로 클릭합니다. 이제 ThermometerCoreApp을 빌드합니다. 드물지만 페이로드 파일인 readme.txt가 존재하지 않는다는 오류가 표시되는 경우에는 해당 파일을 Windows 런타임 구성 요소 프로젝트에서 제외하여 다시 빌드한 다음, ThermometerCoreApp을 다시 빌드합니다.

빌드 프로세스에서 cppwinrt.exe 도구가 실행되면서 참조된 .winmd 파일을 원본 코드 파일로 처리합니다. 이 원본 코드 파일에는 구성 요소를 사용할 때 지원할 목적으로 프로젝션된 형식이 포함되어 있습니다. 구성 요소의 런타임 클래스에 맞게 프로젝션된 형식의 헤더(ThermometerWRC.h)가 \ThermometerCoreApp\ThermometerCoreApp\Generated Files\winrt\ 폴더로 생성됩니다.

이 헤더를 App.cpp에 추가합니다.

// App.cpp
...
#include <winrt/ThermometerWRC.h>
...

또한 App.cpp에서 다음 코드를 추가하여 Thermometer 개체를 인스턴스화하고(프로젝션된 유형의 기본 생성자를 사용하여) 온도계 개체에 대한 메서드를 호출합니다.

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    ThermometerWRC::Thermometer m_thermometer;
    ...
    
    void OnPointerPressed(IInspectable const &, PointerEventArgs const & args)
    {
        m_thermometer.AdjustTemperature(1.f);
        ...
    }
    ...
};

창을 클릭할 때마다 온도계 개체의 온도가 올라갑니다. 애플리케이션이 실제로 Windows 런타임 구성 요소를 호출하고 있는지 확인하기 위해 코드를 단계별로 실행하려는 경우 중단점을 설정할 수 있습니다.

다음 단계

C++/WinRT Windows 런타임 구성 요소에 더 많은 기능 또는 새로운 Windows 런타임 유형을 추가하려면 위에 표시된 것과 동일한 패턴을 따를 수 있습니다. 먼저 IDL을 사용하여 노출하려는 기능을 정의합니다. 그런 다음 Visual Studio에서 프로젝트를 빌드하여 스텁 구현을 생성합니다. 그런 다음 적절하게 구현을 완료합니다. IDL에서 정의한 모든 메서드, 속성 및 이벤트는 Windows 런타임 구성 요소를 사용하는 애플리케이션에서 볼 수 있습니다. IDL에 대한 자세한 내용은 Microsoft 인터페이스 정의 언어 3.0 소개를 참조하세요.

Windows 런타임 구성 요소에 이벤트를 추가하는 방법의 예는 C++/WinRT의 작성 이벤트를 참조하세요.

문제 해결

증상 해결 방법
C++/WinRT 앱에서 XAML을 사용하는 C# Windows 런타임 구성 요소를 사용할 때 컴파일러는 "'MyNamespace_XamlTypeInfo': is not a member of 'winrt::MyNamespace'" 형식의 오류를 발생합니다. 여기서 MyNamespace는 Windows 런타임 구성 요소의 네임스페이스 이름입니다. 사용하는 C++/WinRT 앱의 pch.h에서 MyNamespace를 적절하게 대체하는 #include <winrt/MyNamespace.MyNamespace_XamlTypeInfo.h>를 추가합니다.