WinRT est COM — IInspectable, .winmd, projections de langage, et pourquoi WinUI repose encore sur un contrat binaire

· Mis à jour le: · · Windows, WinRT, COM, WinUI, Windows App SDK, Développement Windows

Historique des révisions (première version, publiée le 29 Aug 2026)
Première publication

Vous voulez ajouter une nouvelle capacité Windows à une application WPF ou WinForms. Mais appeler un sélecteur WinRT lève une exception. Puis vous lisez WinUI, et il semble qu’il faille reconstruire l’interface. Cette confusion se dissipe une fois que vous séparez le fonctionnement de WinRT du choix du framework d’interface.

Le point de départ de cet article est que WinRT, lui aussi, repose sur le contrat binaire COM. Microsoft lui-même affirme clairement que « The Windows Runtime is based on COM ».1 Le tableau Excel intégré dans Word vu dans l’article précédent sur les objets OLE, et WinRT et WinUI d’aujourd’hui, ont le même IUnknown à la racine.

Ici, nous fixons d’abord les trois éléments qui composent WinRT, puis nous regardons ce qu’il faut surveiller pour l’utiliser depuis une application de bureau, et enfin comment traiter les actifs existants. Les lecteurs visés sont les développeurs ayant une expérience de COM ou du développement de bureau Windows, les prérequis sont Windows 10/11 et .NET 6 ou ultérieur (C#) ou C++17 (C++/WinRT), et le niveau de difficulté est intermédiaire.

1. D’abord la conclusion

Il y a trois conclusions à retenir.

  • WinRT n’est pas un runtime managé ; c’est un ABI (un contrat binaire) construit sur COM. S’ajoutent à ce contrat .winmd, qui porte les informations de type, et les projections de langage, qui permettent à chaque langage d’appeler naturellement.1234
  • La plupart des API WinRT peuvent s’utiliser depuis des applications WPF, WinForms et Win32 existantes. Mais trois prérequis doivent être vérifiés : le passage d’un HWND, l’identité de package et l’initialisation du thread.5678
  • Une migration d’interface complète vers WinUI et l’usage sélectif des API WinRT sont des décisions distinctes. WinUI aussi repose sur l’ABI WinRT, et les actifs COM/ActiveX existants et WinRT peuvent coexister sur le même fondement.921

À partir d’ici, lire dans l’ordre suivant rend les liens plus faciles à suivre.

Ce que vous voulez savoir Où lire
Pourquoi peut-on dire « WinRT est COM » ? Sections 2–3 : ce qu’il partage avec COM, et IInspectable
Pourquoi C# et C++ peuvent-ils l’appeler si naturellement ? Sections 4–5 : .winmd et les projections de langage
Que faut-il vérifier pour l’utiliser depuis une application existante ? Section 6 : HWND, identité de package, apartments
Faut-il migrer vers WinUI ? Sections 7–9 : ce qui porte WinUI, la décision de migration, les conditions d’inscription des notifications

La carte des connaissances ci-dessous sert à revoir comment les éléments se relient. Si vous préférez lire d’abord l’explication, poursuivez dans l’ordre à partir de la section 2.

Dans le diagramme, un trait continu marque une relation qui vaut toujours et un trait pointillé une relation conditionnelle (les conditions figurent dans l’explication de chaque relation sur la page de détail). La liste complète des relations (20 au total, avec preuve et niveau de certitude) et les définitions des concepts principaux sont rassemblées sur la page de détail de la carte des connaissances (en japonais). Données : JSON-LD / Turtle

2. OLE et WinRT partagent à la racine le même contrat binaire

Sur ce blog, nous avons jusqu’ici suivi le monde de COM classique : la philosophie de conception de COM, le modèle de threads STA/MTA, le traitement d’ActiveX/OCX et les documents composés OLE. Toutes ces technologies datent des années 1990.

WinRT, de son côté, est le fondement d’API introduit avec Windows 8 (2012). Aujourd’hui, il fournit les notifications toast, le Partage, Bluetooth, l’OCR et d’autres encore comme les API des espaces de noms Windows.*, et c’est aussi ce sur quoi reposent WinUI et le Windows App SDK.10 L’ancien et le nouveau semblent des mondes entièrement séparés, mais dans le fond ils sont continus.

Ce qu’ils partagent, c’est la promesse d’appeler à travers des interfaces

Les composants COM et les classes WinRT exposent tous deux leur fonctionnalité à travers des interfaces. En comparant les interfaces de base, la relation se présente ainsi.1

COM classique WinRT
La base de chaque interface est IUnknown La base de chaque interface est IInspectable, dont la base est IUnknown

Autrement dit, WinRT n’a pas remplacé COM par un mécanisme sans rapport ; c’est une couche de plus empilée sur IUnknown. Le comptage de références, QueryInterface et HRESULT survivent tels quels.

La documentation de C++/WinRT appelle elle aussi les API WinRT « an evolution of COM » et explique qu’elles sont conçues pour être consommées, en tant qu’API fondées sur COM, à travers des projections de langage.211

La lignée de COM classique et de WinRTSur le fondement commun du contrat binaire COM fait d'IUnknown et de vtables se tiennent, côte à côte, le monde COM classique d'OLE et d'ActiveX des années 1990 et le monde de WinRT à partir de 2012, avec WinUI et le Windows App SDK au-dessus ; les deux ne s'excluent pas, ils sont continusContrat binaire COM (IUnknown, vtable)COM classique (OLE, ActiveX, COM maison)WinRT (IInspectable, .winmd)WinUI / Windows App SDKPeuvent coexister sur le même fondement

Figure 1 : COM classique et WinRT ne sont pas des mondes séparés, mais deux générations, ancienne et nouvelle, sur le même contrat binaire.

C’est pourquoi cet article n’est pas seulement « une introduction à une nouvelle API ». C’est un article qui confirme où les connaissances COM que vous avez déjà s’appliquent dans le développement Windows de 2026.

3. IInspectable au-dessus d’IUnknown

Le fondement inchangé et les trois méthodes ajoutées

La spécification officielle du système de types WinRT stipule que chaque interface WinRT exige implicitement IInspectable, et que IInspectable exige IUnknown. Ce que IUnknown définit, ce sont, comme toujours, les trois méthodes QueryInterface, AddRef et Release.12

Par-dessus, IInspectable ajoute les trois méthodes suivantes.13

Méthode Rôle
GetIids Renvoie la liste des IID des interfaces que cet objet implémente
GetRuntimeClassName Renvoie le nom de type WinRT pleinement qualifié (tel que Windows.Storage.StorageFile) sous forme d’HSTRING
GetTrustLevel Renvoie le niveau de confiance de l’objet
IInspectable reposant sur IUnknownChaque interface WinRT exige IInspectable, et IInspectable exige IUnknown. IUnknown fournit QueryInterface, AddRef et Release ; IInspectable fournit GetIids, GetRuntimeClassName et GetTrustLevel ; et les méthodes de chaque interface WinRT s'empilent dessusIUnknown (QI, AddRef, Release)IInspectable (GetIids, nom de type, niveau de confiance)Méthodes de chaque interface WinRT

Figure 2 : Un objet WinRT empile les trois méthodes d’IInspectable sur les trois méthodes d’IUnknown, et les API individuelles s’empilent dessus.

À partir d’un nom de type, on retrouve les définitions des méthodes, propriétés et événements

Ce qui compte n’est pas le nombre de méthodes ajoutées, mais la capacité de relier un nom de type aux métadonnées.

En COM classique, le moyen standard de connaître l’identité d’un objet à l’exécution était « connaître l’IID et demander via QueryInterface ». Pour les langages de script, il existait un chemin distinct, IDispatch.

En WinRT, le nom de type obtenu de GetRuntimeClassName peut se résoudre dans les métadonnées .winmd de la section suivante. De là, vous obtenez la définition complète de ses méthodes, propriétés et événements. La spécification elle-même dit que pouvoir obtenir un nom de type WinRT résoluble via les métadonnées « enables language projection ».12

De GetRuntimeClassName à la projection de langageQuand l'appelant invoque GetRuntimeClassName sur un objet, le nom de type WinRT pleinement qualifié revient ; résoudre ce nom de type dans Windows Metadata donne la définition complète du type, et c'est ce qui rend possible la projection vers chaque langageObjet WinRTNom de type (GetRuntimeClassName)Résoudre la définition de type dans .winmdLa projection de langage devient possible

Figure 3 : « Le nom de type est disponible à l’exécution, et le nom de type mène aux métadonnées » est le cœur du mécanisme de WinRT.

Séparer « héritage » et « requires » pour les interfaces définies par l’utilisateur

Il y a aussi une différence que les personnes habituées à COM doivent noter. Le système de types WinRT n’a pas d’héritage entre interfaces définies par l’utilisateur. Une dérivation telle que IFileSystemBindData2 : IFileSystemBindData de COM classique est délibérément absente ; elle s’exprime à la place par la déclaration « l’interface A exige l’interface B (requires) ».121

C’est une affaire distincte de la chaîne ABI de base IUnknownIInspectable vue jusqu’ici. Cette base demeure le fondement de chaque interface WinRT.

Les contrats définis par l’utilisateur ont été déplacés vers une forme plus lâche, qui ne dépend pas de la disposition d’héritage de la vtable. En même temps, les appels eux-mêmes passent toujours par la vtable. Il importe de ne pas confondre la façon d’écrire les contrats et le mécanisme de l’appel.

4. Ce que .winmd a résolu — l’enfer de liaison des informations de type

La difficulté de COM classique tenait à « comment distribuer les informations de type »

Dans la pratique de COM classique, plus d’effort allait à la façon de distribuer les informations de type qu’à l’implémentation des interfaces elles-mêmes.

Consommateur Chemin pour livrer les informations de type
C++ Écrire le contrat en IDL, générer en-têtes et proxy/stub avec MIDL
VB6, scripts Distribuer une bibliothèque de types (TLB)
.NET Construire un assembly d’interopération distinct

Une TLB a des restrictions de types orientées automation : certaines informations que l’on peut écrire en IDL ne tiennent pas dans une TLB. Et comme chaque langage a son propre chemin, dès que l’un d’eux vieillit, on se retrouve avec des décalages de types. Nous avons traité ces difficultés dans l’article sur les bibliothèques de types et dscom et l’article sur la compatibilité descendante des interfaces DLL et COM.

.winmd est le contrat commun que chaque langage lit

La réponse de WinRT est Windows Metadata (.winmd). Les API sont décrites comme des métadonnées lisibles par machine, et les outils et projections de langage les lisent pour générer une projection pour chaque langage.3

Windows livre les métadonnées de chaque API WinRT fournie par le système et fournit aussi des API qui résolvent les espaces de noms et les types à l’exécution. Le Windows SDK contient une copie pour le temps de compilation. Les tiers aussi peuvent participer à la projection de langage par le même mécanisme que les API système, en joignant un .winmd à leur propre composant WinRT.3

Ce qui a changé ici, c’est la forme sous laquelle les informations de type sont distribuées. À la place de TLB, d’en-têtes et d’assemblys d’interopération dispersés par langage, un seul .winmd est désormais lu par les projections de chaque langage.

Cela dit, IDL n’est pas devenu inutile. Lorsque vous créez un composant WinRT, vous décrivez encore le contrat en IDL (MIDL 3.0, modernisé pour WinRT), et le compilateur MIDL génère le .winmd.14

Le pipeline de production d'un composant WinRTLe contrat d'un composant WinRT s'écrit encore en IDL, c'est-à-dire MIDL 3.0, et le compilateur MIDL le compile en .winmd. Ce qui est distribué, c'est ce .winmd, et l'outil de projection de chaque langage, tel que cppwinrt.exe ou cswinrt.exe, le lit pour générer la projection. Ce qui a été remplacé n'est pas IDL, mais la forme sous laquelle les informations de type sont distribuéesÉcrire le contrat (IDL, MIDL 3.0)Compilateur MIDL.winmd (informations de type distribuées)Générer la projection de chaque langage

Figure 4 : Le point d’entrée du contrat (IDL) reste en service ; le point de sortie, les informations de type distribuées, a été unifié en .winmd.

Le même format de fichier que .NET, mais pas un runtime managé

Le format physique d’un .winmd utilise la spécification ECMA-335, la même qu’un assembly CLR. Les règles sur les combinaisons de données valides diffèrent toutefois de celles des assemblys CLR. Emprunter le format et avoir besoin du CLR pour s’exécuter sont deux choses distinctes.3

Ici, il faut lire séparément les API système et les composants tiers.

Sujet Relation entre le .winmd et l’implémentation
API WinRT fournies par le système Le .winmd est de pures métadonnées, sans code exécutable. L’implémentation vit dans des DLL natives de l’OS, et le CLR n’est pas nécessaire pour l’exécuter
Composants WinRT tiers Le .winmd peut aussi contenir du code d’implémentation. Pour un composant managé (écrit en C#) qui contient du MSIL, le runtime .NET correspondant est requis pour l’exécuter

Parce qu’un .winmd ressemble à un assembly .NET lorsqu’on l’ouvre dans un outil, il est facile de tomber dans l’idée fausse « WinRT = managé ». Mais le contenu d’un .winmd fourni par le système est un contrat pour des interfaces COM.3

Séparation du .winmd et de l'implémentationUn .winmd est un fichier de métadonnées qui emprunte le format physique ECMA-335 ; ceux fournis par le système sont des contrats sans code exécutable, et l'implémentation des API WinRT fournies par le système vit dans des DLL natives de l'OS. Grâce à cette séparation, même si un .winmd ressemble à un assembly .NET, le CLR n'est pas nécessaire pour exécuter les API WinRT système (le .winmd d'un composant managé tiers contient du MSIL et exige le runtime .NET)Fourni par le système : pas de codeLes définitions de type correspondent à l'implémentation.winmd (contrat, format ECMA-335)DLL native de l'OS (implémentation)Les API système n'ont pas besoin du CLR

Figure 5 : Pour les API WinRT fournies par le système, le .winmd est le contrat et l’implémentation une DLL native de l’OS. Le format ressemble à .NET, mais l’exécution est du COM natif.

Informations de type de COM classique face à .winmdEn COM classique, le chemin des informations de type était scindé par langage, de l'IDL vers les en-têtes C++, des bibliothèques de types vers VB6 et les scripts, des assemblys d'interopération vers .NET, ce qui causait des incohérences, alors qu'en WinRT un seul .winmd est lu en commun par les projections de chaque langageCOM classique : un chemin par langageIDL vers en-têtes C++TLB vers VB6 et scriptsAssembly d'interopération vers .NET.winmd (métadonnées uniques)Lu en commun par la projection de chaque langage

Figure 6 : .winmd a replié la dispersion des informations de type par langage en « un fichier de métadonnées que tout le monde lit ».

Les contraintes n’ont pas disparu ; elles se sont déplacées vers un axe de projectabilité

Pour qui connaît COM classique, en une phrase : .winmd est « la bibliothèque de types, refaite ». Voyez-le comme le rôle qu’une TLB tentait de jouer, redessiné dès le départ comme source de vérité unique partagée par tous les langages, sur le format éprouvé ECMA-335, et sa place devient claire.

Les contraintes n’ont toutefois pas disparu. Les contraintes orientées automation des TLB ont été remplacées par les contraintes du système de types propre à WinRT, dont l’axe est « pouvoir se projeter en toute sécurité dans chaque langage ». L’absence d’héritage entre interfaces définies par l’utilisateur, vue à la section 3, en est un exemple.12

Par conséquent, un contrat COM/IDL existant ne peut pas nécessairement se porter tel quel dans WinRT. L’API peut devoir être redessinée.

Remplacement des contraintes de bibliothèque de types par celles du système de types WinRTLes contraintes d'expressivité orientées automation de la TLB (bibliothèque de types) n'ont pas été retirées par .winmd, mais remplacées par les contraintes du système de types propre à WinRT, dont l'axe est la projection sûre vers chaque langage. Un exemple est l'absence d'héritage d'interfaces définies par l'utilisateur ; un contrat COM/IDL existant ne peut pas nécessairement se reprendre tel quel, et l'API peut devoir être redessinéeRemplacées parContraintes de TLB (orientées automation)Contraintes du système de types WinRT (axe projectabilité)Exemple : pas d'héritage défini par l'utilisateurLes contrats COM existants peuvent devoir être redessinés

Figure 7 : Les contraintes de TLB n’ont pas « disparu » ; elles ont été remplacées par d’autres contraintes dont l’axe est la projectabilité vers chaque langage.

5. C++/WinRT et C#/WinRT sont des projections, pas des « enveloppes »

Montrer le contrat commun à chaque langage sous sa forme naturelle

Une fois qu’il existe un contrat commun sous forme de .winmd, la vue pour chaque langage peut se générer automatiquement par des outils. C’est une projection de langage (language projection). Elle expose les API WinRT dans l’idiome de chaque langage et cache les détails de COM, offrant une expérience de programmation naturelle pour ce langage.411

Les projections que Microsoft prend actuellement en charge sont les deux suivantes.4

Projection Ce qu’elle génère Caractéristiques
C++/WinRT cppwinrt.exe génère des en-têtes de projection C++ à partir de .winmd Une projection fondée sur des fichiers d’en-tête en C++17 standard. Aucune extension de langage comme celles de C++/CX n’est nécessaire ; le successeur de C++/CX et de WRL11
C#/WinRT (CsWinRT) cswinrt.exe génère du code C# à partir de .winmd et en fait un assembly d’interopération La projection pour .NET. Une chaîne d’outils indépendante du runtime1516

L’histoire côté C# est un peu confuse, démêlons-la. Jusqu’à .NET Core 3.x, le runtime .NET avait une prise en charge intégrée de la consommation de WinRT/winmd. Dans .NET 5, cette prise en charge intégrée a été retirée, et le rôle est passé à C#/WinRT.16 Les API WinRT n’ont pas cessé d’être utilisables ; c’est l’endroit où la projection est traitée qui a changé.

Aujourd’hui, lorsque vous indiquez en C# un TFM tel que net8.0-windows10.0.19041.0, les assemblys de projection du Windows SDK sont référencés automatiquement.10

Génération des projections pour chaque langage à partir de .winmdQuand cppwinrt.exe lit le .winmd unique, il génère des en-têtes de projection C++17 ; quand cswinrt.exe le lit, il génère un assembly d'interopération C# ; et les API WinRT sont exposées sous une forme qui suit l'idiome de chaque langage.winmd (le contrat d'API)cppwinrt.exe vers en-têtes C++17cswinrt.exe vers assembly d'interopération C#Appelable dans l'idiome C++Appelable dans l'idiome C#

Figure 8 : Une projection n’est pas une enveloppe écrite à la main ; les outils la génèrent mécaniquement à partir du contrat (.winmd).

Même généré, l’appel lui-même reste COM

Cet article dit « projection » plutôt qu’« enveloppe » pour souligner qu’il s’agit d’un mécanisme dérivé mécaniquement des métadonnées, et non d’une couche de traduction que des personnes maintiennent API par API. Toute API présente dans le .winmd peut être rendue utilisable dès le départ dans chaque langage pris en charge.

Et quel que soit le langage depuis lequel vous appelez, ce qui se passe sous la projection est le même appel COM. Par exemple, lorsque vous écrivez await picker.PickSingleFolderAsync() en C#, la projection relie l’IAsyncOperation de WinRT au monde du Task de .NET. Malgré cela, côté ABI, ce sont des appels de méthode via vtable et HRESULT qui sont utilisés. C’est pourquoi les erreurs apparaissent sous forme d’exceptions COM (un HRESULT tel que 0x80070005).

Les couches du code C# à l'API WinRT de l'OSLe code C# ou C++ de l'application est converti, via la projection de langage, en appels ABI WinRT, c'est-à-dire des appels de vtable sur IInspectable, et atteint l'API WinRT implémentée par l'OS. La projection ne fait que cacher les détails de COM ; l'appel lui-même est COMCode de l'application (C#, C++)Projection de langageABI WinRT (vtable IInspectable)Implémentation OS de l'API WinRT

Figure 9 : Ce que la projection cache, ce sont les « détails » de COM, pas COM lui-même.

Connaître cette structure permet de scinder un incident en deux couches. Un décalage de version du code généré ou un TFM manquant est la couche projection ; les HRESULT, les apartments et le comptage de références sont la couche ABI. Dans cette dernière, l’expérience de développement COM sert directement.

6. Où les applications de bureau coincident — HWND, identité, apartments

D’abord séparer « pouvoir référencer l’API » et « les conditions pour qu’elle fonctionne »

La plupart des API WinRT peuvent s’appeler depuis des applications de bureau WPF, WinForms et Win32.5 Le point d’entrée pour les appeler diffère entre C# et C++ comme suit.10

Environnement Première configuration
C#/.NET 6 ou ultérieur Régler TargetFramework sur un TFM portant une version d’OS Windows, tel que net8.0-windows10.0.19041.0
C++ Ajouter le package NuGet Microsoft.Windows.CppWinRT et utiliser C++/WinRT avec C++17 ou une version ultérieure

Pouvoir référencer les API ne signifie toutefois pas que chaque API fonctionne telle quelle. Vérifiez les trois points suivants. Les conditions d’inscription des notifications toast sont traitées séparément à la section 9.

Ce qu’il faut vérifier Remède principal
A-t-elle besoin d’une fenêtre pour s’afficher ? Passer un HWND via l’interop COM qui correspond à l’interface
A-t-elle besoin d’une identité de package ? Accorder une identité avec MSIX ou un package à emplacement externe
Le thread est-il initialisé pour WinRT ? En code natif, initialiser en indiquant STA/MTA. En C#, le runtime s’en charge en général

Point de blocage 1 : passer un HWND à une interface telle que les sélecteurs

Certains sélecteurs, boîtes de dialogue et l’interface de partage s’attendent à un CoreWindow UWP comme surface d’affichage. Une application de bureau n’a pas de CoreWindow, donc le HWND de la fenêtre propriétaire doit être passé explicitement avant l’affichage de l’objet.6

Le point d’entrée utilisé pour les sélecteurs et assimilés est une interface COM appelée IInitializeWithWindow. Elle hérite de IUnknown et fournit une fenêtre propriétaire aux objets WinRT utilisés dans les applications de bureau.17

En C#, obtenez d’abord le HWND selon le framework d’interface en usage.186

Fenêtre propriétaire Comment obtenir le HWND
Une Window WinUI WinRT.Interop.WindowNative.GetWindowHandle
Une fenêtre WPF WindowInteropHelper
Un formulaire WinForms La propriété Handle du formulaire

Ensuite, passez-le au sélecteur avec WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd), et seulement alors affichez-le. En C++/WinRT, obtenez l’objet comme as<IInitializeWithWindow>() puis appelez Initialize(hwnd). Si vous sautez l’initialisation, cela lève une exception ou échoue silencieusement.1819

Initialiser un objet WinRT moderne par QueryInterface vers une interface COM classique : le fait que ce pont soit la pratique officielle est un autre endroit où WinRT montre qu’il est COM.

L’interface de partage utilise une autre interface. Pour DataTransferManager, vous n’utilisez pas IInitializeWithWindow. Vous utilisez l’IDataTransferManagerInterop dédié et passez le HWND à ShowShareUIForWindow.6

Les sélecteurs plus récents du Windows App SDK sont un autre chemin distinct. Microsoft.Windows.Storage.Pickers prend un WindowId dans le constructeur, donc le schéma InitializeWithWindow est inutile. Ce ne sont toutefois pas des API utilisables avec un réglage de TFM seul. Outre l’ajout du Windows App SDK, une application non empaquetée a besoin que le runtime soit déployé et initialisé sur les machines cibles.19

Étapes pour afficher un sélecteur depuis une application de bureauAprès qu'une application de bureau a créé un sélecteur, appeler PickSingleFolderAsync tel quel aboutit à une exception ou à un échec silencieux, donc il faut d'abord obtenir le HWND de la fenêtre propriétaire et le passer via Initialize de IInitializeWithWindow avant d'afficher le sélecteurSélecteur (WinRT)Application de bureauSélecteur (WinRT)Application de bureaualt[Afficher sans passer de HWND][Passer d'abord le HWND]CréerPickSingleFolderAsyncException ou échec silencieuxDéfinir le HWND via IInitializeWithWindowPickSingleFolderAsyncLe sélecteur s'affiche

Figure 10 : Combler le fossé « il n’y a pas de CoreWindow sur le bureau » en passant un HWND explicitement est la pratique officielle.

Point de blocage 2 : certaines API exigent une identité de package

Certaines API WinRT, telles que l’historique des notifications toast (ToastNotificationHistory), les listes de raccourcis et les cibles de partage, ne fonctionnent que dans des applications qui ont une identité de package (applications empaquetées). Les appeler depuis une application non empaquetée distribuée avec un installateur traditionnel échoue.7

Il y a deux remèdes. Empaqueter l’application avec MSIX, ou utiliser un « package à emplacement externe » (un sparse package), qui accorde une identité tout en conservant l’installateur existant.20 Quelles API exigent une identité se vérifie dans la liste officielle.7

Deux chemins vers les API qui exigent une identité de packageAppeler une API WinRT qui exige une identité de package depuis une application non empaquetée échoue, donc donnez à l'application une identité de package soit en l'empaquetant avec MSIX, soit avec un package à emplacement externe, qui n'accorde que l'identifiant tout en conservant l'installateur existantVouloir utiliser une API exigeant une identitéEmpaqueter avec MSIXPackage à emplacement externeObtenir l'identité de packageL'historique des notifications etc. fonctionne

Figure 11 : Le remède pour les API exigeant une identité est un choix entre deux : « MSIX » ou « installateur existant + octroi d’un identifiant ».

Point de blocage 3 : vérifier l’initialisation du thread et STA/MTA

Un thread qui manipule des objets WinRT doit être initialisé pour WinRT au préalable. En code natif, utilisez RoInitialize ou winrt::init_apartment et indiquez le modèle de concurrence, STA ou MTA. Dans les applications C# WPF/WinForms, le runtime s’occupe en général de l’initialisation.8

RoInitialize est le point d’entrée de génération WinRT dans le même cadre que CoInitializeEx de COM. La documentation de CoInitialize elle-même indique d’appeler RoInitialize ou Windows::Foundation::Initialize à la place lorsque vous utilisez Windows Runtime.21

Le thread d’interface de WPF/WinForms est un STA ; les objets d’interface doivent être touchés sur le thread d’interface ; les attentes bloquantes sur un STA invitent les interblocages. La pensée traitée dans l’article STA/MTA se transpose inchangée aux API WinRT.

Certaines API restent inutilisables même avec un HWND et une identité

Les remèdes jusqu’ici concernent les API qui ont un point d’entrée pour un usage de bureau. Les API qui dépendent de CoreWindow ou d’ApplicationView eux-mêmes ne peuvent pas s’utiliser depuis des applications de bureau. Dans ce cas, plutôt que d’ajouter une initialisation, cherchez une API de remplacement.57

Branchement des points de blocage lors de l'appel d'API WinRT depuis le bureauD'abord confirmer que le thread est initialisé pour WinRT (en code natif, indiquer STA/MTA explicitement avec RoInitialize ou équivalent ; en C#, le runtime s'en charge en général). Puis, si l'API WinRT à appeler est une interface qui présuppose un CoreWindow, passer un HWND via IInitializeWithWindow ou utiliser les nouveaux sélecteurs ; si elle exige une identité de package, accorder un identifiant avec MSIX ou un package à emplacement externe ; les API qui dépendent de CoreWindow ou d'ApplicationView eux-mêmes ne peuvent pas s'utiliser sur le bureau, donc chercher une API de remplacement ; et le grand reste des API s'appelle tel quel avec seulement le réglage TFM ou C++/WinRTInterfaceIdentité exigéeDépend de CoreWindow lui-mêmeTout le resteInitialisation du threadDe quel type d'API s'agit-il ?Natif : explicite ; C# : en général automatiqueHWND ou les nouveaux sélecteursMSIX ou accorder un identifiantChercher une API de remplacementAppelable tel quel

Figure 12 : Avec l’initialisation du thread comme prérequis (explicite en code natif, en général laissée au runtime en C#), les points de blocage se classent en trois familles, chacune avec un remède type.

Correspondance de l'initialisation du thread entre COM et WinRTEn COM classique, un thread s'initialise avec CoInitializeEx en indiquant STA ou MTA, alors qu'en WinRT il s'initialise avec RoInitialize en indiquant le même modèle de concurrence STA ou MTA. La documentation de CoInitialize indique aussi d'appeler RoInitialize lors de l'usage de WinRT, et le concept d'apartment est partagéCOM classique : CoInitializeExApartment (STA / MTA)WinRT : RoInitializeLe thread d'interface est STA ; attention aux attentes

Figure 13 : Le nom de l’API d’initialisation a changé, mais le même concept d’apartment continue d’être utilisé.

7. Ce qui porte WinUI — le développement à découvert ne change pas le contrat

Passer au développement à découvert n’est pas un changement du contrat binaire

À l’été 2025, Microsoft a annoncé officiellement, comme approche par étapes, sa politique de déplacer le développement principal de WinUI à découvert sur GitHub. Il y a quatre étapes : augmenter la fréquence de mise à jour du miroir, rendre les builds locaux possibles, accepter les contributions de la communauté une fois les tests en place, et enfin faire de GitHub le foyer principal du développement.22

À la date de publication de cet article (fin août 2026), la documentation officielle affirme aussi clairement que WinUI est « built in the open ». L’avancement quotidien de l’ingénierie se suit désormais dans le dépôt public.9

Les développeurs qui ont suivi les relèves de génération de WinForms à WPF à UWP à WinUI sentent naturellement « le framework change encore ? ». Mais les frameworks d’interface qui n’ont cessé de changer et le fondement en dessous doivent se regarder séparément. Win32 et COM, et depuis 2012 l’ABI WinRT, sont restés au même endroit.

Les fenêtres WinUI aussi sont portées par un HWND

WinUI est un ensemble d’API WinRT fourni dans le cadre du Windows App SDK.923 Son Microsoft.UI.Xaml.Window est une fenêtre portée par un HWND, qui remplace le modèle de fenêtre fondé sur CoreWindow de la génération UWP. Le tutoriel d’interopération officiel, lui aussi, commence par obtenir le handle de fenêtre.24

Autrement dit, une application WinUI est une application Win32 dans laquelle un arbre d’objets qui suit le contrat IInspectable s’exécute au-dessus d’une fenêtre HWND. Les connaissances de QueryInterface, de comptage de références et d’apartments acquises via COM, et les connaissances de HWND et de boucles de messages acquises via Win32, s’appliquent toutes deux directement au dépannage de WinUI.

Relèves de génération des frameworks d'interface et fondement inchangéLes frameworks d'interface WinForms, WPF, XAML UWP et WinUI ont enchaîné les générations, mais WinForms et WPF reposent directement sur le fondement Win32 et COM, tandis que XAML UWP et WinUI reposent sur le même fondement Win32 et COM via l'ABI WinRT. Ce qui a changé de génération, c'est la couche supérieure ; le contrat en dessous n'a pas changéRelèves de génération des frameworks d'interfaceWinForms, WPFXAML UWPWinUI (actuel)ABI WinRT (depuis 2012)Fondement inchangé (Win32 + COM)

Figure 14 : Ce qui a monté et descendu, c’est la couche framework. WinForms et WPF reposent directement sur Win32 + COM ; UWP et WinUI reposent sur le même fondement via l’ABI WinRT.

Les couches qui portent une application WinUILe XAML et les contrôles de WinUI sont fournis dans le cadre du Windows App SDK, s'exécutent sur l'ABI WinRT, c'est-à-dire le contrat IInspectable, et en dessous se trouve le fondement de COM et du HWND de Win32. Sous les relèves de génération des frameworks d'interface, ce contrat sous-jacent n'a pas changéWinUI (XAML, contrôles)Windows App SDKABI WinRT (IInspectable)COM + Win32 (HWND)

Figure 15 : Sous WinUI se trouve l’ABI WinRT, et en dessous encore COM classique et Win32. Seul l’empilement a changé ; le fondement est le même.

Mélanger avec XAML Islands : vérifier les contraintes de chaque génération

La stratégie « ne mélanger que des contrôles WinUI dans des écrans WPF/WinForms existants » doit s’évaluer séparément pour chaque génération de XAML Islands.

Génération Situation lors de l’usage depuis WPF/WinForms
XAML Islands de génération UWP Des contrôles enveloppe du Windows Community Toolkit existent. Mais les versions WPF/WinForms se sont arrêtées à la génération .NET Core 3.x et ne sont pas prises en charge sur le .NET actuel25
Génération WinUI 3 Peut s’héberger depuis WPF, WinForms et Win32 avec DesktopWindowXamlSource du Windows App SDK. Mais il n’y a pas de contrôles enveloppe commodes comme ceux de la génération UWP, et la charge d’implémentation et de validation de traiter l’API d’hébergement directement vous incombe26

Si vous prévoyez un mélange progressif, il est plus sûr de ne pas présupposer le seul mélange au niveau des contrôles. Organiser le plan autour d’un usage des API WinRT au niveau des fonctionnalités (section 6) ou d’une séparation au niveau de l’écran ou du processus est l’approche plus solide à l’horizon 2026.

8. Implications pour les applications métier — migration complète et usage sélectif sont des problèmes distincts

« WinRT est un monde nouveau et séparé, donc l’utiliser signifie jeter les actifs existants et reconstruire. » Cette idée fausse, rencontrée dans les projets de développement sur mesure, se dissipe une fois qu’on la scinde en deux.

Les actifs COM existants et WinRT peuvent coexister

Une application WPF qui utilise l’automation COM d’Excel, héberge des contrôles ActiveX et appelle des composants COM internes peut se voir ajouter des API WinRT. Ce n’est pas une acrobatie particulière, parce que vous combinez des fonctionnalités sur le même fondement COM.

Pour les notifications toast, par exemple, réglez le TFM pour référencer l’API et satisfaites les conditions d’inscription pour afficher des notifications. Pour que ces dernières ne soient pas oubliées, la section 9 les pose chemin par chemin. En C++/WinRT, les interfaces WinRT et COM classique peuvent se traiter avec les mêmes mécanismes, winrt::com_ptr et winrt::implements.21

Estimer séparément le renouvellement d’interface et l’ajout de fonctionnalités

« Migrer entièrement l’interface vers WinUI ? » et « N’utiliser les API WinRT que là où c’est nécessaire ? » sont des décisions qui diffèrent en ampleur, en durée et en risque.

Une migration d’interface complète est un problème de choix de framework qui dépend des actifs d’écran, des contrôles tiers et de l’organisation de développement. Nous avons traité cette décision dans Comment choisir entre WinForms, WPF et WinUI. L’usage sélectif des API WinRT, lui, est une petite amélioration que vous pouvez commencer dès aujourd’hui dans une application existante.

Pour un projet qui ne veut que des notifications toast, il n’y a pas lieu d’estimer une migration d’interface complète. Inversement, il n’y a pas lieu de reporter l’usage des API WinRT « parce que nous ne migrons pas vers WinUI ».

Migration complète et usage sélectif sont des décisions distinctesUne migration d'interface complète vers WinUI est une grande décision de choix de framework qui dépend des actifs d'écran et de l'organisation, tandis que l'usage sélectif des API WinRT est une petite décision que l'on peut ajouter dès aujourd'hui à une application WPF ou WinForms existante avec un réglage de TFM ou équivalent ; examinez les deux séparément sans les confondreVouloir utiliser de nouvelles fonctionnalités WindowsMigration d'interface complète (grande décision)Usage sélectif des API WinRT (petite décision)Dépend des actifs d'écran et de l'organisationPeut s'ajouter à l'application existante dès aujourd'hui

Figure 16 : Il y a deux routes vers « de nouvelles fonctionnalités », et les confondre fausse à la fois l’estimation et la décision.

9. Tableau de décision — la version WinRT de conserver, envelopper, remplacer

Ne choisir que les changements dont chaque situation a besoin

En prolongement du tableau de décision ActiveX, voici un guide de décision par situation du côté WinRT.

Situation Recommandation Raison
Vouloir utiliser des API WinRT telles que toast, Partage ou Bluetooth depuis WPF/WinForms Usage sélectif via un réglage de TFM (ou l’ajout de C++/WinRT) Callable dès aujourd’hui sans migration d’interface. Mais l’interface de partage passe par IDataTransferManagerInterop (section 6), et pour le toast voir le complément sous le tableau106
Exception ou échec silencieux dans un sélecteur ou une boîte de dialogue Passer un HWND via IInitializeWithWindow. Pour du code nouveau, les nouveaux sélecteurs fondés sur WindowId (exige l’ajout du Windows App SDK) La pratique officielle de combler la conception fondée sur CoreWindow avec un HWND619
L’historique des notifications, les listes de raccourcis et assimilés ne fonctionnent pas Accorder une identité de package avec MSIX ou un package à emplacement externe Les API exigeant une identité présupposent l’empaquetage720
Actifs COM/ActiveX/OLE existants Ne pas les jeter. Décider conserver, envelopper ou remplacer par actif Pas exclusifs de WinRT ; ils coexistent sur le même fondement (section 8)
Interface d’une nouvelle application de bureau Évaluer WinUI comme premier candidat (WPF reste actuel) Le développement à découvert a clarifié la direction d’investissement. En dessous se trouve l’ABI WinRT229
Mélanger des contrôles WinUI dans des écrans WPF/WinForms existants Évaluer avec prudence, en prévoyant la charge d’implémentation de l’absence d’enveloppes Les Islands de génération UWP se sont arrêtées à .NET Core 3.x ; la génération WinUI 3 n’offre que l’API d’hébergement2526
Un plan fondé sur « WinRT s’est arrêté avec UWP » Corriger le prérequis WinRT est le fondement d’API actuel, appelable depuis le bureau5

Les notifications toast n’apparaissent pas avec un réglage de TFM seul

Le réglage qui rend l’API appelable et l’inscription qui fait apparaître une notification sont deux choses distinctes. Quand « la compilation réussit mais aucune notification n’apparaît », vérifiez non seulement le code d’appel, mais le chemin utilisé, la forme d’empaquetage et si le processus est élevé.

Lors de l’usage du ToastNotificationManager classique

Pour une application non empaquetée sans identité de package, le prérequis est d’inscrire un raccourci du menu Démarrer auquel est attribué un AppUserModelID (AUMID). Sans cela, les toasts ne peuvent pas s’afficher.27

Une application empaquetée avec MSIX ou équivalent n’a pas besoin de cette inscription manuelle, car l’identité de package fournit l’AUMID.

Lors de l’usage d’AppNotificationManager du Windows App SDK

Sur ce chemin actuellement recommandé, l’ajout du Windows App SDK et l’appel de Register() au démarrage sont requis. Au-delà, les conditions se ramifient selon la forme d’empaquetage.28

Forme d’empaquetage Ce qu’il faut vérifier en plus
Non empaquetée Le runtime Windows App SDK doit être déployé sur chaque PC cible. Register() effectue l’inscription du serveur COM
Empaquetée avec MSIX L’inscription automatique par Register() ne fonctionne pas. Déclarez un activateur COM dans Package.appxmanifest

De plus, sur le chemin Windows App SDK, les notifications depuis un processus élevé au niveau administrateur ne sont pas prises en charge. Show échoue silencieusement sans lever d’exception. Dans une application qui a besoin d’une élévation, envisagez de séparer les notifications dans un processus non élevé.28

Les détails d’implémentation autour de l’inscription sont traités dans le guide d’implémentation de la zone de notification et des notifications.

Les deux chemins des notifications toast et l'inscription dont chacun a besoinPour afficher une notification toast depuis une application de bureau, le chemin classique ToastNotificationManager se ramifie selon que l'application est empaquetée : une application non empaquetée doit passer par l'inscription d'un raccourci du menu Démarrer auquel est attribué un AppUserModelID, tandis que pour une application empaquetée l'identité de package fournit l'AppUserModelID. Sur le chemin AppNotificationManager du Windows App SDK, outre l'ajout du SDK et l'appel de Register au démarrage, une application non empaquetée doit déployer le runtime sur chaque PC cible et une application empaquetée avec MSIX doit déclarer un activateur COM dans le manifeste avant de pouvoir avancer. Le chemin Windows App SDK se ramifie encore selon que le processus est élevé : les notifications depuis un processus élevé au niveau administrateur ne sont pas prises en charge et Show échoue silencieusement sans lever d'exception, donc sur ce chemin une notification ne s'affiche que pour un processus non élevé, et une application qui a besoin d'une élévation sépare les notifications dans un processus non élevéNonOuiNonOuiNonOuiVouloir afficher un toastClassique : ToastNotificationManagerWASDK : AppNotificationManagerEmpaquetée ?Inscrire un raccourci AUMIDL'identité fournit l'AUMIDAjouter le SDK + Register()Empaquetée ?Déployer le runtimeDéclarer COM dans le manifesteLa notification s'afficheProcessus élevé ?Non pris en charge : Show échoue silencieusementSéparer les notifications dans un processus non élevé

Figure 17 : Sur l’un et l’autre chemin, la notification n’apparaît qu’après que les prérequis de la forme d’empaquetage sont satisfaits. Sur le chemin Windows App SDK, même avec tous les prérequis en place, rien ne s’affiche depuis un processus élevé.

Enfin, revenir à conserver, envelopper, remplacer

Avez-vous besoin d’une nouvelle fonctionnalité, ou voulez-vous renouveler l’interface elle-même ? Revenir à cette question permet de décider séparément de l’usage de WinRT et du remplacement des actifs existants.

Flux de décision sur la façon dont les actifs existants et WinRT s'emboîtentPartant d'une application de bureau existante, un flux de décision par étapes : si aucune nouvelle fonctionnalité Windows n'est nécessaire, conserver ; si une fonctionnalité est nécessaire, envelopper par un usage sélectif des API WinRT (en veillant aux points de blocage de la section 6, HWND, identité de package et initialisation du thread) ; et seulement lorsque l'interface elle-même a besoin d'être renouvelée, envisager de la remplacer par WinUIL'état actuel suffitUne nouvelle fonctionnalitéRenouvellement d'interfaceApplication de bureau existanteDe quoi a-t-on besoin ?Conserver (maintenir tel quel)Envelopper (usage sélectif des API WinRT)Remplacer (évaluer WinUI)Veiller à HWND, identité, initialisation (section 6)

Figure 18 : La même structure « conserver, envelopper, remplacer » que le tableau de décision ActiveX s’applique directement du côté WinRT.

10. Résumé

WinRT n’est pas un nouvel environnement d’exécution coupé de COM. C’est un fondement d’API qui combine le contrat binaire COM avec des métadonnées et des projections vers chaque langage.

Élément Son rôle tel que traité dans cet article
IUnknown et IInspectable Bâtis sur QueryInterface et le comptage de références, en ajoutant trois méthodes qui renvoient le nom de type et davantage
.winmd Le contrat partagé par chaque langage, à partir duquel un nom de type mène à une définition. Emprunte le format ECMA-335, mais ceux fournis par le système ne contiennent pas de code exécutable
C++/WinRT et C#/WinRT Génèrent une API pour chaque langage à partir du contrat. Depuis .NET 5, la projection C# est une chaîne d’outils indépendante du runtime

Même lorsque la surface est une API C# ou C++ naturelle, en dessous ce sont des appels COM via vtables et des HRESULT. C’est pourquoi comprendre le passage de HWND sur le bureau, l’identité de package, et STA/MTA avec l’initialisation du thread rend les incidents WinRT plus faciles à isoler.

L’effort de déplacer le développement principal de WinUI à découvert a été annoncé à l’été 2025 comme une approche par étapes. À la date de publication de cet article, la documentation officielle dit aussi « built in the open », mais le contrat en dessous, IUnknownIInspectable, n’a pas changé.229

La conclusion pour les applications métier est la même. Les actifs COM/ActiveX existants et WinRT peuvent coexister. Ne pas confondre une migration d’interface complète et l’usage sélectif des API WinRT est ce qui protège la précision des estimations et des décisions.

Les documents composés des années 1990 vus dans l’article OLE et WinUI d’aujourd’hui reposent sur le même IUnknown. Connaître COM, ce n’est pas seulement « bien connaître le legacy ». C’est pouvoir lire le fondement de Windows aujourd’hui.

Articles connexes

Domaines de conseil associés

KomuraSoft LLC prend en charge le développement de composants COM ainsi que la maintenance, la modification et le remplacement d’applications métier Windows qui incluent des actifs COM. Vous pouvez nous consulter dès le stade où le contenu de cet article est précisément le sujet en jeu : « nous voulons des notifications toast et des sélecteurs tout en restant sur WPF », « l’appel d’une API WinRT s’est arrêté sur une exception », « nous voulons décider s’il faut migrer vers WinUI ou tirer parti des actifs existants ».

Références

  1. Microsoft Learn, Author COM components with C++/WinRT. Sur le fait que les composants COM et les classes WinRT exposent tous deux la fonctionnalité à travers des interfaces ; l’affirmation explicite « The Windows Runtime is based on COM » ; le fait que les interfaces COM classiques dérivent d’IUnknown et les interfaces WinRT d’IInspectable, qui dérive d’IUnknown ; et le fait que la dérivation entre interfaces définies par l’utilisateur telle que IFileSystemBindData2 : IFileSystemBindData est une fonctionnalité de COM classique délibérément absente du système de types WinRT.  2 3 4 5 6

  2. Microsoft Learn, Consume COM components with C++/WinRT. Sur le fait de programmer en COM à travers des interfaces plutôt que des objets ; que cela vaut aussi dans les coulisses des API WinRT, qui sont « an evolution of COM » ; et que l’on peut traiter WinRT et COM classique dans le même style avec le pointeur intelligent COM winrt::com_ptr.  2 3 4

  3. Microsoft Learn, Windows Metadata (WinMD) files. Sur le fait que les API WinRT sont décrites dans des métadonnées lisibles par machine appelées .winmd, utilisées par les outils et les projections de langage ; que Windows livre les métadonnées de chaque API WinRT fournie par le système et fournit des API de résolution ; que les tiers peuvent participer à la projection de langage avec le même format ; et que le format physique est la spécification ECMA-335 (la même que les assemblys CLR), les fichiers WinMD fournis par le système étant de pures métadonnées.  2 3 4 5

  4. Microsoft Learn, Windows Runtime (WinRT) language projections. Sur le fait que les projections de langage exposent les API WinRT dans l’idiome de chaque langage ; que .winmd définit les API WinRT et que les projections le lisent ; et que les deux projections prises en charge par Microsoft sont C++/WinRT (C++17 ou ultérieur) et C#/WinRT (.NET).  2 3

  5. Microsoft Learn, WinRT APIs callable from a desktop app. Sur le fait que la plupart des API WinRT sont utilisables depuis des applications de bureau .NET et C++ natives, les classes conçues spécifiquement pour UWP, telles que CoreDispatcher, CoreWindow et ApplicationView, étant les exceptions.  2 3 4

  6. Microsoft Learn, Display WinRT UI objects that depend on CoreWindow. Sur le fait que certains sélecteurs, fenêtres contextuelles et boîtes de dialogue dépendent de CoreWindow ; que CoreWindow n’est pas pris en charge dans les applications de bureau ; que les classes qui implémentent IInitializeWithWindow (ou l’équivalent IDataTransferManagerInterop) permettent de définir le HWND de la fenêtre propriétaire avant l’affichage ; et les étapes pour WinUI 3, WPF et WinForms respectivement.  2 3 4 5 6

  7. Microsoft Learn, WinRT APIs not supported in desktop apps. Sur les deux familles d’API WinRT inutilisables dans les applications de bureau, celles qui dépendent de fonctionnalités d’interface propres à UWP et celles qui exigent une identité de package (telles que ToastNotificationHistory et JumpList), et sur le fait que ces dernières ne sont prises en charge que dans les applications empaquetées avec MSIX.  2 3 4 5

  8. Microsoft Learn, RoInitialize function (roapi.h). Sur le fait que RoInitialize initialise le thread courant pour Windows Runtime avec le modèle de concurrence indiqué (RO_INIT_SINGLETHREADED/RO_INIT_MULTITHREADED) ; que chaque thread qui active et opère sur des objets WinRT a besoin d’une initialisation préalable ; et qu’une indication contradictoire sur un thread déjà initialisé en MTA donne RPC_E_CHANGED_MODE.  2

  9. Microsoft Learn, WinUI 3. Sur le fait que WinUI est le framework d’interface natif recommandé pour les nouvelles applications de bureau Windows, qu’il est fourni dans le cadre du Windows App SDK, qu’il s’exécute sur Windows 10 version 1809 et ultérieure, et qu’il est développé à découvert.  2 3 4 5

  10. Microsoft Learn, Call Windows Runtime APIs in desktop apps. Sur le fait qu’indiquer un TFM portant une version d’OS Windows (tel que net10.0-windows10.0.22621.0) à partir de .NET 6 fait référencer le package de ciblage du Windows SDK afin que les API WinRT puissent s’appeler, et que C++ utilise C++/WinRT avec le package NuGet Microsoft.Windows.CppWinRT et C++17 ou une version ultérieure.  2 3 4

  11. Microsoft Learn, Introduction to C++/WinRT. Sur le fait que C++/WinRT est une projection de langage en C++17 entièrement standard et moderne, implémentée comme bibliothèque fondée sur des fichiers d’en-tête ; qu’il est le successeur recommandé de C++/CX et de WRL ; que WinRT est fondé sur des API COM et conçu pour être accédé via des projections de langage ; que les projections cachent les détails de COM ; et que cppwinrt.exe génère des en-têtes de projection à partir de .winmd.  2 3

  12. Microsoft Learn, The Windows Runtime (WinRT) type system. Sur le fait que chaque interface WinRT exige implicitement IInspectable et qu’IInspectable exige IUnknown ; qu’IUnknown définit QueryInterface, AddRef et Release ; les trois méthodes qu’IInspectable ajoute, GetIids, GetRuntimeClassName et GetTrustLevel ; que GetRuntimeClassName renvoie un nom de type résoluble via les métadonnées, ce qui rend possible la projection de langage ; et que l’héritage entre interfaces définies par l’utilisateur est absent du système de types WinRT et s’exprime avec requires à la place.  2 3 4

  13. Microsoft Learn, IInspectable interface (inspectable.h). Sur le fait qu’IInspectable fournit une fonctionnalité exigée par chaque classe WinRT, hérite d’IUnknown, et a les trois méthodes GetIids, GetRuntimeClassName et GetTrustLevel. 

  14. Microsoft Learn, Introduction to Microsoft Interface Definition Language 3.0. Sur le fait que MIDL 3.0 est une syntaxe concise et moderne pour déclarer des types WinRT, et que les contrats WinRT s’écrivent encore en IDL, le compilateur MIDL générant Windows Metadata (.winmd). 

  15. Microsoft Learn, C#/WinRT. Sur le fait que cswinrt.exe, inclus dans le package NuGet C#/WinRT, traite .winmd pour générer du code C# et le compiler en assembly d’interopération, et que cela se positionne de la même façon que C++/WinRT générant des en-têtes pour C++. 

  16. Microsoft Learn, Built-in support for WinRT is removed from .NET. Sur le fait que la prise en charge intégrée de Windows Runtime a été retirée de .NET dans .NET 5 et déplacée vers la chaîne d’outils CsWinRT.  2

  17. Microsoft Learn, IInitializeWithWindow interface (shobjidl_core.h). Sur le fait qu’il s’agit de l’interface pour fournir une fenêtre propriétaire aux objets WinRT utilisés dans les applications de bureau, qu’elle hérite d’IUnknown et qu’elle a une méthode Initialize(HWND). 

  18. Microsoft Learn, Use WinRT COM interop classes in .NET. Sur le fait que certains objets WinRT, tels que les sélecteurs de fichiers et les boîtes de dialogue, ont besoin d’un HWND avant de fonctionner dans une application de bureau, et que les classes C# sûres vis-à-vis des types WinRT.Interop.WindowNative et WinRT.Interop.InitializeWithWindow permettent l’initialisation sans appels QueryInterface écrits à la main.  2

  19. Microsoft Learn, Tutorial: Open files and folders with pickers in WinUI. Sur le fait que les Windows.Storage.Pickers classiques doivent être initialisés avec un HWND avant l’affichage lorsqu’ils sont utilisés dans une application de bureau (WinUI 3), sinon ils lèvent une exception ou échouent silencieusement ; que les applications de bureau WinUI 3 n’ont pas de CoreWindow ; et que les nouveaux sélecteurs du Windows App SDK (Microsoft.Windows.Storage.Pickers) prennent un WindowId dans le constructeur, ce qui rend le schéma InitializeWithWindow inutile.  2 3

  20. Microsoft Learn, Features that require package identity. Sur le fait que certaines fonctionnalités Windows et API WinRT exigent une identité de package à l’exécution, et qu’une identité s’obtient non seulement en distribuant un package MSIX, mais aussi avec un package à emplacement externe.  2

  21. Microsoft Learn, CoInitialize function (objbase.h). Sur le fait que CoInitialize initialise la bibliothèque COM en STA ; que les nouvelles applications sont censées appeler CoInitializeEx ; et qu’il faut appeler RoInitialize ou Windows::Foundation::Initialize à la place lorsque l’on utilise Windows Runtime. 

  22. GitHub, WinUI: Now Developing in the Open (microsoft/microsoft-ui-xaml Discussion #10700). L’annonce officielle de fin juillet 2025. Sur l’approche par étapes pour ouvrir le dépôt WinUI (augmenter la fréquence de mise à jour du miroir, permettre les builds locaux, accepter les contributions de la communauté une fois les tests en place, et enfin faire de GitHub le foyer principal du développement).  2 3

  23. Microsoft Learn, Windows App SDK. Sur le fait que le Windows App SDK est l’ensemble actuel de bibliothèques de développement d’applications Windows qui inclut WinUI. 

  24. Microsoft Learn, Walkthrough: WinUI 3 app with Win32 interop. Sur le fait que la classe Window de WinUI a été étendue pour prendre en charge les fenêtres de bureau, et que Window dans une application de bureau WinUI 3 est portée par un handle de fenêtre Win32 (HWND), de sorte que le handle peut s’obtenir et se manipuler avec des API Win32. 

  25. Microsoft Learn, Host UWP XAML controls in desktop apps (UWP XAML Islands). Sur le fait que les XAML Islands de génération UWP sont le mécanisme pour héberger des contrôles XAML UWP dans des applications de bureau WPF, WinForms et C++, et que leur usage depuis WPF/WinForms est limité aux applications ciblant .NET Core 3.x, non pris en charge sur le .NET actuel et .NET Framework.  2

  26. Microsoft Learn, DesktopWindowXamlSource Class (Microsoft.UI.Xaml.Hosting). Sur le fait qu’il s’agit de la classe centrale de l’API d’hébergement XAML du Windows App SDK, capable d’héberger des contrôles WinUI dans tout élément d’interface associé à un HWND, et utilisable depuis des applications de bureau construites avec WPF, Windows Forms et Win32 (API Windows).  2

  27. Microsoft Learn, Quickstart: Sending a toast notification from the desktop. Sur le fait qu’envoyer un toast depuis une application de bureau présuppose un raccourci du menu Démarrer avec System.AppUserModel.ID défini, et que cet AppUserModelID doit être passé à l’appel CreateToastNotifier, sans quoi le toast ne s’affiche pas. 

  28. Microsoft Learn, Use app notifications with a .NET app. Sur le fait qu’utiliser AppNotificationManager du Windows App SDK dans une application WPF/WinForms exige un appel à Register() après l’inscription du gestionnaire NotificationInvoked ; que Register() effectue automatiquement, pour les applications non empaquetées, l’inscription du serveur COM qui lance l’application lorsqu’on clique sur une notification ; et que les prérequis sont l’ajout du Windows App SDK et la configuration des appels d’API WinRT.  2

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

WinRT est-il un runtime managé comme .NET ?
Non. WinRT (Windows Runtime) n'est pas un environnement d'exécution doté d'une machine virtuelle ou d'un ramasse-miettes ; c'est un ABI (un contrat binaire) construit sur COM. La documentation de Microsoft affirme clairement que « The Windows Runtime is based on COM », et chaque interface WinRT exige IInspectable, qui dérive de IUnknown. Chaque appel reste un appel COM via une vtable, fondé sur le comptage de références (AddRef/Release) et QueryInterface. Le nom « .winmd » et l'affinité avec .NET font facilement prendre WinRT pour un environnement managé, mais un .winmd est un fichier de métadonnées qui emprunte le même format physique qu'ECMA-335, et les fichiers .winmd fournis par le système ne contiennent pas de code exécutable. Si C# peut l'appeler aussi naturellement, c'est qu'une projection de langage génère une vue C# à partir de ces métadonnées.
On dit que UWP n'est plus le courant principal. Y a-t-il encore un intérêt à apprendre WinRT ?
Oui. UWP, le modèle d'application, et WinRT, le fondement d'API, sont deux choses distinctes. Même après le recul d'UWP, la plupart des API WinRT continuent d'être fournies sous une forme que les applications de bureau WPF, WinForms et Win32 peuvent appeler, et une grande part des capacités de Windows aujourd'hui, comme les notifications toast, le Partage, Bluetooth et l'OCR, sont exposées en API WinRT. De plus, WinUI (Windows App SDK), le framework d'interface natif que Microsoft recommande actuellement, est construit sur l'ABI WinRT. Autrement dit, les mécanismes de WinRT (IInspectable, .winmd, projections de langage) ne sont pas un vestige d'UWP ; ils sont précisément le fondement du développement d'applications Windows actuel.
Une application WPF ou WinForms peut-elle appeler des API WinRT ?
Oui. À partir de .NET 6, il suffit de régler le TargetFramework du projet sur un TFM portant une version d'OS Windows, par exemple net8.0-windows10.0.19041.0 ; les assemblys de projection du Windows SDK sont alors référencés et les API WinRT des espaces de noms Windows.* s'appellent directement depuis C#. En C++, ajoutez le package NuGet Microsoft.Windows.CppWinRT et utilisez C++/WinRT avec C++17 ou une version ultérieure. Trois points de blocage existent toutefois. Premièrement, les classes d'interface qui présupposent un CoreWindow, comme les sélecteurs et les boîtes de dialogue, ont besoin que le HWND de la fenêtre propriétaire soit passé via IInitializeWithWindow avant l'affichage (l'exception est DataTransferManager de l'interface de partage : au lieu de IInitializeWithWindow, il utilise l'IDataTransferManagerInterop dédié, un chemin distinct qui passe le HWND à ShowShareUIForWindow). Deuxièmement, certaines API, comme l'historique des notifications et les listes de raccourcis, exigent une identité de package (empaquetage MSIX ou package à emplacement externe). Troisièmement, un thread qui manipule des objets WinRT doit d'abord être initialisé. En code natif, indiquez le modèle de concurrence STA/MTA avec winrt::init_apartment ou RoInitialize (dans les applications C# WPF/WinForms, le runtime s'en charge en général). Notez que les API qui dépendent de CoreWindow ou d'ApplicationView eux-mêmes ne peuvent pas du tout s'utiliser depuis une application de bureau.
Appeler un sélecteur tel que FolderPicker depuis une application de bureau lève une exception. Pourquoi ?
Parce que certaines classes WinRT de sélecteur et de boîte de dialogue ont été conçues pour s'afficher sur un CoreWindow UWP. Une application de bureau n'a pas de CoreWindow, il faut donc indiquer explicitement la fenêtre propriétaire avant l'affichage. Concrètement, obtenez d'abord le HWND de la fenêtre propriétaire (WinRT.Interop.WindowNative.GetWindowHandle pour une Window WinUI, WindowInteropHelper pour WPF, la propriété Handle du formulaire pour WinForms), puis en C# passez-le au sélecteur avec WinRT.Interop.InitializeWithWindow.Initialize. En C++/WinRT, faites un QueryInterface de l'objet vers IInitializeWithWindow (as<IInitializeWithWindow>()) et appelez Initialize(hwnd). Sans cette initialisation, l'appel lève une exception ou échoue silencieusement. Notez que les sélecteurs plus récents du Windows App SDK (Microsoft.Windows.Storage.Pickers) ont été retravaillés pour recevoir un WindowId dans le constructeur, ce qui rend ce schéma d'initialisation inutile (mais ce sont des API du Windows App SDK : un réglage de TFM ne suffit pas, il faut ajouter le SDK et, pour une application non empaquetée, déployer et initialiser le runtime sur les machines cibles).
Le développement de WinUI a été ouvert sur GitHub. Faut-il jeter les applications WPF/WinForms existantes ?
Il n'y a pas lieu de se précipiter. Ouvrir le développement principal de WinUI est une déclaration de direction, celle d'un investissement sérieux de Microsoft dans son framework natif, et non un avis de fin des frameworks existants. WPF et WinForms restent aujourd'hui pris en charge comme partie de .NET. Séparez la décision en deux. L'une est de savoir s'il faut migrer le framework d'interface, une grande décision qui dépend de la taille des actifs d'écran, des contrôles tiers et de l'organisation de développement. L'autre est de savoir s'il faut utiliser sélectivement des API WinRT depuis l'application existante, ce qui peut commencer dès aujourd'hui avec un réglage de TFM. Notez que le TFM ne fait que rendre les API appelables ; les notifications toast, par exemple, exigent une inscription en plus de l'appel (sur le chemin classique, une application non empaquetée doit inscrire un raccourci avec un AppUserModelID — une application empaquetée n'en a pas besoin, car l'identité de package fournit l'AppUserModelID ; sur le chemin Windows App SDK, il faut ajouter le SDK — et pour une application non empaquetée installer aussi le runtime Windows App SDK sur chaque PC cible — et appeler Register() d'AppNotificationManager, tandis que pour une application empaquetée avec MSIX l'inscription automatique par Register() ne fonctionne pas et un activateur COM doit en plus être déclaré dans Package.appxmanifest). De plus, sur le chemin Windows App SDK, les notifications depuis un processus élevé au niveau administrateur ne sont pas prises en charge, et Show échoue silencieusement sans lever d'exception — une application qui a besoin d'une élévation devrait envisager de séparer les notifications dans un processus non élevé. Même ainsi, si vous ne voulez que des notifications toast, migrer vers WinUI n'est pas nécessaire. Notez que mélanger des contrôles WinUI dans des écrans WPF/WinForms existants (XAML Islands) exige de distinguer les générations. Les Islands de génération UWP se sont arrêtées à .NET Core 3.x pour WPF/WinForms, et pour la génération WinUI 3 l'API d'hébergement XAML du Windows App SDK (DesktopWindowXamlSource) est utilisable depuis WPF/WinForms, mais il n'y a pas de contrôles enveloppe commodes et la charge d'implémentation est lourde, donc l'évaluer avec prudence est pour l'instant la voie sûre.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog