C++/WinRT를 통한 COM 구성 요소 사용Consume COM components with C++/WinRT

C++/WinRT 라이브러리의 기능을 사용하여 DirectX API의 고성능 2차원 및 3차원 그래픽과 같은 COM 구성 요소를 사용할 수 있습니다.You can use the facilities of the C++/WinRT library to consume COM components, such as the high-performance 2-D and 3-D graphics of the DirectX APIs. C++/WinRT는 성능 손상 없이 DirectX를 사용하는 가장 간단한 방법입니다.C++/WinRT is the simplest way to use DirectX without compromising performance. 이 항목에서는 Direct2D 코드 예제를 사용하여 C++/WinRT를 통해 COM 클래스 및 인터페이스를 사용하는 방법을 보여 줍니다.This topic uses a Direct2D code example to show how to use C++/WinRT to consume COM classes and interfaces. 동일한 C++/WinRT 프로젝트에서 COM 및 Windows 런타임 프로그래밍을 함께 사용할 수도 있습니다.You can, of course, mix COM and Windows Runtime programming within the same C++/WinRT project.

이 항목의 끝에는 최소 Direct2D 애플리케이션의 전체 소스 코드 목록이 나와 있습니다.At the end of this topic, you'll find a full source code listing of a minimal Direct2D application. 해당 코드에서 발췌한 내용을 사용하여 C++/WinRT 라이브러리의 다양한 기능을 통해 C++/WinRT에서 COM 구성 요소를 사용하는 방법을 설명하겠습니다.We'll lift excerpts from that code and use them to illustrate how to consume COM components using C++/WinRT using various facilities of the C++/WinRT library.

COM 스마트 포인터(winrt::com_ptr)COM smart pointers (winrt::com_ptr)

COM을 사용하여 프로그래밍하는 경우 개체가 아닌 인터페이스로 직접 작업합니다(COM의 발전된 형태인 Windows 런타임 API의 경우에도 백그라운드에서 동일한 방식으로 작동함).When you program with COM, you work directly with interfaces rather than with objects (that's also true behind the scenes for Windows Runtime APIs, which are an evolution of COM). 예를 들어 COM 클래스에서 함수를 호출하려면 클래스를 활성화하고 인터페이스를 다시 가져온 다음, 해당 인터페이스에서 함수를 호출합니다.To call a function on a COM class, for example, you activate the class, get an interface back, and then you call functions on that interface. 개체의 상태에 액세스하려면 데이터 멤버에 직접 액세스하지 않고, 대신 인터페이스에서 접근자 및 변경자 함수를 호출합니다.To access the state of an object, you don't access its data members directly; instead, you call accessor and mutator functions on an interface.

보다 구체적으로, 인터페이스 ‘포인터’ 조작을 말하는 것입니다. To be more specific, we're talking about interacting with interface pointers. 이러한 용도로, C++/WinRT에 있는 COM 스마트 포인터 형식, 즉 winrt::com_ptr 형식을 활용합니다.And for that, we benefit from the existence of the COM smart pointer type in C++/WinRT—the winrt::com_ptr type.

#include <d2d1_1.h>
...
winrt::com_ptr<ID2D1Factory1> factory;

위의 코드는 ID2D1Factory1 COM 인터페이스에 대한 초기화되지 않은 스마트 포인터를 선언하는 방법을 보여 줍니다.The code above shows how to declare an uninitialized smart pointer to a ID2D1Factory1 COM interface. 스마트 포인터가 초기화되지 않았으므로, 아직 실제 개체에 속하는 ID2D1Factory1 인터페이스를 가리키지는 않습니다(인터페이스를 가리키지 않음).The smart pointer is uninitialized, so it's not yet pointing to a ID2D1Factory1 interface belonging to any actual object (it's not pointing to an interface at all). 그러나 잠재적으로 인터페이스를 가리킬 수 있으며, 스마트 포인터로서 COM 참조 계산을 통해 가리키는 인터페이스의 소유 개체 및 해당 인터페이스에서 함수를 호출하는 데 사용되는 매체의 수명을 관리할 수 있습니다.But it has the potential to do so; and (being a smart pointer) it has the ability via COM reference counting to manage the lifetime of the owning object of the interface that it points to, and to be the medium by which you call functions on that interface.

인터페이스 포인터를 void로 반환하는 COM 함수COM functions that return an interface pointer as void

