다음을 통해 공유


C++/CX에서 C++/WinRT로 이동

이 항목은 C++/CX 프로젝트의 소스 코드를 C++/WinRT의 동일한 소스 코드로 포팅하는 방법을 설명하는 시리즈의 첫 번째 항목입니다.

프로젝트에서 Windows Runtime C++ 템플릿 라이브러리(WRL) 타입을 사용하는 경우, WRL에서 C++/WinRT로 이동을 참조하세요.

포팅 전략

C++/CX에서 C++/WinRT로 포팅하는 것은 일반적으로 간단하며, PPL(병렬 패턴 라이브러리)에서 코루틴으로 작업을 한 가지 예외입니다. 모델은 다릅니다. PPL 작업에서 코루틴으로의 자연스러운 일대일 매핑은 없으며 모든 경우에 작동하는 코드를 기계적으로 이식하는 간단한 방법은 없습니다. 포팅의 이러한 특정 측면과 두 모델 간의 상호 운용 옵션에 대한 도움말은 비동기 및 C++/WinRT와 C++/CX간의 interop를 참조하세요.

개발 팀은 비동기 코드 포팅의 장애물을 극복하고 나면 포팅 작업의 나머지 부분도 대체로 기계적이라고 정기적으로 보고합니다.

한 번의 패스로 포팅

전체 프로젝트를 한 번의 패스로 이식할 수 있는 위치에 있는 경우 필요한 정보에 대해 이 항목만 필요합니다(이 항목 다음에 오는 interop 항목은 필요하지 않음). 먼저 C++/WinRT 프로젝트 템플릿 중 하나를 사용하여 Visual Studio에서 새 프로젝트를 만드는 것이 좋습니다( C++/WinRT에 대한 Visual Studio 지원 참조). 그런 다음, 소스 코드 파일을 새 프로젝트로 이동하고 모든 C++/CX 소스 코드를 C++/WinRT로 이식합니다.

또는 기존 C++/CX 프로젝트에서 포팅 작업을 수행하려는 경우 C++/WinRT 지원을 추가해야 합니다. C++/CX 프로젝트를 진행하고 C++/WinRT 지원을 추가하기 위해 따라야 하는 단계는 에서로 설명되어 있습니다. 포팅을 완료하면 순수 C++/CX 프로젝트를 순수 C++/WinRT 프로젝트로 전환하게 됩니다.

비고

Windows 런타임 구성 요소 프로젝트가 있는 경우 한 번의 패스로 포팅하는 것이 유일한 옵션입니다. C++로 작성된 Windows 런타임 구성 요소 프로젝트에는 모든 C++/CX 소스 코드 또는 모든 C++/WinRT 소스 코드가 포함되어야 합니다. 이 프로젝트 형식에서는 공존할 수 없습니다.

프로젝트를 점진적으로 포팅하기

이전 섹션에서 설명한 대로 Windows 런타임 구성 요소 프로젝트를 제외하고 코드베이스의 크기나 복잡성으로 인해 프로젝트를 점진적으로 포팅해야 하는 경우 C++/CX 및 C++/WinRT 코드가 동일한 프로젝트에 나란히 존재하는 포팅 프로세스가 필요합니다. 이 항목을 읽는 것 외에도 C++/WinRT와 C++/CX 간의 상호 운용성 , 그리고 C++/WinRT와 C++/CX 간의 비동기 및 상호 운용성과도 참조하세요. 이러한 항목에서는 두 언어 프로젝션 간에 상호 운용하는 방법을 보여 주는 정보 및 코드 예제를 제공합니다.

프로젝트를 점진적 포팅 프로세스에 대비하기 위해 한 가지 옵션은 C++/CX 프로젝트에 C++/WinRT 지원을 추가하는 것입니다. C++/CX 프로젝트를 진행하고 C++/WinRT 지원을 추가하기 위해 따라야 하는 단계는 에서로 설명되어 있습니다. 그런 다음 거기에서 점진적으로 포트할 수 있습니다.

또 다른 옵션은 C++/WinRT 프로젝트 템플릿 중 하나를 사용하여 Visual Studio에서 새 프로젝트를 만드는 것입니다( C++/WinRT에 대한 Visual Studio 지원 참조). 그런 다음, 해당 프로젝트에 C++/CX 지원을 추가합니다. C++/WinRT 프로젝트에 C++/CX 지원을 추가하는 과정은 에서 설명되어 있습니다. 그런 다음 소스 코드를 새 코드 환경으로 이동하고, C++/CX 소스 코드의 일부를 C++/WinRT로 이식하는 작업을 병행할 수 있습니다.

두 경우 모두 사용자는 C++/WinRT 코드와 아직 이식하지 않은 C++/CX 코드 사이에서 양방향으로 상호 작용하게 됩니다.

비고

C++/CX Windows SDK는 Windows루트 네임스페이스에 형식을 선언합니다. C++/WinRT로 프로젝션된 Windows 형식은 Windows 형식과 동일한 정규화된 이름을 갖지만, C++ winrt 네임스페이스에 위치합니다. 이러한 고유한 네임스페이스를 사용하면 C++/CX에서 C++/WinRT로 고유한 속도로 포트할 수 있습니다.

XAML 프로젝트를 점진적으로 포팅합니다.

중요합니다

XAML을 사용하는 프로젝트의 경우 지정된 시간에 모든 XAML 페이지 형식은 전적으로 C++/CX이거나 완전히 C++/WinRT여야 합니다. C++/CX 및 C++/WinRT 동일한 프로젝트 내에서 XAML 페이지 형식의 외부에서 혼합할 수 있습니다(모델 및 뷰모델 및 다른 곳에서).

