C++/WinRT와 ABI 사이의 InteropInterop between C++/WinRT and the ABI

이번 항목에서는 SDK ABI(Application Binary Interface)와 C++/WinRT 개체를 서로 변환하는 방법에 대해서 설명합니다.This topic shows how to convert between SDK application binary interface (ABI) and C++/WinRT objects. 여기에서 설명하는 방법은 Windows 런타임을 통한 두 가지 프로그래밍 방법을 사용하는 코드 사이의 Interop에 사용하거나, 코드를 ABI에서 C++/WinRT로 점차 마이그레이션하는 데 사용할 수도 있습니다.You can use these techniques to interop between code that uses these two ways of programming with the Windows Runtime, or you can use them as you gradually move your code from the ABI to C++/WinRT.

일반적으로 C++/WinRT는 ABI 형식을 void* 로 표시하므로 플랫폼 헤더 파일을 포함하지 않아도 됩니다.In general, C++/WinRT exposes ABI types as void*, so that you don't need to include platform header files.

Windows 런타임 ABI란 무엇이며, ABI 형식이란 무엇인가요?What is the Windows Runtime ABI, and what are ABI types?

Windows 런타임 클래스(런타임 클래스)는 추상화입니다.A Windows Runtime class (runtime class) is really an abstraction. 이 추상화는 여러 프로그래밍 언어가 개체와 상호 작용할 수 있게 하는 이진 인터페이스(Application Binary Interface, ABI)를 정의합니다.This abstraction defines a binary interface (the Application Binary Interface, or ABI) that allows various programming languages to interact with an object. 프로그래밍 언어에 관계없이 Windows 런타임 개체와의 클라이언트 코드 상호 작용은 개체의 ABI에 대한 호출로 번역되는 클라이언트 언어 구문으로 최저 수준에서 수행됩니다.Regardless of programming language, client code interaction with a Windows Runtime object happens at the lowest level, with client language constructs translated into calls into the object's ABI.

“%WindowsSdkDir%Include\10.0.17134.0\winrt” 폴더(필요한 경우 SDK 버전 번호를 사용자 요건에 맞게 조정)에 있는 Windows SDK 헤더가 Windows 런타임 ABI 헤더 파일입니다.The Windows SDK headers in the folder "%WindowsSdkDir%Include\10.0.17134.0\winrt" (adjust the SDK version number for your case, if necessary), are the Windows Runtime ABI header files. 이 파일은 MIDL 컴파일러에서 생성되었습니다.They were produced by the MIDL compiler. 다음은 이 헤더 중 하나를 추가하는 예제입니다.Here's an example of including one of these headers.

#include <windows.foundation.h>

다음은 특정 SDK 헤더에서 찾을 수 있는 ABI 형식 중 하나를 간단히 표현한 예제입니다.And here's a simplified example of one of the ABI types that you'll find in that particular SDK header. ABI 네임스페이스, Windows::Foundation, 그리고 모든 다른 Windows 네임스페이스는 ABI 네임스페이스 내 SDK 헤더에 의해 선언됩니다.Note the ABI namespace; Windows::Foundation, and all other Windows namespaces, are declared by the SDK headers within the ABI namespace.

namespace ABI::Windows::Foundation
{
    IUriRuntimeClass : public IInspectable
    {
    public:
        /* [propget] */ virtual HRESULT STDMETHODCALLTYPE get_AbsoluteUri(/* [retval, out] */__RPC__deref_out_opt HSTRING * value) = 0;
        ...
    }
}

IUriRuntimeClass는 COM 인터페이스입니다.IUriRuntimeClass is a COM interface. 하지만 IUriRuntimeClassIInspectable을 기반으로 한다는 점에서 Windows 런타임 인터페이스에 더욱 가깝습니다.But more than that—since its base is IInspectableIUriRuntimeClass is a Windows Runtime interface. 예외 발생보다는 오히려 HRESULT 반환 형식에 주의하세요.Note the HRESULT return type, rather than the raising of exceptions. 또한 HSTRING 핸들 같은 아티팩트의 사용도 주의할 필요가 있습니다(작업을 마치면 핸들을 다시 nullptr로 설정하는 것이 좋음).And the use of artifacts such as the HSTRING handle (it's good practice to set that handle back to nullptr when you're finished with it). 이렇게 하면 Windows 런타임이 애플리케이션 이진 수준, 즉 COM 프로그래밍 수준에서 어떻게 보이는지 알 수 있습니다.This gives a taste of what the Windows Runtime looks like at the application binary level; in other words, at the COM programming level.