com_ptr::put_void 함수를 호출하여 초기화되지 않은 스마트 포인터의 기본 원시 포인터에 쓸 수 있습니다.You can call the com_ptr::put_void function to write to an uninitialized smart pointer's underlying raw pointer.

D2D1_FACTORY_OPTIONS options{ D2D1_DEBUG_LEVEL_NONE };
D2D1CreateFactory(
    D2D1_FACTORY_TYPE_SINGLE_THREADED,
    __uuidof(factory),
    &options,
    factory.put_void()
);

위의 코드는 void** 형식인 마지막 매개 변수를 통해 ID2D1Factory1 인터페이스 포인터를 반환하는 D2D1CreateFactory 함수를 호출합니다.The code above calls the D2D1CreateFactory function, which returns an ID2D1Factory1 interface pointer via its last parameter, which has void** type. 대부분의 COM 함수는 void** 를 반환합니다.Many COM functions return a void**. 이러한 함수의 경우 표시된 대로 com_ptr::put_void를 사용합니다.For such functions, use com_ptr::put_void as shown.

특정 인터페이스 포인터를 반환하는 COM 함수COM functions that return a specific interface pointer

D3D11CreateDevice 함수는 ID3D11Device** 형식인 끝에서 세 번째 매개 변수를 통해 ID3D11Device 인터페이스 포인터를 반환합니다.The D3D11CreateDevice function returns an ID3D11Device interface pointer via its third-from-last parameter, which has ID3D11Device** type. 이와 같이 특정 인터페이스 포인터를 반환하는 함수의 경우 com_ptr::put를 사용합니다.For functions that return a specific interface pointer like that, use com_ptr::put.

winrt::com_ptr<ID3D11Device> device;
D3D11CreateDevice(
    ...
    device.put(),
    ...);

섹션에서 이 코드 예제 앞에 있는 코드 예제는 원시 D2D1CreateFactory 함수를 호출하는 방법을 보여 줍니다.The code example in the section before this one shows how to call the raw D2D1CreateFactory function. 그러나 이 항목의 코드 예제는 D2D1CreateFactory를 호출할 때 원시 API를 래핑하는 도우미 함수 템플릿을 사용하므로, 실제로 com_ptr::put를 사용합니다.But in fact, when the code example for this topic calls D2D1CreateFactory, it uses a helper function template that wraps the raw API, and so the code example actually uses com_ptr::put.

winrt::com_ptr<ID2D1Factory1> factory;
D2D1CreateFactory(
    D2D1_FACTORY_TYPE_SINGLE_THREADED,
    options,
    factory.put());

인터페이스 포인터를 IUnknown으로 반환하는 COM 함수COM functions that return an interface pointer as IUnknown

DWriteCreateFactory 함수는 IUnknown 형식인 마지막 매개 변수를 통해 DirectWrite 팩터리 인터페이스 포인터를 반환합니다.The DWriteCreateFactory function returns a DirectWrite factory interface pointer via its last parameter, which has IUnknown type. 이러한 함수의 경우 com_ptr::put를 사용하지만, IUnknown으로 재해석 캐스팅합니다.For such a function, use com_ptr::put, but reinterpret cast that to IUnknown.

DWriteCreateFactory(
    DWRITE_FACTORY_TYPE_SHARED,
    __uuidof(dwriteFactory2),
    reinterpret_cast<IUnknown**>(dwriteFactory2.put()));

winrt::com_ptr 재배치Re-seat a winrt::com_ptr

중요

이미 배치된 winrt::com_ptr이 있고(내부 원시 포인터에 이미 대상이 있음) 다른 개체를 가리키도록 다시 배치하려는 경우, 아래 코드 예제와 같이 nullptr을 먼저 할당해야 합니다.If you have a winrt::com_ptr that's already seated (its internal raw pointer already has a target) and you want to re-seat it to point to a different object, then you first need to assign nullptr to it—as shown in the code example below. 할당하지 않으면, com_ptr::put 또는 com_ptr::put_void를 호출할 때 이미 배치된 com_ptr에서 내부 포인터가 Null이 아님을 어설션하여 문제가 부각됩니다.If you don't, then an already-seated com_ptr will draw the issue to your attention (when you call com_ptr::put or com_ptr::put_void) by asserting that its internal pointer is not null.

winrt::com_ptr<ID2D1SolidColorBrush> brush;
...
    brush.put()