이 시나리오에서는 새 C++/WinRT 프로젝트를 만들고 C++/CX 프로젝트에서 소스 코드 및 태그를 복사하는 것이 좋습니다. 모든 XAML 페이지 형식이 C++/WinRT인 경우, Project>새 항목 추가...>Visual C++>빈 페이지(C++/WinRT)를 사용하여 새로운 XAML 페이지를 추가할 수 있습니다.

또는 Windows 런타임 구성 요소(WRC)를 사용하여 XAML C++/CX 프로젝트에서 코드를 이식하는 과정에서 분리할 수 있습니다.

  • 새 C++/CX WRC 프로젝트를 만들고, 가능한 한 많은 C++/CX 코드를 해당 프로젝트로 이동한 다음, XAML 프로젝트를 C++/WinRT로 변경할 수 있습니다.
  • 또는 새 C++/WinRT WRC 프로젝트를 만들고, XAML 프로젝트를 C++/CX로 두고, C++/CX를 C++/WinRT로 포팅하고, 결과 코드를 XAML 프로젝트에서 구성 요소 프로젝트로 이동하기 시작할 수 있습니다.
  • 동일한 솔루션 내에서 C++/WinRT 구성 요소 프로젝트와 함께 C++/CX 구성 요소 프로젝트를 만들고, 애플리케이션 프로젝트에서 둘 다 참조하고, 점진적으로 한 쪽에서 다른 솔루션으로 이식할 수도 있습니다. 동일한 프로젝트에서 두 언어 프로젝션을 사용하는 방법에 대한 자세한 내용은 C++/WinRT와 C++/CX 간의 Interop 을 참조하세요.

C++/CX 프로젝트를 C++/WinRT로 포팅하는 첫 번째 단계

포팅 전략이 무엇이든(한 번의 패스로 포팅 또는 점진적으로 포팅) 첫 번째 단계는 포팅을 위한 프로젝트를 준비하는 것입니다. 다음은 시작하려는 프로젝트의 종류와 설정하는 방법에 대한 포팅하기 위한 전략에서 설명한 내용입니다.

  • 한 번의 패스로 포팅을 . C++/WinRT 프로젝트 템플릿 중 하나를 사용하여 Visual Studio에서 새 프로젝트를 만듭니다. C++/CX 프로젝트에서 해당 새 프로젝트로 파일을 이동하고 C++/CX 소스 코드를 이식합니다.
  • XAML이 아닌 프로젝트를 점진적으로 포팅하는 과정보. C++/CX 프로젝트에 C++/WinRT 지원을 추가하고 점진적으로 전환할 수 있습니다(참고 C++/CX 프로젝트 및 C++/WinRT 지원추가). 또는 새 C++/WinRT 프로젝트를 만들고 C++/CX 지원을 추가하도록 선택할 수 있습니다( C++/WinRT 프로젝트 가져오기 및 C++/CX 지원 추가 참조), 파일을 이동하고 점진적으로 포트할 수 있습니다.
  • XAML 프로젝트를 점진적으로 이식하기. 새 C++/WinRT 프로젝트를 만들고, 파일을 이동하고, 점진적으로 포트 작업을 진행합니다. 언제든지 XAML 페이지 형식은 모든 C++/WinRT 또는 모든 C++/CX를 합니다.

이 항목의 나머지 내용은 선택한 포팅 전략에 관계없이 적용됩니다. C++/CX에서 C++/WinRT로 소스 코드를 포팅하는 데 관련된 기술 세부 정보 카탈로그가 포함되어 있습니다. 점진적으로 포팅하는 경우, C++/WinRT와 C++/CX 간의 상호 운용성()과 C++/WinRT와 C++/CX 간의 비동기 ()도 확인하고 싶을 것입니다.

파일 명명 규칙

XAML 마크업 파일

파일 원본 C++/CX C++/WinRT
개발자 XAML 파일 MyPage.xaml
MyPage.xaml.h
MyPage.xaml.cpp
MyPage.xaml
MyPage.h
MyPage.cpp
MyPage.idl(아래 참조)
생성된 XAML 파일 MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.xaml.g.h
MyPage.xaml.g.hpp
MyPage.g.h

C++/WinRT는 .xaml*.h 파일 이름에서 *.cpp을 제거합니다.

C++/WinRT는 추가 개발자 파일인 Midl 파일(.idl)추가합니다. C++/CX는 이 파일을 내부적으로 자동 생성하며, 이 파일에는 모든 공용 및 보호 멤버가 추가됩니다. C++/WinRT에서는 파일을 직접 추가하고 작성합니다. 자세한 내용, 코드 예제 및 IDL 작성 방법에 대한 연습은 XAML 컨트롤을 참조하고 C++/WinRT 속성을 바인딩하십시오.

또한 런타임 클래스를 Midl 파일(.idl)로 구분하는 방법에 관한 정보를 참조하십시오

런타임 클래스

C++/CX는 헤더 파일의 이름에 제한을 적용하지 않습니다. 특히 작은 클래스의 경우 여러 런타임 클래스 정의를 단일 헤더 파일에 배치하는 것이 일반적입니다. 그러나 C++/WinRT를 사용하려면 각 런타임 클래스에 클래스 이름 이름을 따서 명명된 고유한 헤더 파일이 있어야 합니다.