Windows 런타임은 COM(구성 요소 개체 모델) API를 기반으로 합니다.The Windows Runtime is based on Component Object Model (COM) APIs. 따라서 Windows 런타임에는 COM API를 통하거나, ‘언어 프로젝션’을 통해 액세스할 수 있습니다.You can access the Windows Runtime that way, or you can access it through language projections. 프로젝션은 COM 세부 정보를 숨기며, 지정된 언어에 더욱 자연스러운 프로그래밍 환경을 제공합니다.A projection hides the COM details, and provides a more natural programming experience for a given language.

예를 들어, “%WindowsSdkDir%Include\10.0.17134.0\cppwinrt\winrt” 폴더(다시 말하지만 필요한 경우 사용자 요건에 맞게 SDK 버전 번호를 조정) 안을 보면 C++/WinRT 언어 프로젝션 헤더를 찾을 수 있습니다.For example, if you look in the folder "%WindowsSdkDir%Include\10.0.17134.0\cppwinrt\winrt" (again, adjust the SDK version number for your case, if necessary), then you'll find the C++/WinRT language projection headers. Windows 네임스페이스마다 ABI 헤더가 하나씩 있는 것처럼 각 Windows 네임스페이스에도 헤더가 있습니다.There's a header for each Windows namespace, just like there's one ABI header per Windows namespace. 다음은 C++/WinRT 헤더 중 하나를 추가하는 예제입니다.Here's an example of including one of the C++/WinRT headers.

#include <winrt/Windows.Foundation.h>

다음은 위의 헤더에서 시작하여 방금 본 ABI 형식에 상응하는 C++/WinRT를 간략하게 표현한 예제입니다.And, from that header, here (simplified) is the C++/WinRT equivalent of that ABI type we just saw.

namespace winrt::Windows::Foundation
{
    struct Uri : IUriRuntimeClass, ...
    {
        winrt::hstring AbsoluteUri() const { ... }
        ...
    };
}

여기에서 사용하는 인터페이스는 최신 표준 C++입니다.The interface here is modern, standard C++. 이 인터페이스는 HRESULT를 사용하지 않습니다(필요한 경우 C++/WinRT에서 예외 발생).It does away with HRESULTs (C++/WinRT raises exceptions if necessary). 접근자 함수는 범위 끝에서 정리되는 단순 문자열 개체를 반환합니다.And the accessor function returns a simple string object, which is cleaned up at the end of its scope.

이 항목은 ABI(Application Binary Interface) 계층에서 사용할 수 있는 코드와 interop하거나 코드를 이식하려는 경우를 다룹니다.This topic is for cases when you want to interop with, or port, code that works at the Application Binary Interface (ABI) layer.

코드의 ABI 형식 변환Converting to and from ABI types in code

안전성과 단순성, 그리고 양방향 변환 시 편의성을 고려하여 간단하게 winrt::com_ptr, com_ptr::aswinrt::Windows::Foundation::IUnknown::as를 사용할 수 있습니다.For safety and simplicity, for conversions in both directions you can simply use winrt::com_ptr, com_ptr::as, and winrt::Windows::Foundation::IUnknown::as. 다음은 C++/WinRT 프로젝션 및 ABI 간의 잠재적인 네임스페이스 충돌을 다루기 위해 다른 격리 영역에 대한 네임스페이스 별칭을 사용하는 방법을 보여 주는 코드 예제입니다(콘솔 앱 프로젝트 템플릿 기반).Here's a code example (based on the Console App project template), which also illustrates how you can use namespace aliases for the different islands to deal with otherwise potential namespace collisions between the C++/WinRT projection and the ABI.

// pch.h
#pragma once
#include <windows.foundation.h>
#include <unknwn.h>
#include "winrt/Windows.Foundation.h"

// main.cpp
#include "pch.h"

namespace winrt
{
    using namespace Windows::Foundation;
}

namespace abi
{
    using namespace ABI::Windows::Foundation;
};