...
brush = nullptr; // Important because we're about to re-seat
target->CreateSolidColorBrush(
    color_orange,
    D2D1::BrushProperties(0.8f),
    brush.put()));

HRESULT 오류 코드 처리Handle HRESULT error codes

COM 함수에서 반환된 HRESULT의 값을 확인하고 오류 코드를 나타내는 이벤트에서 예외를 throw하려면 winrt::check_hresult를 호출합니다.To check the value of a HRESULT returned from a COM function, and throw an exception in the event that it represents an error code, call winrt::check_hresult.

winrt::check_hresult(D2D1CreateFactory(
    D2D1_FACTORY_TYPE_SINGLE_THREADED,
    __uuidof(factory),
    options,
    factory.put_void()));

특정 인터페이스 포인터를 사용하는 COM 함수COM functions that take a specific interface pointer

com_ptr::get 함수를 호출하여 동일한 형식의 특정 인터페이스 포인터를 사용하는 함수에 com_ptr을 전달할 수 있습니다.You can call the com_ptr::get function to pass your com_ptr to a function that takes a specific interface pointer of the same type.

... ExampleFunction(
    winrt::com_ptr<ID2D1Factory1> const& factory,
    winrt::com_ptr<IDXGIDevice> const& dxdevice)
{
    ...
    winrt::check_hresult(factory->CreateDevice(dxdevice.get(), ...));
    ...
}

IUnknown 인터페이스 포인터를 사용하는 COM 함수COM functions that take an IUnknown interface pointer

winrt::get_unknown 프리 함수를 호출하여 IUnknown 인터페이스 포인터를 사용하는 함수에 com_ptr을 전달할 수 있습니다.You can call the winrt::get_unknown free function to pass your com_ptr to a function that takes an IUnknown interface pointer.

winrt::check_hresult(factory->CreateSwapChainForCoreWindow(
    ...
    winrt::get_unknown(CoreWindow::GetForCurrentThread()),
    ...));

COM 스마트 포인터 전달 및 반환Passing and returning COM smart pointers

winrt::com_ptr 형식의 COM 스마트 포인터를 사용하는 함수는 상수 참조 또는 참조로 사용해야 합니다.A function taking a COM smart pointer in the form of a winrt::com_ptr should do so by constant reference, or by reference.

... GetDxgiFactory(winrt::com_ptr<ID3D11Device> const& device) ...

... CreateDevice(..., winrt::com_ptr<ID3D11Device>& device) ...

winrt::com_ptr을 반환하는 함수는 값으로 반환해야 합니다.A function that returns a winrt::com_ptr should do so by value.

winrt::com_ptr<ID2D1Factory1> CreateFactory() ...

다른 인터페이스에 대한 COM 스마트 포인터 쿼리Query a COM smart pointer for a different interface

com_ptr:: 함수로 사용하여 다른 인터페이스에 대한 COM 스마트 포인터를 쿼리할 수 있습니다.You can use the com_ptr::as function to query a COM smart pointer for a different interface. 쿼리가 실패하면 함수에서 예외를 throw합니다.The function throws an exception if the query doesn't succeed.

void ExampleFunction(winrt::com_ptr<ID3D11Device> const& device)
{
    ...
    winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };
    ...
}

또는 쿼리의 성공 여부를 확인하기 위해 nullptr과 비교할 수 있는 값을 반환하는 com_ptr::try_as를 사용합니다.Alternatively, use com_ptr::try_as, which returns a value that you can check against nullptr to see whether the query succeeded.

최소 Direct2D 애플리케이션의 전체 소스 코드 목록Full source code listing of a minimal Direct2D application

이 소스 코드 예제를 빌드 및 실행하려는 경우, 먼저 Visual Studio에서 새 코어 앱(C++/WinRT) 을 만듭니다.If you want to build and run this source code example then first, in Visual Studio, create a new Core App (C++/WinRT). Direct2D는 프로젝트에 적합한 이름이지만, 원하는 이름을 임의로 지정할 수 있습니다.Direct2D is a reasonable name for the project, but you can name it anything you like. App.cpp를 열고 전체 내용을 삭제한 다음, 아래 목록을 붙여넣습니다.Open App.cpp, delete its entire contents, and paste in the listing below.