C++/CX C++/WinRT
Common.h
ref class A { ... }
ref class B { ... }
common.idl
runtimeclass A { ... }
runtimeclass B { ... }
A.h
namespace implements {
  struct A { ... };
}
B.h
namespace implements {
  struct B { ... };
}

C++/CX에서 덜 일반적(하지만 여전히 합법적임)은 XAML 사용자 지정 컨트롤에 서로 다른 이름의 헤더 파일을 사용하는 것입니다. 클래스 이름과 일치하도록 이러한 헤더 파일의 이름을 바꿔야 합니다.

C++/CX C++/WinRT
A.xaml
<Page x:Class="LongNameForA" ...>
A.xaml
<Page x:Class="LongNameForA" ...>
A.h
partial ref class LongNameForA { ... }
LongNameForA.h
namespace implements {
  struct LongNameForA { ... };
}

헤더 파일 요구 사항

C++/CX는 파일에서 .winmd 헤더 파일을 내부적으로 자동으로 생성하므로 특별한 헤더 파일을 포함할 필요가 없습니다. C++/CX에서는 이름으로 사용하는 네임스페이스에 using 지시문을 사용하는 것이 일반적입니다.

using namespace Windows::Media::Playback;

String^ NameOfFirstVideoTrack(MediaPlaybackItem^ item)
{
    return item->VideoTracks->GetAt(0)->Name;
}

지시 using namespace Windows::Media::Playback 문을 사용하면 네임스페이스 접두사 없이 작성 MediaPlaybackItem 할 수 있습니다. 를 반환하기 때문에 Windows.Media.Core 네임스페이스도 수정했습니다. item->VideoTracks->GetAt(0) Windows.Media.Core.VideoTrack. 하지만 우리는 이름 VideoTrack를 어디에서도 입력할 필요가 없었기 때문에 using Windows.Media.Core 지시문도 필요하지 않았습니다.

그러나 C++/WinRT를 사용하려면 이름을 지정하지 않더라도 사용하는 각 네임스페이스에 해당하는 헤더 파일을 포함해야 합니다.

#include <winrt/Windows.Media.Playback.h>
#include <winrt/Windows.Media.Core.h> // !!This is important!!

using namespace winrt;
using namespace Windows::Media::Playback;

winrt::hstring NameOfFirstVideoTrack(MediaPlaybackItem const& item)
{
    return item.VideoTracks().GetAt(0).Name();
}

반면에 MediaPlaybackItem.AudioTracksChanged 이벤트는 TypedEventHandler <MediaPlaybackItem 및 Windows.Foundation.Collections.IVectorChangedEventArgs>형식이지만, 해당 이벤트를 사용하지 않았으므로 winrt/Windows.Foundation.Collections.h를 포함할 필요가 없습니다.

또한 C++/WinRT를 사용하려면 XAML 마크업에서 사용하는 네임스페이스에 대한 헤더 파일을 포함해야 합니다.

<!-- MainPage.xaml -->
<Rectangle Height="400"/>

Rectangle 클래스를 사용하면 이 포함을 추가해야 합니다.

// MainPage.h
#include <winrt/Windows.UI.Xaml.Shapes.h>

헤더 파일을 잊어버린 경우, 모든 것이 제대로 컴파일되기는 하지만 consume_ 클래스가 없어 링커 오류가 발생합니다.

매개 변수 전달

C++/CX 소스 코드를 작성할 때, C++/CX 타입을 hat(^) 참조 형태로 함수의 매개 변수로 전달합니다.

void LogPresenceRecord(PresenceRecord^ record);

C++/WinRT에서 동기 함수의 경우 기본적으로 매개 변수를 사용해야 const& 합니다. 그러면 복사본과 연동된 오버헤드를 방지할 수 있습니다. 그러나 코루틴은 값 별 캡처 및 수명 문제를 방지하기 위해 값 별로 인수를 전달해야 합니다(자세한 내용은 C++/WinRT의 동시성 및 비동기 작업 참조).

void LogPresenceRecord(PresenceRecord const& record);
IASyncAction LogPresenceRecordAsync(PresenceRecord const record);

C++/WinRT 개체는 기본적으로 Windows 런타임 개체에 대한 인터페이스 포인터를 보유하는 값입니다. C++/WinRT 개체를 복사하면 컴파일러가 캡슐화된 인터페이스 포인터를 복사하여 참조 횟수를 증분합니다. 복사본의 최종 소멸에는 참조 횟수 감소가 포함됩니다. 따라서 필요한 경우에만 복사본의 오버헤드가 발생합니다.

변수와 필드 참조

C++/CX 소스 코드를 작성할 때 hat(^) 변수를 사용하여 Windows 런타임 개체를 참조하고 화살표(->) 연산자를 사용하여 hat 변수를 역참조합니다.

IVectorView<User^>^ userList = User::Users;

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList->Size; ++iUser)
    ...

해당하는 C++/WinRT 코드로 포팅할 때 모자를 제거하고 화살표 연산자(->)를 점 연산자(.)로 변경하여 먼 길을 갈 수 있습니다. C++/WinRT 프로젝션된 형식은 포인터가 아닌 값입니다.

IVectorView<User> userList = User::Users();

