Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Det här avsnittet visar hur du konverterar mellan SDK-program binärt gränssnitt (ABI) och C++/WinRT-objekt . Du kan använda dessa tekniker för att interop mellan kod som använder dessa två sätt att programmera med Windows Runtime, eller så kan du använda dem när du gradvis flyttar koden från ABI till C++/WinRT.
I allmänhet exponerar C++/WinRT ABI-typer som void*, så att du inte behöver inkludera plattformshuvudfiler.
Note
I kodexemplen använder vi reinterpret_cast (snarare än static_cast) för att signalera att det rör sig om castningar som i sig är osäkra.
Vad är Windows Runtime ABI och vad är ABI-typer?
En Windows Runtime-klass (runtimeklass) är egentligen en abstraktion. Den här abstraktionen definierar ett binärt gränssnitt (application binary interface eller ABI) som gör att olika programmeringsspråk kan interagera med ett objekt. Oavsett programmeringsspråk sker klientkodsinteraktion med ett Windows Runtime objekt på den lägsta nivån, med klientspråkkonstruktioner översatta till anrop till objektets ABI.
De Windows SDK-huvudena i mappen "%WindowsSdkDir%Include\10.0.17134.0\winrt" (justera SDK-versionsnumret för ditt fall om det behövs) är Windows Runtime ABI-huvudfiler. De producerades av MIDL-kompilatorn. Här är ett exempel på att inkludera en av dessa rubriker.
#include <windows.foundation.h>
Och här är ett förenklat exempel på en av de ABI-typer som du hittar i den specifika SDK-rubriken. Observera ABI-namnområdet; Windows::Foundation och alla andra Windows namnområden deklareras av SDK-huvudena i ABI-namnområdet.
namespace ABI::Windows::Foundation
{
IUriRuntimeClass : public IInspectable
{
public:
/* [propget] */ virtual HRESULT STDMETHODCALLTYPE get_AbsoluteUri(/* [retval, out] */__RPC__deref_out_opt HSTRING * value) = 0;
...
}
}
IUriRuntimeClass är ett COM-gränssnitt. Men mer än så – eftersom dess bas är IInspectable – är IUriRuntimeClass ett Windows Runtime gränssnitt. Observera HRESULT-returtypen i stället för att skapa undantag. Och användning av artefakter som HSTRING-handtaget (det är bra praxis att återställa handtaget till nullptr när du är klar med det). Detta ger en försmak av hur Windows Runtime ser ut på program binär nivå, med andra ord på COM-programmeringsnivå.
Windows Runtime baseras på API:er för komponentobjektmodell (COM). Du kan komma åt Windows Runtime på det sättet, eller så kan du komma åt den via språkprojektioner. En projektion döljer COM-informationen och ger en mer naturlig programmeringsupplevelse för ett visst språk.
Om du till exempel tittar i mappen "%WindowsSdkDir%Include\10.0.17134.0\cppwinrt\winrt" (återigen justera SDK-versionsnumret för ditt ärende, om det behövs), hittar du C++/WinRT-språkprojektionsrubrikerna. Det finns en rubrik för varje Windows namnområde, precis som det finns ett ABI-huvud per Windows namnområde. Här är ett exempel på att inkludera en av C++/WinRT-huvudena.
#include <winrt/Windows.Foundation.h>
Och från den headerfilen är här, i förenklad form, C++/WinRT-motsvarigheten till den ABI-typ som vi nyss såg.
namespace winrt::Windows::Foundation
{
struct Uri : IUriRuntimeClass, ...
{
winrt::hstring AbsoluteUri() const { ... }
...
};
}
Gränssnittet här är modernt, standard C++. Det gör sig av med HRESULT:er (C++/WinRT utlöser undantag om det behövs). Och accessor-funktionen returnerar ett enkelt strängobjekt som rensas i slutet av dess omfång.
Det här avsnittet handlar om situationer där du vill samverka med, eller portera, kod som fungerar på nivån för Application Binary Interface (ABI).
Konvertera till och från ABI-typer i kod
För säkerhet och enkelhet kan du för konverteringar i båda riktningarna helt enkelt använda winrt::com_ptr, com_ptr::as och winrt::Windows::Foundation::IUnknown::as. Här är ett kodexempel (baserat på projektmallen Konsolapp ), som också visar hur du kan använda namnområdesalias för de olika öarna för att hantera annars potentiella namnområdeskollisioner mellan C++/WinRT-projektionen och 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>() };
}
Implementeringarna av as-funktionerna anropar QueryInterface. Om du vill ha konverteringar på lägre nivå som bara anropar AddRef kan du använda hjälpfunktionerna winrt::copy_to_abi och winrt::copy_from_abi . I nästa kodexempel läggs dessa konverteringar på lägre nivå till kodexemplet ovan.
Viktigt!
När du samverkar med ABI-typer är det viktigt att den ABI-typ som används motsvarar standardgränssnittet för C++/WinRT-objektet. Annars kommer anrop av metoder på ABI-typen i praktiken att anropa metoder på samma plats i vtabellen i standardgränssnittet, med mycket oväntade resultat. Observera att winrt::copy_to_abi inte skyddar mot detta vid kompileringstillfället eftersom det använder void* för alla ABI-typer och förutsätter att anroparen har varit noga med att inte matcha typerna på fel sätt. Detta är för att undvika att kräva att C++/WinRT-huvuden refererar till ABI-huvuden när ABI-typer kanske aldrig används.
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;
}
Här är några andra konverteringstekniker på liknande låg nivå, men den här gången används råpekare till ABI-gränssnittstyper (de som definieras i Windows SDK-headerfilerna).
// 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();
För konverteringar på lägsta nivå, som endast kopierar adresser, kan du använda funktionerna winrt::get_abi, winrt::d etach_abi och winrt::attach_abi helper.
WINRT_ASSERT är en makrodefinition och expanderas till _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 funktion
Den här hjälpfunktionen konverterar en rå ABI-gränssnittspekare till ett motsvarande C++/WinRT-objekt med minimala omkostnader.
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;
}
Funktionen anropar bara QueryInterface för att fråga efter standardgränssnittet för den begärda C++/WinRT-typen.
Som vi har sett krävs ingen hjälpfunktion för att konvertera från ett C++/WinRT-objekt till motsvarande ABI-gränssnittspekare. Använd bara funktionen winrt::Windows::Foundation::IUnknown::as (eller try_as) för att fråga efter det begärda gränssnittet. Funktionerna as och try_asreturnerar ett winrt::com_ptr-objekt som omsluter den begärda ABI-typen.
Kodexempel med convert_from_abi
Här är ett kodexempel som visar den här hjälpfunktionen i praktiken.
// 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);
}
Samverka med ABI COM-gränssnittspekare
Hjälpfunktionsmallen nedan illustrerar hur du kopierar en ABI COM-gränssnittspekare av en viss typ till dess motsvarande C++/WinRT-projicerade smartpekartyp.
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)};
Nästa hjälpfunktionsmall är likvärdig, förutom att den kopierar från typen smart pekare från Windows Implementeringsbibliotek (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;
}
Mer information finns i Använda COM-komponenter med C++/WinRT.
Osäkert interop med ABI COM-gränssnittspekare
Tabellen nedan visar (utöver andra operationer) osäkra konverteringar mellan en COM-gränssnittspekare på ABI-nivå av en angiven typ och dess motsvarande projicerade smartpekartyp i C++/WinRT. Anta dessa deklarationer för koden i tabellen.
winrt::Sample s;
ISample* p;
void GetSample(_Out_ ISample** pp);
Anta vidare att ISample är standardgränssnittet för Exempel.
Du kan bekräfta det vid kompileringstillfället med den här koden.
static_assert(std::is_same_v<winrt::default_interface<winrt::Sample>, winrt::ISample>);
| Operation | Hur du gör det. | Notes |
|---|---|---|
| Extrahera ISample* från winrt::Sample | p = reinterpret_cast<ISample*>(get_abi(s)); |
s äger fortfarande objektet. |
| Frikoppla ISample* från winrt::Sample | p = reinterpret_cast<ISample*>(detach_abi(s)); |
s äger inte längre objektet. |
| Överför ISample* till ny winrt::Sample | winrt::Sample s{ p, winrt::take_ownership_from_abi }; |
s tar över ägarskapet för objektet. |
| Ange ISample* till winrt::Sample | *put_abi(s) = p; |
s tar ägarskapet för objektet. Alla objekt som tidigare ägdes av s har läckts (kommer att hävdas i felsökningen). |
| Ta emot ISample* i winrt::Sample | GetSample(reinterpret_cast<ISample**>(put_abi(s))); |
s tar ägarskapet för objektet. Alla objekt som tidigare ägdes av s har läckts (kommer att hävdas i felsökningen). |
| Ersätt ISample* i winrt::Sample | attach_abi(s, p); |
s tar ägarskapet för objektet. Objektet som tidigare ägdes av s frigörs . |
| Kopiera ISample* till winrt::Sample | copy_from_abi(s, p); |
s gör en ny referens till objektet. Objektet som tidigare ägdes av s frigörs . |
| Kopiera winrt::Exempel till ISample* | copy_to_abi(s, reinterpret_cast<void*&>(p)); |
p tar emot en kopia av objektet. Alla objekt som tidigare ägdes av p har läckt ut. |
Samverka med ABI:s GUID-struct
GUID (/previous-versions/aa373931(v%3Dvs.80)) beräknas som winrt::guid. För API:er som du implementerar måste du använda winrt::guid för GUID-parametrar. I annat fall sker automatiska konverteringar mellan winrt::guid och GUID så länge du inkluderar unknwn.h (ingår implicit av <windows.h> och många andra huvudfiler) innan du inkluderar C++/WinRT-huvuden.
Om du inte gör det kan du hård-reinterpret_castväxla mellan dem. För tabellen som följer antar du dessa deklarationer.
winrt::guid winrtguid;
GUID abiguid;
| Conversion | Med #include <unknwn.h> |
Utan #include <unknwn.h> |
|---|---|---|
| Från winrt::guid till GUID | abiguid = winrtguid; |
abiguid = reinterpret_cast<GUID&>(winrtguid); |
| Från GUID till winrt::guid | winrtguid = abiguid; |
winrtguid = reinterpret_cast<winrt::guid&>(abiguid); |
Du kan skapa en winrt::guid så här.
winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
En gist som visar hur du konstruerar en winrt::guid från en sträng finns i make_guid.cpp.
Interoperabilitet med ABI:s HSTRING
Tabellen nedan visar konverteringar mellan winrt::hstring och HSTRING och andra åtgärder. Anta dessa deklarationer för koden i tabellen.
winrt::hstring s;
HSTRING h;
void GetString(_Out_ HSTRING* value);
| Operation | Hur du gör det. | Notes |
|---|---|---|
| Extrahera HSTRING från hstring | h = reinterpret_cast<HSTRING>(get_abi(s)); |
s äger fortfarande strängen. |
| Koppla loss HSTRING från hstring | h = reinterpret_cast<HSTRING>(detach_abi(s)); |
s äger inte längre strängen. |
| Ange HSTRING i hstring | *put_abi(s) = h; |
s tar över ägarskapet för strängen. Varje sträng som tidigare ägdes av s läcker (utlöser en assert i debugläge). |
| Ta emot HSTRING till hstring | GetString(reinterpret_cast<HSTRING*>(put_abi(s))); |
s tar över ägarskapet för strängen. Varje sträng som tidigare ägdes av s läcker (utlöser en assert i debugläge). |
| Ersätt HSTRING i hstring | attach_abi(s, h); |
s tar över ägarskapet för strängen. Strängen som tidigare ägdes av s frigörs. |
| Kopiera HSTRING till hstring | copy_from_abi(s, h); |
s gör en privat kopia av strängen. Strängen som tidigare ägdes av s frigörs. |
| Kopiera hstring till HSTRING | copy_to_abi(s, reinterpret_cast<void*&>(h)); |
h tar emot en kopia av strängen. Alla strängar som tidigare ägdes av h har läckt ut. |
Dessutom utför stränghjälparna Windows Implementation Libraries (WIL) grundläggande strängmanipuleringar. Om du vill använda WIL-stränghjälparna inkluderar du <wil/resource.h> och läser tabellen nedan. Följ länkarna i tabellen för fullständig information.
| Operation | WIL-stränghjälp för mer information |
|---|---|
| Ange en rå Unicode- eller ANSI-strängpekare och en valfri längd; få en lämpligt anpassad unique_any-omslutning | wil::make_something_string |
| Packa upp ett smart objekt tills en rå pekare till en nullterminerad Unicode-sträng påträffas | wil::str_raw_ptr |
Hämta strängen omsluten av ett smart pekarobjekt. eller den tomma strängen L"" om den smarta pekaren är tom |
wil::string_get_not_null |
| Sammanfoga valfritt antal strängar | wil::str_concat |
| Hämta en sträng från en formatsträng i printf-format och en motsvarande parameterlista | wil::str_printf |
Viktiga API:er
- funktionen AddRef
- QueryInterface-funktion
- winrt::attach_abi funktion
- winrt::com_ptr struct-mall
- winrt::copy_from_abi funktion
- winrt::copy_to_abi funktion
- winrt::detach_abi-funktionen
- winrt::get_abi funktion
- winrt::Windows::Foundation::IUnknown::as medlemsfunktion
- winrt::Windows::Foundation::IUnknown::try_as medlemsfunktionen
Windows developer