int main()
{
    winrt::init_apartment();

    winrt::Uri uri(L"http://aka.ms/cppwinrt");

    // Convert to an ABI type.
    winrt::com_ptr<abi::IStringable> ptr{ uri.as<abi::IStringable>() };

    // Convert from an ABI type.
    uri = ptr.as<winrt::Uri>();
    winrt::IStringable uriAsIStringable{ ptr.as<winrt::IStringable>() };
}

as 함수의 구현이 QueryInterface를 호출합니다.The implementations of the as functions call QueryInterface. AddRef만 호출하는 하위 수준의 변환을 원한다면 도우미 함수로 winrt::copy_to_abiwinrt::copy_from_abi를 사용할 수 있습니다.If you want lower-level conversions that only call AddRef, then you can use the winrt::copy_to_abi and winrt::copy_from_abi helper functions. 다음 코드 예제에서는 이 하위 수준 변환을 위의 코드 예제에 추가합니다.This next code example adds these lower-level conversions to the code example above.

중요

ABI 형식과 상호 운용할 때 사용되는 ABI 형식이 C++/WinRT 개체의 기본 인터페이스에 해당해야 합니다.When interoperating with ABI types it's critical that the ABI type used corresponds to the default interface of the C++/WinRT object. 그렇지 않으면 ABI 형식에서 메서드를 호출하면 실제로는 기본 인터페이스의 동일한 vtable 슬롯에서 메서드가 호출되고 예기치 않은 결과가 발생합니다.Otherwise, invocations of methods on the ABI type will actually end up calling methods in the same vtable slot on the default interface with very unexpected results. winrt::copy_to_abi는 모든 ABI 형식에 void* 를 사용하고 호출자가 형식 불일치에 주의한다고 가정하므로 컴파일 시 이 문제를 방지하지 않습니다.Note that winrt::copy_to_abi does not protect against this at compile time since it uses void* for all ABI types and assumes that the caller has been careful not to mis-match the types. 이는 ABI 형식이 전혀 사용되지 않을 때 C++/WinRT 헤더가 ABI 헤더를 참조하도록 요구하는 일이 없도록 방지하기 위한 조치입니다.This is to avoid requiring C++/WinRT headers to reference ABI headers when ABI types may never be used.

int main()
{
    // The code in main() already shown above remains here.

    // Lower-level conversions that only call AddRef.

    // Convert to an ABI type.
    ptr = nullptr;
    winrt::copy_to_abi(uriAsIStringable, *ptr.put_void());

    // Convert from an ABI type.
    uri = nullptr;
    winrt::copy_from_abi(uriAsIStringable, ptr.get());
    ptr = nullptr;
}

다음은 비슷한 하위 수준의 변환 예제이지만 원시 포인터를 ABI 인터페이스 형식(Windows SDK 헤더에서 정의한 형식)에 사용한다는 점에서 다릅니다.Here are other similarly low-level conversions techniques but using raw pointers to ABI interface types (those defined by the Windows SDK headers) this time.

    // The code in main() already shown above remains here.

    // Copy to an owning raw ABI pointer with copy_to_abi.
    abi::IStringable* owning{ nullptr };
    winrt::copy_to_abi(uriAsIStringable, *reinterpret_cast<void**>(&owning));

    // Copy from a raw ABI pointer.
    uri = nullptr;
    winrt::copy_from_abi(uriAsIStringable, owning);
    owning->Release();

주소만 복사하는 최하위 수준의 변환일 때는 도우미 함수로 winrt::get_abi, winrt::detach_abiwinrt::attach_abi를 사용할 수 있습니다.For the lowest-level conversions, which only copy addresses, you can use the winrt::get_abi, winrt::detach_abi, and winrt::attach_abi helper functions.

WINRT_ASSERT는 매크로 정의이며 _ASSERTE로 확장됩니다.WINRT_ASSERT is a macro definition, and it expands to _ASSERTE.

    // The code in main() already shown above remains here.

    // Lowest-level conversions that only copy addresses

    // Convert to a non-owning ABI object with get_abi.
    abi::IStringable* non_owning{ static_cast<abi::IStringable*>(winrt::get_abi(uriAsIStringable)) };
    WINRT_ASSERT(non_owning);

    // Avoid interlocks this way.
    owning = static_cast<abi::IStringable*>(winrt::detach_abi(uriAsIStringable));
    WINRT_ASSERT(!uriAsIStringable);
    winrt::attach_abi(uriAsIStringable, owning);
    WINRT_ASSERT(uriAsIStringable);

