Cette rubrique montre comment effectuer des conversions entre des objets de l’interface binaire d’application (ABI) du SDK et C++/WinRT. Vous pouvez utiliser ces techniques pour permettre l’interopérabilité entre le code qui utilise ces deux méthodes de programmation avec Windows Runtime, ou vous pouvez les utiliser à mesure que vous transférez votre code depuis ABI vers C++/WinRT.
En règle générale, C++/WinRT expose les types ABI en tant que void*, de sorte que vous n’avez pas besoin d’inclure des fichiers d’en-tête de plateforme.
Remarque
Dans les exemples de code, nous utilisons reinterpret_cast (plutôt que static_cast) dans le but de télégraphier les casts intrinsèquement dangereux.
Qu'est-ce que l’ABI Windows Runtime et quels sont les types ABI ?
Une classe Windows Runtime (classe runtime) est en réalité une abstraction. Cette abstraction définit une interface binaire (interface binaire d’application, ou ABI) qui permet à plusieurs langages de programmation d'interagir avec un objet. Quel que soit le langage de programmation, l'interaction du code client avec un objet Windows Runtime se produit au niveau le plus bas, avec des constructions de langage client traduites en appels dans ABI de l’objet.
Les en-têtes du SDK Windows dans le dossier « % WindowsSdkDir%Include\10.0.17134.0\winrt » (ajustez le numéro de version du SDK pour votre cas, si nécessaire), sont les fichiers d’en-tête ABI Windows Runtime. Ils ont été générés par le compilateur MIDL. Voici un exemple d’ajout d’un de ces en-têtes.
C++
#include<windows.foundation.h>
Et voici un exemple simplifié de l’un des types ABI que vous trouverez dans cet en-tête SDK particulier. Notez que l'espace de noms ABI, Windows::Foundation et tous les autres espaces de noms Windows, sont déclarés par les en-têtes SDK au sein de l'espace de noms ABI.
IUriRuntimeClass est une interface COM. Mais en plus, comme sa base est IInspectable, IUriRuntimeClass est une interface Windows Runtime. Notez le type de retour HRESULT, plutôt que le déclenchement d’exceptions. Et l’utilisation d’artefacts tels que le handle HSTRING (il est recommandé de redéfinir ce handle sur nullptr lorsque vous avez terminé avec celui-ci). Cela donne un aperçu de ce que à quoi Windows Runtime ressemble au niveau binaire de l’application ; en d’autres termes, au niveau de programmation COM.
Windows Runtime est basé sur les API COM (Component Object Model). Vous pouvez accéder à Windows Runtime de cette façon, ou vous pouvez y accéder par le biais de projections de langage. Une projection masque les détails COM et fournit une expérience de programmation plus naturelle pour un langage donné.
Par exemple, si vous recherchez dans le dossier « % WindowsSdkDir%Include\10.0.17134.0\cppwinrt\winrt » (là encore, ajustez le numéro de version du SDK pour votre cas, si nécessaire), vous y trouverez les en-têtes de projection de langage C++/WinRT. Il existe un en-tête pour chaque espace de noms Windows, tout comme il existe un en-tête ABI par espace de noms Windows. Voici un exemple d’ajout d’un de ces en-têtes C++/WinRT.
C++/WinRT
#include<winrt/Windows.Foundation.h>
Et, à partir de cet en-tête, voici l’équivalent C++/WinRT (simplifié) de ce type ABI que nous venons de voir.
L’interface ici est en C++ moderne standard. Elle rend superflus les HRESULT (C++/WinRT génère des exceptions si nécessaire). Et la fonction accesseur retourne un objet chaîne simple, qui est nettoyé à la fin de son étendue.
Cette rubrique est utile si vous souhaitez permettre l'interopérabilité avec, ou porter, du code qui fonctionne au niveau de la couche d'interface binaire d’application (ABI).
Conversion vers et depuis les types ABI dans le code
Par soucis de sécurité et de simplicité, pour les conversions bidirectionnelles vous pouvez simplement utiliser winrt::com_ptr, com_ptr::as et winrt::Windows::Foundation::IUnknown::as. Voici un exemple de code (basé sur le modèle de projet Console App), qui montre également comment vous pouvez utiliser des alias d’espace de noms pour les différents îlots pour éviter les collisions potentielles d’espace de noms entre la projection C++/WinRT et ABI.
C++/WinRT
// pch.h#pragma once#include<windows.foundation.h>#include<unknwn.h>#include"winrt/Windows.Foundation.h"// main.cpp#include"pch.h"namespace winrt
{
usingnamespace Windows::Foundation;
}
namespace abi
{
usingnamespace ABI::Windows::Foundation;
};
intmain(){
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>() };
}
Les implémentations des fonctions as appellent QueryInterface. Si vous souhaitez des conversions de niveau inférieur qui appellent uniquement AddRef, vous pouvez utiliser les fonctions d’assistance winrt::copy_to_abi et winrt::copy_from_abi. L’exemple de code suivant ajoute ces conversions de niveau inférieur à l’exemple de code ci-dessus.
Important
Dans le cas d’une interopérabilité avec les types ABI, il est essentiel que le type ABI utilisé corresponde à l’interface par défaut de l’objet C++/WinRT. Sinon, les appels de méthodes sur le type ABI finiront en fait par appeler des méthodes dans le même emplacement vtable de l’interface par défaut avec des résultats très inattendus. Notez que winrt::copy_to_abi n’empêche pas cela de se produire au moment de la compilation, car il utilise void* pour tous les types ABI et part du principe que l’appelant a veillé à ne pas dépareiller les types. Le but est ici d’éviter que les en-têtes C++/WinRT soient contraints de faire référence à des en-têtes ABI alors que les types ABI risquent de ne jamais être utilisés.
C++/WinRT
intmain(){
// 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;
}
Voici d’autres techniques de conversions de bas niveau similaires, mais cette fois en utilisant des pointeurs bruts vers les types d’interface ABI (ceux définis par les en-têtes du SDK Windows).
C++/WinRT
// 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_ASSERT est une définition de macro qui est développée en _ASSERTE.
C++/WinRT
// 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);
Fonction convert_from_abi
Cette fonction d’assistance convertit un pointeur d’interface ABI brut en un objet équivalent C++/WinRT, avec une surcharge minimale.
C++/WinRT
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;
}
La fonction appelle simplement QueryInterface pour rechercher l’interface par défaut du type C++/WinRT demandé.
Comme nous l’avons vu, une fonction d’assistance n’est pas nécessaire pour convertir à partir d’un objet C++/WinRT vers le pointeur d’interface ABI équivalent. Utilisez simplement la fonction de membre winrt::Windows::Foundation::IUnknown::as (ou try_as) pour rechercher l’interface demandée. Les fonctions as et try_as renvoient un objet winrt::com_ptr encapsulant le type ABI demandé.
Exemple de code utilisant convert_from_abi
Voici un exemple de code illustrant cette fonction d’assistance dans la pratique.
C++/WinRT
// pch.h#pragma once#include<windows.foundation.h>#include<unknwn.h>#include"winrt/Windows.Foundation.h"// main.cpp#include"pch.h"#include<iostream>usingnamespace winrt;
usingnamespace Windows::Foundation;
namespace winrt
{
usingnamespace Windows::Foundation;
}
namespace abi
{
usingnamespace 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;
}
inlineautoput_abi(winrt::hstring& object)noexcept{
returnreinterpret_cast<HSTRING*>(winrt::put_abi(object));
}
}
intmain(){
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);
}
Interopérabilité avec les pointeurs d’interface COM ABI
Le modèle de fonction helper ci-dessous montre comment copier un pointeur d’interface COM ABI d’un type donné en son type de pointeur intelligent projeté C++/WinRT équivalent.
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)};
Interopérabilité non sécurisée avec les pointeurs d’interface COM ABI
Le tableau qui suit montre (en plus d’autres opérations) des conversions non sécurisées entre un pointeur d’interface COM ABI d’un type donné et son type de pointeur intelligent projeté C++/WinRT équivalent. Pour le code dans le tableau, supposez ces déclarations.
s prend la propriété de l’objet. Tout objet précédemment propriété de s subit une fuite (déclaration dans le débogage).
Remplacer ISample* dans winrt::Sample
attach_abi(s, p);
s prend la propriété de l’objet. L’objet précédemment propriété de s est libéré.
Copier ISample* dans winrt::Sample
copy_from_abi(s, p);
s fait une nouvelle référence à l’objet. L’objet précédemment propriété de s est libéré.
Copier winrt::Sample dans ISample*
copy_to_abi(s, reinterpret_cast<void*&>(p));
p reçoit une copie de l’objet. Tout objet précédemment propriété de s subit une fuite.
Interopération avec le struct GUID d’ABI
GUID (/previous-versions/aa373931(v%3Dvs.80)) est projeté comme winrt::guid. Pour les API que vous implémentez, vous devez utiliser winrt::guid pour les paramètres GUID. Sinon, il existe des conversions automatiques entre winrt::guid et GUID dès lors que vous incluez unknwn.h (implicitement inclus par <windows.h> et de nombreux autres fichiers d’en-tête) avant d’inclure les en-têtes C++/WinRT.
Si vous ne faites pas ça, vous pouvez faire un reinterpret_cast en dur entre eux. Pour le tableau qui suit, supposez ces déclarations.
Pour obtenir un Gist qui montre comment construire un winrt::guid à partir d’une chaîne, consultez make_guid.cpp.
Interopération avec HSTRING d’ABI
Le tableau suivant montre des conversions entre winrt::hstring et HSTRING, et d’autres opérations. Pour le code dans le tableau, supposez ces déclarations.
s prend la propriété de la chaîne. Toute chaîne précédemment propriété de s subit une fuite (déclaration dans le débogage).
Remplacer HSTRING dans hstring
attach_abi(s, h);
s prend la propriété de la chaîne. La chaîne précédemment propriété de s est libérée.
Copier HSTRING dans hstring
copy_from_abi(s, h);
s fait une copie privée de la chaîne. La chaîne précédemment propriété de s est libérée.
Copier hstring dans HSTRING
copy_to_abi(s, reinterpret_cast<void*&>(h));
h reçoit une copie de la chaîne. Toute chaîne précédemment propriété de h est libérée.
En outre, les helpers de chaîne des bibliothèques d’implémentation Windows (WIL, Windows Implementation Libraries) effectuent des manipulations de chaîne de base. Pour utiliser les helpers de chaîne de WIL, incluez <wil/resource.h> et reportez-vous au tableau ci-dessous. Pour plus d’informations, suivez les liens dans le tableau.
Opération
Helper de chaîne WIL - pour plus d’informations
Fournir un pointeur de chaîne Unicode ou ANSI brut et une longueur facultative ; obtenir un wrapper unique_any spécialisé de façon appropriée
Azure HPC is een speciaal gebouwde cloudmogelijkheid voor HPC & AI-workload, met behulp van toonaangevende processors en HPC-klasse InfiniBand-interconnect, om de beste toepassingsprestaties, schaalbaarheid en waarde te leveren. Met Azure HPC kunnen gebruikers innovatie, productiviteit en bedrijfsflexibiliteit ontgrendelen via een maximaal beschikbare reeks HPC & AI-technologieën die dynamisch kunnen worden toegewezen wanneer uw bedrijf en technische behoeften veranderen. Dit leertraject is een reeks module