if (userList != nullptr)
{
    for (UINT32 iUser = 0; iUser < userList.Size(); ++iUser)
    ...

C++/CX hat 참조의 기본 생성자는 그것을 null로 초기화합니다. 다음은 C++/CX 코드 예제로, 올바른 유형의 변수/필드를 만들지만 초기화되지 않은 상태로 두는 경우입니다. 즉, 처음에는 TextBlock참조하지 않습니다. 나중에 참조를 할당하려고 합니다.

TextBlock^ textBlock;

class MyClass
{
    TextBlock^ textBlock;
};

C++/WinRT에 해당하는 항목은 지연된 초기화참조하세요.

속성

C++/CX 언어 확장에는 속성 개념이 포함됩니다. C++/CX 소스 코드를 작성할 때 마치 필드인 것처럼 속성에 액세스할 수 있습니다. 표준 C++에는 속성의 개념이 없으므로 C++/WinRT에서는 get 및 set 함수를 호출합니다.

다음 예제에서는 XboxUserId , UserState, PresenceDeviceRecordsSize 모두 속성입니다.

속성에서 값 추출

C++/CX에서 속성 값을 가져오는 방법은 다음과 같습니다.

void Sample::LogPresenceRecord(PresenceRecord^ record)
{
    auto id = record->XboxUserId;
    auto state = record->UserState;
    auto size = record->PresenceDeviceRecords->Size;
}

해당하는 C++/WinRT 소스 코드는 속성과 이름이 같지만 매개 변수가 없는 함수를 호출합니다.

void Sample::LogPresenceRecord(PresenceRecord const& record)
{
    auto id = record.XboxUserId();
    auto state = record.UserState();
    auto size = record.PresenceDeviceRecords().Size();
}

PresenceDeviceRecords 함수는 자체에 Size 함수가 있는 Windows 런타임 개체를 반환합니다. 반환된 개체도 C++/WinRT 프로젝션된 형식이므로 점(.) 연산자를 사용하여 Size를 호출하기 위해 역참조합니다.

속성을 새 값으로 설정

속성을 새 값으로 설정하는 것은 비슷한 패턴을 따릅니다. 먼저 C++/CX에서

record->UserState = newValue;

C++/WinRT에서 동일한 작업을 수행하려면 속성과 이름이 같은 함수를 호출하고 인수를 전달합니다.

record.UserState(newValue);

클래스 인스턴스 만들기

C++/CX 개체는 일반적으로 모자(^) 참조로 알려진 핸들을 통해 작업할 수 있습니다. 키워드를 통해 새 개체를 ref new 만듭니다. 그러면 RoActivateInstance 를 호출하여 런타임 클래스의 새 인스턴스를 활성화합니다.

using namespace Windows::Storage::Streams;

class Sample
{
private:
    Buffer^ m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
};

C++/WinRT 개체는 값으로 취급됩니다. 따라서 스택에 할당하거나 개체의 필드로 할당할 수 있습니다. C++/WinRT 개체를 할당하기 위해 (또는 ref new)를 new 수 없습니다. 백그라운드에서 RoActivateInstance 는 여전히 호출되고 있습니다.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
private:
    Buffer m_gamerPicBuffer{ MAX_IMAGE_SIZE };
};

리소스를 초기화하는 데 비용이 많이 드는 경우 실제로 필요할 때까지 초기화를 지연하는 것이 일반적입니다. 이미 언급했듯이 C++/CX hat 참조의 기본 생성자는 null로 초기화합니다.

using namespace Windows::Storage::Streams;

class Sample
{
public:
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = ref new Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer^ m_gamerPicBuffer;
};

C++/WinRT에 이식된 동일한 코드입니다. std::nullptr_t 생성자의 사용에 주의하세요. 해당 생성자에 대한 자세한 내용은 지연된 초기화를 참조하세요.

using namespace winrt::Windows::Storage::Streams;

struct Sample
{
    void DelayedInit()
    {
        // Allocate the actual buffer.
        m_gamerPicBuffer = Buffer(MAX_IMAGE_SIZE);
    }

private:
    Buffer m_gamerPicBuffer{ nullptr };
};

기본 생성자가 컬렉션에 미치는 영향

C++ 컬렉션 형식은 의도하지 않은 개체 생성을 초래할 수 있는 기본 생성자를 사용합니다.

시나리오 C++/CX C++/WinRT (올바르지 않음) C++/WinRT(올바른)
지역 변수( 처음에는 비어 있음) TextBox^ textBox; TextBox textBox; // Creates a TextBox! TextBox textBox{ nullptr };
멤버 변수( 처음에는 비어 있음) class C {
  TextBox^ textBox;
};
class C {
  TextBox textBox; // Creates a TextBox!
};
class C {
  TextBox textbox{ nullptr };
};
전역 변수( 처음에는 비어 있음) TextBox^ g_textBox; TextBox g_textBox; // Creates a TextBox! TextBox g_textBox{ nullptr };
빈 참조의 벡터 std::vector<TextBox^> boxes(10); // Creates 10 TextBox objects!
std::vector<TextBox> boxes(10);
std::vector<TextBox> boxes(10, nullptr);
맵에서 값 설정 std::map<int, TextBox^> boxes;
boxes[2] = value;
std::map<int, TextBox> boxes;
// Creates a TextBox at 2,
// then overwrites it!
boxes[2] = value;
std::map<int, TextBox> boxes;
boxes.insert_or_assign(2, value);
빈 참조의 배열 TextBox^ boxes[2]; // Creates 2 TextBox objects!
TextBox boxes[2];
TextBox boxes[2] = { nullptr, nullptr };
std::pair<TextBox^, String^> p; // Creates a TextBox!
std::pair<TextBox, String> p;
std::pair<TextBox, String> p{ nullptr, nullptr };

빈 참조 모음에 대한 자세한 정보

C++/CX에서 Platform::Array^(포트 Platform::Array^참조)를 사용할 때마다, 배열로 그대로 두기보다는 C++/WinRT에서 std::vector(사실, 어떤 연속된 컨테이너라도)로 포팅할 수 있습니다. 와 std::vector를 선택하는 데 장점이 있습니다.

예를 들어 빈 참조의 고정 크기 벡터를 만들기 위한 약식이 있지만(위의 표 참조), 빈 참조 배열 을 만들기 위한 이러한 약식은 없습니다. 배열의 각 요소에 대해 반복 nullptr 해야 합니다. 개수가 너무 적으면, 추가 항목이 기본 생성됩니다.

벡터의 경우 위 표와 같이 초기화 시 빈 참조로 채우거나 초기화 후 빈 참조를 다음과 같은 코드로 채울 수 있습니다.

std::vector<TextBox> boxes(10); // 10 default-constructed TextBoxes.
boxes.resize(10, nullptr); // 10 empty references.

std::map 예제에 대한 자세한 정보

[]에서의 첨자 연산자는 다음과 같이 작동합니다.

  • 키가 맵에 있으면 기존 값(덮어쓸 수 있음)에 대한 참조를 반환합니다.
  • 키가 맵에 없는 경우, 키(이동 가능한 경우)에 대해 맵에 새 항목을 만들고, 기본 생성된 값의 참조를 반환합니다. 이 참조는 나중에 덮어쓸 수 있습니다.

즉, 연산자는 [] 항상 맵에 항목을 만듭니다. 이는 C#, Java 및 JavaScript와 다릅니다.

기본 런타임 클래스에서 파생 클래스로 변환

파생 형식 객체를 참조하는 기본 참조를 갖는 것이 일반적입니다. C++/CX에서는 를 사용하여 기본 참조를 파생 참조로 캐스트할 수 있습니다. dynamic_cast는 실제로 숨겨진 QueryInterface호출일 뿐입니다. 다음은 종속성 속성 변경 이벤트를 처리하고 DependencyObject 에서 종속성 속성을 소유하는 실제 형식으로 다시 캐스팅하려는 일반적인 예제입니다.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject^ d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e)
{
    BgLabelControl^ theControl{ dynamic_cast<BgLabelControl^>(d) };

    if (theControl != nullptr)
    {
        // succeeded ...
    }
}

해당하는 C++/WinRT 코드는 dynamic_cast를 캡슐화하는 IUnknown::try_as 함수에 대한 호출로 바꿉 니다. 또한 IUnknown::as를 호출하는 옵션이 있으며, 필요한 인터페이스(요청하는 형식의 기본 인터페이스)를 쿼리할 때 반환되지 않는 경우에는 예외가 발생합니다. 다음은 C++/WinRT 코드 예제입니다.

void BgLabelControl::OnLabelChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
    if (BgLabelControlApp::BgLabelControl theControl{ d.try_as<BgLabelControlApp::BgLabelControl>() })
    {
        // succeeded ...
    }

    try
    {
        BgLabelControlApp::BgLabelControl theControl{ d.as<BgLabelControlApp::BgLabelControl>() };
        // succeeded ...
    }
    catch (winrt::hresult_no_interface const&)
    {
        // failed ...
    }
}

파생 클래스

런타임 클래스에서 파생하려면 기본 클래스가 구성 가능해야합니다. C++/CX는 클래스를 구성하기 위해 특별한 단계를 수행할 필요가 없지만 C++/WinRT는 그렇게 합니다. 봉인되지 않은 키워드 사용하여 클래스를 기본 클래스로 사용할 수 있도록 함을 나타냅니다.

unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

구현 헤더 클래스에서 파생 클래스에 대한 자동 생성된 헤더를 포함하기 전에 기본 클래스 헤더 파일을 포함해야 합니다. 그렇지 않으면 "이 형식을 표현식으로 잘못 사용함"과 같은 오류가 발생합니다.

// DerivedPage.h
#include "BasePage.h"       // This comes first.
#include "DerivedPage.g.h"  // Otherwise this header file will produce an error.

namespace winrt::MyNamespace::implementation
{
    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        ...
    }
}

대리자를 통한 이벤트 처리

다음은 C++/CX에서 람다 함수를 대리자로 사용하여 이벤트를 처리하는 일반적인 예입니다.

auto token = myButton->Click += ref new RoutedEventHandler([=](Platform::Object^ sender, RoutedEventArgs^ args)
{
    // Handle the event.
    // Note: locals are captured by value, not reference, since this handler is delayed.
});

이는 C++/WinRT와 동일합니다.

auto token = myButton().Click([=](IInspectable const& sender, RoutedEventArgs const& args)
{
    // Handle the event.
    // Note: locals are captured by value, not reference, since this handler is delayed.
});

람다 함수 대신 대리자를 자유 함수 또는 포인터-멤버-함수로 구현하도록 선택할 수 있습니다. 자세한 내용은 C++/WinRT에서 대리자를 사용하여 이벤트를 처리하는 방법에 대한 정보는 ,을 참조하세요.

C++/CX 코드베이스에서 이벤트 및 대리자가 내부적으로 사용되는 경우(파일 사이의 이진 관계가 아닌 경우), winrt::delegate를 사용하면 C++/WinRT에서 해당 패턴을 그대로 재현하는 데 도움이 됩니다. 또한 매개변수가 있는 대리자, 간단한 신호, 그리고 콜백 을(를) 프로젝트내에서 참조하세요.