convert_from_abi 함수convert_from_abi function

이 도우미 함수는 오버헤드를 최소화하면서 원시 ABI 인터페이스 포인터를 상응하는 C++/WinRT 개체로 변환합니다.This helper function converts a raw ABI interface pointer to an equivalent C++/WinRT object, with minimal overhead.

template <typename T>
T convert_from_abi(::IUnknown* from)
{
    T to{ nullptr }; // `T` is a projected type.

    winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(),
        winrt::put_abi(to)));

    return to;
}

이 함수는 단순하게 QueryInterface를 호출하여 요청된 C++/WinRT 형식의 기본 인터페이스에 대해 쿼리를 실행합니다.The function simply calls QueryInterface to query for the default interface of the requested C++/WinRT type.

앞에서 본 것처럼 C++/WinRT 개체를 상응하는 ABI 인터페이스 포인터로 변환할 때는 도우미 함수가 필요하지 않습니다.As we've seen, a helper function is not required to convert from a C++/WinRT object to the equivalent ABI interface pointer. 단순히 winrt::Windows::Foundation::IUnknown::as(또는 try_as) 멤버 함수를 사용하여 요청된 인터페이스에 대해 쿼리를 실행하면 됩니다.Simply use the winrt::Windows::Foundation::IUnknown::as (or try_as) member function to query for the requested interface. astry_as 함수는 요청된 ABI 형식을 래핑하는 winrt::com_ptr 개체를 반환합니다.The as and try_as functions return a winrt::com_ptr object wrapping the requested ABI type.

convert_from_abi를 사용하는 코드 예제Code example using convert_from_abi

다음은 이 도우미 함수가 실제로 사용되는 코드 예제입니다.Here's a code example showing this helper function in practice.

// pch.h
#pragma once
#include <windows.foundation.h>
#include <unknwn.h>
#include "winrt/Windows.Foundation.h"

// main.cpp
#include "pch.h"
#include <iostream>

using namespace winrt;
using namespace Windows::Foundation;

namespace winrt
{
    using namespace Windows::Foundation;
}

namespace abi
{
    using namespace ABI::Windows::Foundation;
};

namespace sample
{
    template <typename T>
    T convert_from_abi(::IUnknown* from)
    {
        T to{ nullptr }; // `T` is a projected type.

        winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(),
            winrt::put_abi(to)));

        return to;
    }
    inline auto put_abi(winrt::hstring& object) noexcept
    {
        return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
    }
}

int main()
{
    winrt::init_apartment();

    winrt::Uri uri(L"http://aka.ms/cppwinrt");
    std::wcout << "C++/WinRT: " << uri.Domain().c_str() << std::endl;

    // Convert to an ABI type.
    winrt::com_ptr<abi::IUriRuntimeClass> ptr = uri.as<abi::IUriRuntimeClass>();
    winrt::hstring domain;
    winrt::check_hresult(ptr->get_Domain(sample::put_abi(domain)));
    std::wcout << "ABI: " << domain.c_str() << std::endl;

    // Convert from an ABI type.
    winrt::Uri uri_from_abi = sample::convert_from_abi<winrt::Uri>(ptr.get());

    WINRT_ASSERT(uri.Domain() == uri_from_abi.Domain());
    WINRT_ASSERT(uri == uri_from_abi);
}

ABI COM 인터페이스 포인터와 상호 운용Interoperating with ABI COM interface pointers

아래 도우미 함수 템플릿은 지정된 형식의 ABI COM 인터페이스 포인터를 해당 C++/WinRT 프로젝션된 스마트 포인터 형식으로 복사하는 방법을 보여 줍니다.The helper function template below illustrates how to copy an ABI COM interface pointer of a given type to its equivalent C++/WinRT projected smart pointer type.

template<typename To, typename From>
To to_winrt(From* ptr)
{
    To result{ nullptr };
    winrt::check_hresult(ptr->QueryInterface(winrt::guid_of<To>(), winrt::put_abi(result)));
    return result;
}
...
ID2D1Factory1* com_ptr{ ... };
auto cppwinrt_ptr {to_winrt<winrt::com_ptr<ID2D1Factory1>>(com_ptr)};