아래 코드는 가능한 경우 winrt::com_ptr::capture 함수를 사용합니다.The code below uses the winrt::com_ptr::capture function where possible. WINRT_ASSERT는 매크로 정의이며 _ASSERTE로 확장됩니다.WINRT_ASSERT is a macro definition, and it expands to _ASSERTE.

#include "pch.h"
#include <d2d1_1.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#include <winrt/Windows.Graphics.Display.h>

using namespace winrt;

using namespace Windows;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::UI;
using namespace Windows::UI::Core;
using namespace Windows::Graphics::Display;

namespace
{
    winrt::com_ptr<ID2D1Factory1> CreateFactory()
    {
        D2D1_FACTORY_OPTIONS options{};

#ifdef _DEBUG
        options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif

        winrt::com_ptr<ID2D1Factory1> factory;

        winrt::check_hresult(D2D1CreateFactory(
            D2D1_FACTORY_TYPE_SINGLE_THREADED,
            options,
            factory.put()));

        return factory;
    }

    HRESULT CreateDevice(D3D_DRIVER_TYPE const type, winrt::com_ptr<ID3D11Device>& device)
    {
        WINRT_ASSERT(!device);

        return D3D11CreateDevice(
            nullptr,
            type,
            nullptr,
            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
            nullptr, 0,
            D3D11_SDK_VERSION,
            device.put(),
            nullptr,
            nullptr);
    }

    winrt::com_ptr<ID3D11Device> CreateDevice()
    {
        winrt::com_ptr<ID3D11Device> device;
        HRESULT hr{ CreateDevice(D3D_DRIVER_TYPE_HARDWARE, device) };

        if (DXGI_ERROR_UNSUPPORTED == hr)
        {
            hr = CreateDevice(D3D_DRIVER_TYPE_WARP, device);
        }

        winrt::check_hresult(hr);
        return device;
    }

    winrt::com_ptr<ID2D1DeviceContext> CreateRenderTarget(
        winrt::com_ptr<ID2D1Factory1> const& factory,
        winrt::com_ptr<ID3D11Device> const& device)
    {
        WINRT_ASSERT(factory);
        WINRT_ASSERT(device);

        winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };

        winrt::com_ptr<ID2D1Device> d2device;
        winrt::check_hresult(factory->CreateDevice(dxdevice.get(), d2device.put()));

        winrt::com_ptr<ID2D1DeviceContext> target;
        winrt::check_hresult(d2device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, target.put()));
        return target;
    }

    winrt::com_ptr<IDXGIFactory2> GetDxgiFactory(winrt::com_ptr<ID3D11Device> const& device)
    {
        WINRT_ASSERT(device);

        winrt::com_ptr<IDXGIDevice> const dxdevice{ device.as<IDXGIDevice>() };

        winrt::com_ptr<IDXGIAdapter> adapter;
        winrt::check_hresult(dxdevice->GetAdapter(adapter.put()));

        winrt::com_ptr<IDXGIFactory2> factory;
        factory.capture(adapter, &IDXGIAdapter::GetParent);
        return factory;
    }

    void CreateDeviceSwapChainBitmap(
        winrt::com_ptr<IDXGISwapChain1> const& swapchain,
        winrt::com_ptr<ID2D1DeviceContext> const& target)
    {
        WINRT_ASSERT(swapchain);
        WINRT_ASSERT(target);

        winrt::com_ptr<IDXGISurface> surface;
        surface.capture(swapchain, &IDXGISwapChain1::GetBuffer, 0);

        D2D1_BITMAP_PROPERTIES1 const props{ D2D1::BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)) };

        winrt::com_ptr<ID2D1Bitmap1> bitmap;

        winrt::check_hresult(target->CreateBitmapFromDxgiSurface(surface.get(),
            props,
            bitmap.put()));

        target->SetTarget(bitmap.get());
    }

    winrt::com_ptr<IDXGISwapChain1> CreateSwapChainForCoreWindow(winrt::com_ptr<ID3D11Device> const& device)
    {
        WINRT_ASSERT(device);

        winrt::com_ptr<IDXGIFactory2> const factory{ GetDxgiFactory(device) };

        DXGI_SWAP_CHAIN_DESC1 props{};
        props.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
        props.SampleDesc.Count = 1;
        props.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        props.BufferCount = 2;
        props.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;

        winrt::com_ptr<IDXGISwapChain1> swapChain;

        winrt::check_hresult(factory->CreateSwapChainForCoreWindow(
            device.get(),
            winrt::get_unknown(CoreWindow::GetForCurrentThread()),
            &props,
            nullptr, // all or nothing
            swapChain.put()));

        return swapChain;
    }

    constexpr D2D1_COLOR_F color_white{ 1.0f,  1.0f,  1.0f,  1.0f };
    constexpr D2D1_COLOR_F color_orange{ 0.92f,  0.38f,  0.208f,  1.0f };
}