대리자 해지

C++/CX에서는 연산자를 -= 사용하여 이전 이벤트 등록을 해지합니다.

myButton->Click -= token;

이는 C++/WinRT와 동일합니다.

myButton().Click(token);

자세한 정보 및 옵션은 등록된 대리자 해지을 참조하세요.

박싱 및 언박싱

C++/CX는 스칼라를 개체에 자동으로 상자로 지정합니다. C++/WinRT를 사용하려면 winrt::box_value 함수를 명시적으로 호출해야 합니다. 두 언어 모두 명시적으로 언박싱해야 합니다. C++/WinRT 의 Boxing 및 unboxing을 참조하세요.

다음 표에서는 이러한 정의를 사용합니다.

C++/CX C++/WinRT
int i; int i;
String^ s; winrt::hstring s;
Object^ o; IInspectable o;
수술 C++/CX C++/WinRT
권투 o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
제품 개봉 i = (int)o;
s = (String^)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

값 형식에 대한 null 포인터를 언박스 해제하려고 하면 C++/CX 및 C#에서 예외가 발생합니다. C++/WinRT는 이를 프로그래밍 오류로 간주하여 프로그램이 오류로 인해 종료됩니다. C++/WinRT에서 객체가 예상한 형식이 아닌 경우를 처리하려면 winrt::unbox_value_or 함수를 사용하세요.

시나리오 C++/CX C++/WinRT
알려진 정수를 언박스하다 i = (int)o; i = unbox_value<int>(o);
o가 null인 경우 Platform::NullReferenceException 추락
o가 박싱된 정수가 아닌 경우 Platform::InvalidCastException 추락
정수를 언박스, null인 경우 대체 옵션 사용; 다른 경우 크래시 발생 i = o ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
가능한 경우 int을 언박싱하고, 그렇지 않으면 대체 방법을 사용하세요. auto box = dynamic_cast<IBox<int>^>(o);
i = box ? box->Value : fallback;
i = unbox_value_or<int>(o, fallback);

문자열을 객체로 변환하기 및 객체에서 문자열로 변환하기

문자열은 어떤 면에서는 값 형식이고 다른 방법으로는 참조 형식입니다. C++/CX 및 C++/WinRT는 문자열을 다르게 처리합니다.

ABI 형식 HSTRING은 참조 횟수가 있는 문자열에 대한 포인터입니다. 그러나 은 IInspectable에서 파생된 것이 아니므로, 기술적으로개체가 아닙니다. 또한 null HSTRING 은 빈 문자열을 나타냅니다. IInspectable에서 파생되지 않은 항목의 boxing은 IReference<T>내에 래핑함으로써 수행되며, Windows 런타임은 PropertyValue 개체 형태로 표준 구현을 제공합니다. 사용자 지정 형식은 PropertyType::OtherType로 보고됩니다.

C++/CX는 Windows 런타임 문자열을 참조 형식으로 나타내며, C++/WinRT는 문자열을 값 형식으로 투영합니다. 즉, 박스화된 null 문자열은 생성된 상황이나 맥락에 따라 다양한 표현을 가질 수 있습니다.

또한 C++/CX를 사용하면 null String^을(를) 역참조할 수 있습니다. 이 경우에는 문자열 ""처럼 동작합니다.

행동 C++/CX C++/WinRT
선언 Object^ o;
String^ s;
IInspectable o;
hstring s;
문자열 형식 범주 참조 형식 값 형식
null HSTRING 프로젝트로 예상됨 (String^)nullptr hstring{}
null이고 "" 동일합니까?
null의 유효성 s = nullptr;
s->Length == 0 (유효)
s = hstring{};
s.size() == 0 (유효)
개체에 null 문자열을 할당하는 경우 o = (String^)nullptr;
o == nullptr
o = box_value(hstring{});
o != nullptr
개체에 ""를 할당할 때 o = "";
o == nullptr
o = box_value(hstring{L""});
o != nullptr

기본 박싱 및 언박싱.

수술 C++/CX C++/WinRT
문자열을 상자에 넣기 o = s;
빈 문자열은 nullptr이 됩니다.
o = box_value(s);
빈 문자열은 null이 아닌 개체가 됩니다.
알려진 문자열을 해제하기 s = (String^)o;
Null 개체가 빈 문자열이 됩니다.
문자열이 아닌 경우에는 InvalidCastException이 발생합니다.
s = unbox_value<hstring>(o);
Null 개체가 충돌합니다.
문자열이 아니면 크래시입니다.
가능성 있는 문자열 열기 s = dynamic_cast<String^>(o);
Null 개체 또는 문자열이 아닌 경우 빈 문자열이 됩니다.
s = unbox_value_or<hstring>(o, fallback);
Null 또는 비 문자열이 대체 옵션이 됩니다.
빈 문자열이 유지됩니다.

동시성 및 비동기 작업

Parallel Patterns Library(병렬 패턴 라이브러리, PPL)(동시성::task)가 C++/CX hat 참조를 지원하게 업데이트되었습니다.

C++/WinRT의 경우에는 코루틴과 co_await을 사용하는 것이 좋습니다. 자세한 정보 및 코드 예제는 C++/WinRT의 동시성 및 비동기 작업을 참조하세요.

XAML 마크업에서 개체 사용

C++/CX 프로젝트에서는 XAML 마크업에서 프라이빗 멤버 및 명명된 요소를 사용할 수 있습니다. 그러나 C++/WinRT에서는 XAML {x:Bind} 마크업 확장을 사용하여 소비되는 모든 엔터티가 IDL에 공개되어야 합니다.