다음 도우미 함수 템플릿은 Windows 구현 라이브러리(WIL)의 스마트 포인터 형식에서 복사한다는 점을 제외하면 동일합니다.This next helper function template is equivalent, except that it copies from the smart pointer type from the Windows Implementation Libraries (WIL).

template<typename To, typename From, typename ErrorPolicy>
To to_winrt(wil::com_ptr_t<From, ErrorPolicy> const& ptr)
{
    To result{ nullptr };
    if constexpr (std::is_same_v<typename ErrorPolicy::result, void>)
    {
        ptr.query_to(winrt::guid_of<To>(), winrt::put_abi(result));
    }
    else
    {
        winrt::check_result(ptr.query_to(winrt::guid_of<To>(), winrt::put_abi(result)));
    }
    return result;
}

C++/WinRT를 통한 COM 구성 요소 사용을 참조하세요.Also see Consume COM components with C++/WinRT.

ABI COM 인터페이스 포인터와의 안전하지 않은 상호 운용Unsafe interop with ABI COM interface pointers

다음 표에서는 지정된 형식의 ABI COM 인터페이스 포인터와 해당 C++/WinRT 프로젝션된 스마트 포인터 형식 사이의 안전하지 않은 변환을 보여 줍니다(다른 작업도 포함).The table that follows shows (in addition to other operations) unsafe conversions between an ABI COM interface pointer of a given type and its equivalent C++/WinRT projected smart pointer type. 표에 나온 코드의 경우 다음 선언을 가정합니다.For the code in the table, assume these declarations.

winrt::Sample s;
ISample* p;

void GetSample(_Out_ ISample** pp);

또한 ISampleSample에 대한 기본 인터페이스라고 가정합니다.Assume further that ISample is the default interface for Sample.

이 코드를 사용하여 컴파일 시간에 해당 항목을 어설션할 수 있습니다.You can assert that at compile time with this code.

static_assert(std::is_same_v<winrt::default_interface<winrt::Sample>, winrt::ISample>);
작업Operation 작업 방법How to do it 참고Notes
winrt::Sample에서 ISample* 추출Extract ISample* from winrt::Sample p = reinterpret_cast<ISample*>(get_abi(s)); s는 여전히 개체를 소유합니다.s still owns the object.
winrt::Sample에서 ISample* 분리Detach ISample* from winrt::Sample p = reinterpret_cast<ISample*>(detach_abi(s)); s는 더 이상 개체를 소유하지 않습니다.s no longer owns the object.
ISample* 을 새로운 winrt::Sample로 전송Transfer ISample* to new winrt::Sample winrt::Sample s{ p, winrt::take_ownership_from_abi }; s가 개체의 소유권을 갖습니다.s takes ownership of the object.
ISample*winrt::Sample로 설정Set ISample* into winrt::Sample *put_abi(s) = p; s가 개체의 소유권을 갖습니다.s takes ownership of the object. s가 이전에 소유한 모든 개체가 손실됩니다(디버그 시 어설션됨).Any object previously owned by s is leaked (will assert in debug).
ISample*winrt::Sample에서 수신Receive ISample* into winrt::Sample GetSample(reinterpret_cast<ISample**>(put_abi(s))); s가 개체의 소유권을 갖습니다.s takes ownership of the object. s가 이전에 소유한 모든 개체가 손실됩니다(디버그 시 어설션됨).Any object previously owned by s is leaked (will assert in debug).
ISample*winrt::Sample에서 교체Replace ISample* in winrt::Sample attach_abi(s, p); s가 개체의 소유권을 갖습니다.s takes ownership of the object. s가 이전에 소유한 개체가 해제됩니다.The object previously owned by s is freed.
ISample*winrt::Sample로 복사Copy ISample* to winrt::Sample copy_from_abi(s, p); s가 개체에 대한 새 참조를 만듭니다.s makes a new reference to the object. s가 이전에 소유한 개체가 해제됩니다.The object previously owned by s is freed.
winrt::SampleISample* 로 복사Copy winrt::Sample to ISample* copy_to_abi(s, reinterpret_cast<void*&>(p)); p가 개체의 복사본을 수신합니다.p receives a copy of the object. p가 이전에 소유한 모든 개체가 해제됩니다.Any object previously owned by p is leaked.

ABI의 GUID 구조체와 상호 운용Interoperating with the ABI's GUID struct