struct App : implements<App, IFrameworkViewSource, IFrameworkView>
{
    winrt::com_ptr<ID2D1Factory1> m_factory;
    winrt::com_ptr<ID2D1DeviceContext> m_target;
    winrt::com_ptr<IDXGISwapChain1> m_swapChain;
    winrt::com_ptr<ID2D1SolidColorBrush> m_brush;
    float m_dpi{};

    IFrameworkView CreateView()
    {
        return *this;
    }

    void Initialize(CoreApplicationView const&)
    {
    }

    void Load(hstring const&)
    {
        CoreWindow const window{ CoreWindow::GetForCurrentThread() };

        window.SizeChanged([&](auto&&...)
        {
            if (m_target)
            {
                ResizeSwapChainBitmap();
                Render();
            }
        });

        DisplayInformation const display{ DisplayInformation::GetForCurrentView() };
        m_dpi = display.LogicalDpi();

        display.DpiChanged([&](DisplayInformation const& display, IInspectable const&)
        {
            if (m_target)
            {
                m_dpi = display.LogicalDpi();
                m_target->SetDpi(m_dpi, m_dpi);
                CreateDeviceSizeResources();
                Render();
            }
        });

        m_factory = CreateFactory();
        CreateDeviceIndependentResources();
    }

    void Uninitialize()
    {
    }

    void Run()
    {
        CoreWindow const window{ CoreWindow::GetForCurrentThread() };
        window.Activate();

        Render();
        CoreDispatcher const dispatcher{ window.Dispatcher() };
        dispatcher.ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
    }

    void SetWindow(CoreWindow const&) {}

    void Draw()
    {
        m_target->Clear(color_white);

        D2D1_SIZE_F const size{ m_target->GetSize() };
        D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f };
        m_target->DrawRectangle(rect, m_brush.get(), 100.0f);

        char buffer[1024];
        (void)snprintf(buffer, sizeof(buffer), "Draw %.2f x %.2f @ %.2f\n", size.width, size.height, m_dpi);
        ::OutputDebugStringA(buffer);
    }

    void Render()
    {
        if (!m_target)
        {
            winrt::com_ptr<ID3D11Device> const device{ CreateDevice() };
            m_target = CreateRenderTarget(m_factory, device);
            m_swapChain = CreateSwapChainForCoreWindow(device);

            CreateDeviceSwapChainBitmap(m_swapChain, m_target);

            m_target->SetDpi(m_dpi, m_dpi);

            CreateDeviceResources();
            CreateDeviceSizeResources();
        }

        m_target->BeginDraw();
        Draw();
        m_target->EndDraw();

        HRESULT const hr{ m_swapChain->Present(1, 0) };

        if (S_OK != hr && DXGI_STATUS_OCCLUDED != hr)
        {
            ReleaseDevice();
        }
    }

    void ReleaseDevice()
    {
        m_target = nullptr;
        m_swapChain = nullptr;

        ReleaseDeviceResources();
    }

    void ResizeSwapChainBitmap()
    {
        WINRT_ASSERT(m_target);
        WINRT_ASSERT(m_swapChain);

        m_target->SetTarget(nullptr);

        if (S_OK == m_swapChain->ResizeBuffers(0, // all buffers
            0, 0, // client area
            DXGI_FORMAT_UNKNOWN, // preserve format
            0)) // flags
        {
            CreateDeviceSwapChainBitmap(m_swapChain, m_target);
            CreateDeviceSizeResources();
        }
        else
        {
            ReleaseDevice();
        }
    }

    void CreateDeviceIndependentResources()
    {
    }

    void CreateDeviceResources()
    {
        winrt::check_hresult(m_target->CreateSolidColorBrush(
            color_orange,
            D2D1::BrushProperties(0.8f),
            m_brush.put()));
    }

    void CreateDeviceSizeResources()
    {
    }

    void ReleaseDeviceResources()
    {
        m_brush = nullptr;
    }
};