또한 부울에 바인딩하면 C++/CX에서는 true 또는 false로 표시되지만, C++/WinRT에서는 Windows.Foundation.IReference`1<부울>로 표시됩니다.

코드 예제 및 관련 정보는 마크업에서 개체를 사용하는 방법을 참조하세요.

C++/CX 플랫폼 형식을 C++/WinRT 형식에 매핑

C++/CX는 플랫폼 네임스페이스에 여러 데이터 형식을 제공합니다. 이러한 형식은 표준 C++가 아니므로 Windows 런타임 언어 확장을 사용하도록 설정할 때만 사용할 수 있습니다(Visual Studio 프로젝트 속성 C/C++>일반>Windows 런타임 확장 소비>예 (/ZW)). 아래 표는 플랫폼 형식에서 C++/WinRT의 해당 형식으로 포트하는 데 도움이 됩니다. 이렇게 한 후 C++/WinRT가 표준 C++이므로 /ZW 옵션을 해제할 수 있습니다.

C++/CX C++/WinRT
Platform::Agile^ winrt::agile_ref
Platform::Array^ 포트 Platform::Array^을 참조하세요.
Platform::Exception^ winrt::hresult_error
Platform::잘못된인수예외^ winrt::hresult_invalid_argument
Platform::Object^ winrt::Windows::Foundation::IInspectable
Platform::String^ winrt::hstring

포트 플랫폼::Agile^winrt::agile_ref

C++/CX의 Platform::Agile^ 형식은 모든 스레드에서 액세스할 수 있는 Windows 런타임 클래스를 나타냅니다. C++/WinRT의 해당 코드는 winrt::agile_ref입니다.

C++/CX에서

Platform::Agile<Windows::UI::Core::CoreWindow> m_window;

C++/WinRT에서

winrt::agile_ref<Windows::UI::Core::CoreWindow> m_window;

포트 Platform::Array^

C++/CX에서 배열을 사용해야 하는 경우 C++/WinRT를 사용하면 연속 컨테이너를 사용할 수 있습니다. 기본 생성자가 컬렉션에 미치는 영향을 참조하시면 std::vector가 좋은 선택인 이유를 알 수 있습니다.

따라서 C++/CX에서 Platform::Array^가 있을 때마다 이식 옵션으로는 초기화 목록, std::array또는 std::vector를 사용할 수 있습니다. 자세한 정보 및 코드 예제는 표준 이니셜라이저 목록표준 배열 및 벡터를 참조하세요.

포트 플랫폼::Exception^에서 winrt::hresult_error

Platform::Exception^ 형식은 Windows 런타임 API가 S_OK 아닌 HRESULT를 반환할 때 C++/CX에서 생성됩니다. C++/WinRT의 동등물은 winrt::hresult_error입니다.

C++/WinRT로 포트하려면, Platform::Exception^을 사용하던 모든 코드를 winrt::hresult_error로 변경하세요.

C++/CX에서

catch (Platform::Exception^ ex)

C++/WinRT에서

catch (winrt::hresult_error const& ex)

C++/WinRT는 이러한 예외 클래스를 제공합니다.

예외 유형 기본 클래스 HRESULT (작업의 성공 여부를 나타내는 결과 코드)
winrt::hresult_error 호출 hresult_error::to_abi
winrt::hresult_access_denied winrt::hresult_error E_ACCESSDENIED (접근이 거부되었습니다)
winrt::hresult_canceled winrt::hresult_error 오류_취소됨
winrt::hresult_changed_state winrt::hresult_error E_CHANGED_STATE (변경된 상태)
winrt::hresult_class_not_available winrt::hresult_error 클래스_클래스사용불가
winrt::hresult_illegal_delegate_assignment winrt::hresult_error E_ILLEGAL_DELEGATE_ASSIGNMENT
winrt::hresult_illegal_method_call winrt::hresult_error E_ILLEGAL_METHOD_CALL (잘못된 메서드 호출)
winrt::hresult_illegal_state_change winrt::hresult_error E_불법 상태 변경
winrt::hresult_invalid_argument winrt::hresult_error E_INVALIDARG (잘못된 인수 오류)
winrt::hresult_no_interface winrt::hresult_error E_NOINTERFACE (인터페이스 없음)
winrt::hresult_not_implemented winrt::hresult_error E_NOTIMPL
winrt::hresult_out_of_bounds winrt::hresult_error E_BOUNDS
winrt::hresult_wrong_thread (스레드 오류) winrt::hresult_error RPC_E_WRONG_THREAD

각 클래스( hresult_error 기본 클래스를 통해)는 오류의 HRESULT를 반환하는 to_abi 함수와 해당 HRESULT의 문자열 표현을 반환하는 메시지 함수를 제공합니다.

다음은 C++/CX에서 예외를 throw하는 예제입니다.

throw ref new Platform::InvalidArgumentException(L"A valid User is required");

C++/WinRT에서의 해당 내용은 다음과 같습니다.

throw winrt::hresult_invalid_argument{ L"A valid User is required" };

포트 을(를) Platform::Object^에서 로 winrt::Windows::Foundation::IInspectable로 변환하십시오.

모든 C++/WinRT 형식과 마찬가지로 winrt::Windows::Foundation::IInspectable 은 값 형식입니다. 해당 형식의 변수를 null로 초기화하는 방법은 다음과 같습니다.

winrt::Windows::Foundation::IInspectable var{ nullptr };

포트 Platform::String^winrt::hstring로 변환

Platform::String^는 Windows 런타임 HSTRING ABI 유형과 동일합니다. C++/WinRT의 경우 동급 winrt::hstring. 그러나 C++/WinRT를 사용하면 std::wstring 및/또는 와이드 문자열 리터럴과 같은 C++ 표준 라이브러리 와이드 문자열 형식을 사용하여 Windows 런타임 API를 호출할 수 있습니다. 자세한 내용 및 코드 예제는 C++/WinRT의 문자열 처리를 참조하세요.

C++/CX를 사용하면 Platform::String::Data 속성에 액세스하여 문자열을 C 스타일의 const wchar_t* 배열로 변환할 수 있습니다(예: 이를 std::wcout에 전달하기 위해).

auto var{ titleRecord->TitleName->Data() };

C++/WinRT에서 동일한 작업을 수행하려면 hstring::c_str 함수를 사용하여 std::wstring에서와 마찬가지로 null로 끝나는 C 스타일 문자열 버전을 가져올 수 있습니다.

auto var{ titleRecord.TitleName().c_str() };

문자열을 사용하거나 반환하는 API를 구현하는 경우 일반적으로 Platform::String^ 사용하여 winrt::hstring 사용하는 C++/CX 코드를 변경합니다.

다음은 문자열을 사용하는 C++/CX API의 예입니다.

void LogWrapLine(Platform::String^ str);

C++/WinRT의 경우 API를 다음과 같이 MIDL 3.0에서 선언할 수 있습니다.

// LogType.idl
void LogWrapLine(String str);

그런 다음 C++/WinRT 도구 체인은 다음과 같은 소스 코드를 생성합니다.

void LogWrapLine(winrt::hstring const& str);

ToString()

C++/CX 형식은 Object::ToString 메서드를 제공합니다.

int i{ 2 };
auto s{ i.ToString() }; // s is a Platform::String^ with value L"2".

C++/WinRT는 이 기능을 직접 제공하지는 않지만 대안으로 전환할 수 있습니다.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

C++/WinRT는 제한된 수의 형식에 대해 winrt::to_hstring 지원합니다. 문자열로 변환하고자 하는 모든 추가 형식에 대한 오버로드를 추가해야 합니다.

언어 정수 문자열로 변환 열거형을 문자열로 변환
C++/CX String^ result = "hello, " + intValue.ToString(); String^ result = "status: " + status.ToString();
C++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

열거형을 문자열로 변환하는 경우, winrt::to_hstring의 구현을 제공해야 합니다.

namespace winrt
{
    hstring to_hstring(StatusEnum status)
    {
        switch (status)
        {
        case StatusEnum::Success: return L"Success";
        case StatusEnum::AccessDenied: return L"AccessDenied";
        case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
        default: return to_hstring(static_cast<int>(status));
        }
    }
}

이러한 문자열화는 데이터 바인딩에서 암시적으로 사용되는 경우가 많습니다.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

이 바인딩은 바인딩된 속성에 대해 winrt::to_hstring을 수행합니다. 두 번째 예제( StatusEnum)의 경우 winrt::to_hstring 고유한 오버로드를 제공해야 합니다. 그렇지 않으면 컴파일러 오류가 발생합니다.

문자열 생성

C++/CX 및 C++/WinRT는 문자열을 구축할 때 표준 std::wstringstream을 사용합니다.

수술 C++/CX C++/WinRT
문자열 추가, null 유지 stream.print(s->Data(), s->Length); stream << std::wstring_view{ s };
문자열 추가, 첫 번째 null에서 중지 stream << s->Data(); stream << s.c_str();
결과 추출 ws = stream.str(); ws = stream.str();

추가 예제

아래 예제에서 wsstd::wstring유형의 변수입니다. 또한 C++/CX는 8비트 문자열에서 Platform::String 을 생성할 수 있지만 C++/WinRT는 그렇게 하지 않습니다.

수술 C++/CX C++/WinRT
리터럴을 사용하여 문자열 구성 String^ s = "hello";
String^ s = L"hello";
// winrt::hstring s{ "hello" }; // Doesn't compile
winrt::hstring s{ L"hello" };
std::wstring로 변환하여 null을 그대로 유지합니다. String^ s = ref new String(ws.c_str(),
  (uint32_t)ws.size());
winrt::hstring s{ ws };
s = winrt::hstring(ws);
// s = ws; // Doesn't compile
std::wstring변환 후, 첫 번째 null 값에서 중지 String^ s = ref new String(ws.c_str()); winrt::hstring s{ ws.c_str() };
s = winrt::hstring(ws.c_str());
// s = ws.c_str(); // Doesn't compile
std::wstring로 변환하고 null을 유지하면서 std::wstring ws{ s->Data(), s->Length };
ws = std::wstring(s>Data(), s->Length);
std::wstring ws{ s };
ws = s;
std::wstring변환, 첫 번째 null에 도달하면 중지 std::wstring ws{ s->Data() };
ws = s->Data();
std::wstring ws{ s.c_str() };
ws = s.c_str();
메서드에 리터럴 전달 Method("hello");
Method(L"hello");
// Method("hello"); // Doesn't compile
Method(L"hello");
메서드에 std::wstring 전달 Method(ref new String(ws.c_str(),
  (uint32_t)ws.size()); // Stops on first null
Method(ws);
// param::winrt::hstring accepts std::wstring_view

중요 API