GUIDwinrt::guid로 프로젝션됩니다.GUID is projected as winrt::guid. 구현하는 API의 경우 GUID 매개 변수에 winrt::guid를 사용해야 합니다.For APIs that you implement, you must use winrt::guid for GUID parameters. 그렇지 않은 경우 C++/WinRT 헤더를 포함하기 전에 unknwn.h(<windows.h> 및 다른 많은 헤더 파일에서 암시적으로 포함)를 포함하면 winrt::guidGUID 사이에 자동 변환이 수행됩니다.Otherwise, there are automatic conversions between winrt::guid and GUID as long as you include unknwn.h (implicitly included by <windows.h> and many other header files) before you include any C++/WinRT headers.

그렇게 하지 않으면 둘 사이에서 reinterpret_cast를 하드코딩할 수 있습니다.If you don't do that, then you can hard-reinterpret_cast between them. 아래의 표에서는 다음 선언을 가정합니다.For the table that follows, assume these declarations.

winrt::guid winrtguid;
GUID abiguid;
변환Conversion #include <unknwn.h> 사용With #include <unknwn.h> #include <unknwn.h> 사용 안 함Without #include <unknwn.h>
winrt::guid에서 GUIDFrom winrt::guid to GUID abiguid = winrtguid; abiguid = reinterpret_cast<GUID&>(winrtguid);
GUID에서 winrt::guidFrom GUID to winrt::guid winrtguid = abiguid; winrtguid = reinterpret_cast<winrt::guid&>(abiguid);

이와 같이 winrt::guid를 작성할 수 있습니다.You can construct a winrt::guid like this.

winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };

문자열에서 winrt::guid를 작성하는 방법을 보여주는 요점은 make_guid.cpp를 참조하세요.For a gist showing how to construct a winrt::guid from a string, see make_guid.cpp.

ABI의 HSTRING과 상호 운용Interoperating with the ABI's HSTRING

다음 표에서는 winrt::hstringHSTRING 사이의 변환 및 기타 작업을 보여 줍니다.The table that follows shows conversions between winrt::hstring and HSTRING, and other operations. 표에 나온 코드의 경우 다음 선언을 가정합니다.For the code in the table, assume these declarations.

winrt::hstring s;
HSTRING h;

void GetString(_Out_ HSTRING* value);
작업Operation 작업 방법How to do it 참고Notes
hstring에서 HSTRING 추출Extract HSTRING from hstring h = static_cast<HSTRING>(get_abi(s)); s는 여전히 문자열을 소유합니다.s still owns the string.
hstring에서 HSTRING 분리Detach HSTRING from hstring h = reinterpret_cast<HSTRING>(detach_abi(s)); s는 더 이상 문자열을 소유하지 않습니다.s no longer owns the string.
HSTRINGhstring으로 설정Set HSTRING into hstring *put_abi(s) = h; s가 문자열의 소유권을 갖습니다.s takes ownership of string. s가 이전에 소유한 모든 문자열이 손실됩니다(디버그 시 어설션됨).Any string previously owned by s is leaked (will assert in debug).
HSTRINGhstring으로 수신Receive HSTRING into hstring GetString(reinterpret_cast<HSTRING*>(put_abi(s))); s가 문자열의 소유권을 갖습니다.s takes ownership of string. s가 이전에 소유한 모든 문자열이 손실됩니다(디버그 시 어설션됨).Any string previously owned by s is leaked (will assert in debug).
HSTRINGhstring에서 교체Replace HSTRING in hstring attach_abi(s, h); s가 문자열의 소유권을 갖습니다.s takes ownership of string. s가 이전에 소유한 문자열이 해제됩니다.The string previously owned by s is freed.
HSTRINGhstring에 복사Copy HSTRING to hstring copy_from_abi(s, h); s가 문자열의 개인 복사본을 만듭니다.s makes a private copy of the string. s가 이전에 소유한 문자열이 해제됩니다.The string previously owned by s is freed.
hstringHSTRING에 복사Copy hstring to HSTRING copy_to_abi(s, reinterpret_cast<void*&>(h)); h가 문자열의 복사본을 수신합니다.h receives a copy of the string. h가 이전에 소유한 모든 문자열이 손실됩니다.Any string previously owned by h is leaked.

중요 APIImportant APIs