int __stdcall wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    CoreApplication::Run(winrt::make<App>());
}

BSTR, VARIANT 등의 COM 형식 작업Working with COM types, such as BSTR and VARIANT

C++/WinRT는 COM 인터페이스 구현 및 호출을 모두 지원합니다.As you can see, C++/WinRT provides support for both implementing and calling COM interfaces. BSTR, VARIANT 등의 COM 형식을 사용하려면 WIL(Windows 구현 라이브러리)에서 제공하는 wil::unique_bstr, wil::unique_variant(리소스 수명 관리) 등의 래퍼를 사용하는 것이 좋습니다.For using COM types, such as BSTR and VARIANT, we recommend that you use wrappers provided by the Windows Implementation Libraries (WIL), such as wil::unique_bstr and wil::unique_variant (which manage resources lifetimes).

WIL은 ATL(액티브 템플릿 라이브러리) 및 Visual C++ 컴파일러의 COM 지원과 같은 프레임워크를 대체합니다.WIL supersedes frameworks such as the Active Template Library (ATL), and the Visual C++ compiler's COM Support. 또한 고유한 래퍼를 작성하거나 적절한 API와 함께 BSTR, VARIANT 등의 COM 형식을 원시 형식으로 사용하는 것보다 WIL이 권장됩니다.And we recommend it over writing your own wrappers, or using COM types such as BSTR and VARIANT in their raw form (together with the appropriate APIs).

네임스페이스 충돌 방지Avoiding namespace collisions

일반적으로 C++/WinRT에서는 using 지시문을 자유롭게 사용할 수 있습니다(이 항목의 코드 목록에 있는 설명 참조).It's common practice in C++/WinRT—as the code listing in this topic demonstrates—to use using-directives liberally. 그러나 이로 인해 충돌하는 이름을 전역 네임스페이스로 가져오는 문제가 발생할 수 있습니다.In some cases, though, that can lead to the problem of importing colliding names into the global namespace. 예를 들면 다음과 같습니다.Here's an example.

C++/WinRT에 이름이 winrt::Windows::Foundation::IUnknown인 형식이 포함되어 있지만 COM에서는 ::IUnknown라는 이름의 형식을 정의합니다.C++/WinRT contains a type named winrt::Windows::Foundation::IUnknown; while COM defines a type named ::IUnknown. 따라서 COM 헤더를 사용하는 C++/WinRT 프로젝트에서 다음 코드를 고려해 보세요.So consider the following code, in a C++/WinRT project that consumes COM headers.

using namespace winrt::Windows::Foundation;
...
void MyFunction(IUnknown*); // error C2872:  'IUnknown': ambiguous symbol

정규화되지 않은 이름인 IUnknown은 전역 네임스페이스에서 충돌하므로 모호한 기호 컴파일러 오류가 발생합니다.The unqualified name IUnknown collides in the global namespace, hence the ambiguous symbol compiler error. 대신 다음과 같이 C++/WinRT 버전의 이름을 winrt 네임스페이스로 격리할 수 있습니다.Instead, you can isolate the C++/WinRT version of the name into the winrt namespace, like this.

namespace winrt
{
    using namespace Windows::Foundation;
}
...
void MyFunctionA(IUnknown*); // Ok.
void MyFunctionB(winrt::IUnknown const&); // Ok.

아니면, 간편하게 using namespace winrt를 사용할 수도 있습니다.Or, if you want the convenience of using namespace winrt, then you can. 다음과 같이 전역 버전의 IUnknown을 한정하면 됩니다.You just need to qualify the global version of IUnknown, like this.

using namespace winrt;
namespace winrt
{
    using namespace Windows::Foundation;
}
...
void MyFunctionA(::IUnknown*); // Ok.
void MyFunctionB(winrt::IUnknown const&); // Ok.

물론, 이 방법은 모든 C++/WinRT 네임스페이스에서 작동합니다.Naturally, this works with any C++/WinRT namespace.

namespace winrt
{
    using namespace Windows::Storage;
    using namespace Windows::System;
}

그 다음, winrt::Windows::Storage::StorageFile(예를 들어, 간단히 winrt::StorageFile)을 참조할 수 있습니다.You can then refer to winrt::Windows::Storage::StorageFile, for example, as just winrt::StorageFile.

중요 APIImportant APIs