Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit onderwerp wordt beschreven hoe u converteert tussen binaire INTERFACE (ABI) voor SDK-toepassingen en C++/WinRT-objecten . U kunt deze technieken gebruiken om te interoppen tussen code die gebruikmaakt van deze twee manieren van programmeren met de Windows Runtime, of u kunt ze gebruiken wanneer u uw code geleidelijk van de ABI naar C++/WinRT verplaatst.
Over het algemeen maakt C++/WinRT ABI-typen beschikbaar als ongeldig*, zodat u geen platformheaderbestanden hoeft op te nemen.
Note
In de codevoorbeelden gebruiken we reinterpret_cast (in plaats van static_cast) om duidelijk te maken dat het om inherent onveilige typeconversies gaat.
Wat is de Windows Runtime ABI en wat zijn ABI-typen?
Een Windows Runtime-klasse (runtimeklasse) is echt een abstractie. Deze abstractie definieert een binaire interface (de binaire interface van de toepassing of ABI) waarmee verschillende programmeertalen kunnen communiceren met een object. Ongeacht de programmeertaal vindt interactie van clientcode met een Windows Runtime-object plaats op het laagste niveau, waarbij clienttaalconstructies worden omgezet in aanroepen naar de ABI van het object.
De Windows SDK-headers in de map%WindowsSdkDir%Include\10.0.17134.0\winrt" (pas zo nodig het SDK-versienummer voor uw case aan) zijn de Windows Runtime ABI-headerbestanden. Ze zijn geproduceerd door de MIDL-compiler. Hier volgt een voorbeeld van het opnemen van een van deze headers.
#include <windows.foundation.h>
Hier volgt een vereenvoudigd voorbeeld van een van de ABI-typen die u in die specifieke SDK-header vindt. Noteer de ABI-naamruimte; Windows::Foundation en alle andere Windows naamruimten worden gedeclareerd door de SDK-headers binnen de ABI-naamruimte.
namespace ABI::Windows::Foundation
{
IUriRuntimeClass : public IInspectable
{
public:
/* [propget] */ virtual HRESULT STDMETHODCALLTYPE get_AbsoluteUri(/* [retval, out] */__RPC__deref_out_opt HSTRING * value) = 0;
...
}
}
IUriRuntimeClass is een COM-interface. Maar meer dan dat, omdat de basis IInspectable is, is IUriRuntimeClass een Windows Runtime interface. Let op het retourtype HRESULT, in plaats van het genereren van uitzonderingen. En het gebruik van artefacten zoals de HSTRING-ingang (het is een goede gewoonte om die ingang weer in nullptr te stellen wanneer u er klaar mee bent). Dit geeft een idee van hoe de Windows Runtime eruitziet op binair niveau van de toepassing, met andere woorden, op com-programmeerniveau.
De Windows Runtime is gebaseerd op COM-API's (Component Object Model). U hebt op die manier toegang tot de Windows Runtime of u kunt deze openen via taalprojecties. Een projectie verbergt de COM-details en biedt een natuurlijkere programmeerervaring voor een bepaalde taal.
Als u bijvoorbeeld in de map '%WindowsSdkDir%Include\10.0.17134.0\cppwinrt\winrt' zoekt (pas indien nodig het SDK-versienummer voor uw case aan), dan vindt u de C++/WinRT-taalprojectieheaders. Er is een header voor elke Windows naamruimte, net zoals er één ABI-header per Windows naamruimte is. Hier volgt een voorbeeld van het opnemen van een van de C++/WinRT-headers.
#include <winrt/Windows.Foundation.h>
En vanuit die header is hier (vereenvoudigd) het C++/WinRT-equivalent van dat ABI-type dat we zojuist hebben gezien.
namespace winrt::Windows::Foundation
{
struct Uri : IUriRuntimeClass, ...
{
winrt::hstring AbsoluteUri() const { ... }
...
};
}
De interface hier is modern, standaard C++. Het sluit af met HRESULTs (C++/WinRT genereert indien nodig uitzonderingen). En de accessorfunctie retourneert een eenvoudig tekenreeksobject, dat aan het einde van het bereik wordt opgeschoond.
Dit onderwerp is bedoeld voor situaties waarin u wilt interopereren met of code wilt porteren die op het niveau van de Application Binary Interface (ABI) werkt.
Converteren van en naar ABI-typen in code
Voor veiligheid en eenvoud kunt u voor conversies in beide richtingen gewoon winrt::com_ptr, com_ptr::as en winrt::Windows::Foundation::IUnknown::as gebruiken. Hier volgt een codevoorbeeld (op basis van de console-app-projectsjabloon ), die ook laat zien hoe u naamruimtealiassen voor de verschillende eilanden kunt gebruiken om andere mogelijke naamruimteconflicten tussen de C++/WinRT-projectie en de ABI af te handelen.
// 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>() };
}
De implementaties van de as-functies roepen QueryInterface aan. Als u conversies op lager niveau wilt die alleen AddRef aanroepen, kunt u de helperfuncties winrt::copy_to_abi en winrt::copy_from_abi gebruiken. In dit volgende codevoorbeeld worden deze conversies op lager niveau toegevoegd aan het bovenstaande codevoorbeeld.
Belangrijk
Bij samenwerking met ABI-typen is het essentieel dat het gebruikte ABI-type overeenkomt met de standaardinterface van het C++/WinRT-object. Anders komen aanroepen van methoden op het ABI-type feitelijk uit bij het aanroepen van methoden in dezelfde vtable-slot van de standaardinterface, met zeer onverwachte resultaten. Houd er rekening mee dat winrt::copy_to_abi hier tijdens het compileren niet tegen beschermt, omdat het void* gebruikt voor alle ABI-typen en ervan uitgaat dat de aanroepende code zorgvuldig is geweest en de typen niet met elkaar verwisselt. Dit is om te voorkomen dat C++/WinRT-headers moeten verwijzen naar ABI-headers wanneer ABI-typen nooit kunnen worden gebruikt.
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;
}
Hier zijn andere vergelijkbare conversietechnieken op laag niveau, maar dit keer met gebruik van raw pointers naar ABI-interfacetypes (zoals gedefinieerd in de Windows SDK-headers).
// 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();
Voor de conversies op het laagste niveau, die alleen adressen kopiëren, kunt u de helperfuncties winrt::get_abi, winrt::d etach_abi en winrt::attach_abi gebruiken.
WINRT_ASSERT is een macrodefinitie en wordt uitgebreid naar _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{ reinterpret_cast<abi::IStringable*>(winrt::get_abi(uriAsIStringable)) };
WINRT_ASSERT(non_owning);
// Avoid interlocks this way.
owning = reinterpret_cast<abi::IStringable*>(winrt::detach_abi(uriAsIStringable));
WINRT_ASSERT(!uriAsIStringable);
winrt::attach_abi(uriAsIStringable, owning);
WINRT_ASSERT(uriAsIStringable);
convert_from_abi, functie
Met deze helperfunctie wordt een onbewerkte ABI-interfaceaanwijzer geconverteerd naar een equivalent C++/WinRT-object, met minimale 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;
}
De functie roept QueryInterface aan om een query uit te voeren op de standaardinterface van het aangevraagde C++/WinRT-type.
Zoals we hebben gezien, is een helperfunctie niet vereist om te converteren van een C++/WinRT-object naar de equivalente ABI-interfacepointer. Gebruik gewoon de lidfunctie winrt::Windows::Foundation::IUnknown::as (of try_as) om een query uit te voeren op de aangevraagde interface. De functies as en try_as retourneren een winrt::com_ptr object dat het aangevraagde ABI-type verpakt.
Codevoorbeeld met convert_from_abi
Hier volgt een codevoorbeeld met deze helperfunctie in de praktijk.
// 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);
}
Samenwerken met COM-interfacepointers van ABI
In het onderstaande sjabloon voor de helperfunctie wordt geïllustreerd hoe u een ABI COM-interfaceaanwijzer van een bepaald type kopieert naar het overeenkomstige slimme aanwijzertype van C++/WinRT.
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)};
Deze volgende helperfunctiesjabloon is gelijkwaardig, behalve dat deze wordt gekopieerd van het type slimme aanwijzer uit de Windows Implementatiebibliotheken (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;
}
Zie Ook COM-onderdelen gebruiken met C++/WinRT.
Onveilige interoperabiliteit met ABI COM-interfacepointers
De volgende tabel toont (naast andere bewerkingen) onveilige conversies tussen een ABI COM-interfacepointer van een bepaald type en het overeenkomstige geprojecteerde smartpointertype van C++/WinRT. Ga voor de code in de tabel uit van de volgende declaraties.
winrt::Sample s;
ISample* p;
void GetSample(_Out_ ISample** pp);
Stel verder dat ISample de standaardinterface is voor Sample.
U kunt bevestigen dat tijdens het compileren met deze code.
static_assert(std::is_same_v<winrt::default_interface<winrt::Sample>, winrt::ISample>);
| Operation | Hoe het te doen | Notes |
|---|---|---|
| ISample* extraheren uit winrt::Sample | p = reinterpret_cast<ISample*>(get_abi(s)); |
s is nog steeds eigenaar van het object. |
| ISample loskoppelen* van winrt::Sample | p = reinterpret_cast<ISample*>(detach_abi(s)); |
s is niet langer eigenaar van het object. |
| ISample* overdragen naar nieuwe winrt::Sample | winrt::Sample s{ p, winrt::take_ownership_from_abi }; |
s is eigenaar van het object. |
| ISample* instellen in winrt::Sample | *put_abi(s) = p; |
s is eigenaar van het object. Elk object dat eerder eigendom is van s , wordt gelekt (zal bevestigen in foutopsporing). |
| Ontvang ISample* als winrt::Sample | GetSample(reinterpret_cast<ISample**>(put_abi(s))); |
s is eigenaar van het object. Elk object dat eerder eigendom is van s , wordt gelekt (zal bevestigen in foutopsporing). |
| ISample* vervangen in winrt::Sample | attach_abi(s, p); |
s is eigenaar van het object. Het object dat eerder eigendom is van s , wordt vrijgemaakt. |
| ISample* kopiëren naar winrt::Sample | copy_from_abi(s, p); |
s maakt een nieuwe verwijzing naar het object. Het object dat eerder eigendom is van s , wordt vrijgemaakt. |
| Winrt::Sample naar ISample* kopiëren | copy_to_abi(s, reinterpret_cast<void*&>(p)); |
p ontvangt een kopie van het object. Elk object dat eerder eigendom is van p , wordt gelekt. |
Samenwerken met de GUID-struct van de ABI
GUID (/previous-versions/aa373931(v%3Dvs.80)) wordt geprojecteerd als winrt::guid. Voor API's die u implementeert, moet u winrt::guid gebruiken voor GUID-parameters. Anders zijn er automatische conversies tussen winrt::guid en GUID zolang u opneemt unknwn.h (impliciet opgenomen door <windows.h> en vele andere headerbestanden) voordat u C++/WinRT-headers opneemt.
Als je dat niet doet, kun je er hardreinterpret_cast tussen zitten. Ga voor de onderstaande tabel uit van de volgende declaraties.
winrt::guid winrtguid;
GUID abiguid;
| Conversion | Met #include <unknwn.h> |
Zonder #include <unknwn.h> |
|---|---|---|
| Van winrt::guid naar GUID | abiguid = winrtguid; |
abiguid = reinterpret_cast<GUID&>(winrtguid); |
| Van GUID naar winrt::guid | winrtguid = abiguid; |
winrtguid = reinterpret_cast<winrt::guid&>(abiguid); |
U kunt als volgt een winrt::guid maken.
winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
Zie make_guid.cpp voor een voorbeeld waarin wordt getoond hoe u een winrt::guid construeert uit een tekenreeks.
Samenwerken met de HSTRING van de ABI
De volgende tabel bevat conversies tussen winrt::hstring en HSTRING en andere bewerkingen. Ga voor de code in de tabel uit van de volgende declaraties.
winrt::hstring s;
HSTRING h;
void GetString(_Out_ HSTRING* value);
| Operation | Hoe het te doen | Notes |
|---|---|---|
| HSTRING extraheren uit hstring | h = reinterpret_cast<HSTRING>(get_abi(s)); |
s is nog steeds eigenaar van de tekenreeks. |
| HSTRING loskoppelen van hstring | h = reinterpret_cast<HSTRING>(detach_abi(s)); |
s is niet langer eigenaar van de tekenreeks. |
| HSTRING instellen in hstring | *put_abi(s) = h; |
s neemt het eigenaarschap van de tekenreeks over. Elke tekenreeks die eerder in het bezit was van s, lekt (zal een assertie veroorzaken in debugmodus). |
| HSTRING ontvangen in hstring | GetString(reinterpret_cast<HSTRING*>(put_abi(s))); |
s neemt het eigenaarschap van de tekenreeks over. Elke tekenreeks die eerder eigendom is van s , wordt gelekt (wordt in foutopsporing weergegeven). |
| HSTRING vervangen in hstring | attach_abi(s, h); |
s neemt het eigenaarschap van de tekenreeks over. De tekenreeks die eerder in bezit was van s, wordt vrijgegeven. |
| HSTRING kopiëren naar hstring | copy_from_abi(s, h); |
s maakt een eigen kopie van de tekenreeks. De tekenreeks die eerder eigendom is van s , wordt vrijgemaakt. |
| HSTRING kopiëren naar HSTRING | copy_to_abi(s, reinterpret_cast<void*&>(h)); |
h ontvangt een kopie van de tekenreeks. Elke tekenreeks die eerder in bezit was van h, lekt. |
Daarnaast voeren de Windows wil-tekenreekshulpen (Implementation Libraries) eenvoudige tekenreeksbewerkingen uit. Als u de WIL-stringhelpers wilt gebruiken, neemt u <wil/resource.h> op en raadpleegt u de onderstaande tabel. Volg de koppelingen in de tabel voor volledige details.
| Operation | WIL-tekenreekshulp voor meer informatie |
|---|---|
| Geef een onbewerkte Unicode- of ANSI-tekenreeksaanwijzer en een optionele lengte op; een geschikte unique_any wrapper verkrijgen | wil::make_something_string |
| Een smartobject uitvouwen totdat een onbewerkte pointer naar een null-beëindigde Unicode-tekenreeks is gevonden | wil::str_raw_ptr |
Haal de tekenreeks op die is verpakt door een slim aanwijzerobject; of de lege tekenreeks L"" als de slimme aanwijzer leeg is |
wil::string_get_not_null |
| Een willekeurig aantal tekenreeksen samenvoegen | wil::str_concat |
| Een tekenreeks ophalen op basis van een printf-opmaakreeks en de bijbehorende parameterlijst | wil::str_printf |
Belangrijke API's
- AddRef-functie
- QueryInterface-functie
- functie winrt::attach_abi
- winrt::com_ptr struct-sjabloon
- winrt::copy_from_abi functie
- functie winrt::copy_to_abi
- functie winrt::detach_abi
- functie winrt::get_abi
- winrt::Windows::Foundation::IUnknown::as member function
- winrt::Windows::Foundation::IUnknown::try_as memberfunctie
Windows developer