WinRT es COM — IInspectable, .winmd, proyecciones de lenguaje y por qué WinUI sigue apoyándose en un contrato binario

· Actualizado el: · · Windows, WinRT, COM, WinUI, Windows App SDK, Desarrollo para Windows

Historial de revisiones (primera versión, publicada el 29 Aug 2026)
Primera publicación

Quiere añadir una capacidad nueva de Windows a una aplicación WPF o WinForms. Pero llamar a un selector WinRT lanza una excepción. Luego lee sobre WinUI y parece que habría que reconstruir la interfaz. Esta confusión se aclara una vez que separa el funcionamiento de WinRT de la elección del marco de interfaz.

El punto de partida de este artículo es que WinRT también se apoya en el contrato binario de COM. Microsoft mismo afirma con claridad que «The Windows Runtime is based on COM».1 La tabla de Excel incrustada en Word que vimos en el artículo anterior sobre objetos OLE, y el WinRT y WinUI de hoy, tienen el mismo IUnknown en la raíz.

Aquí fijamos primero los tres elementos que componen WinRT, luego vemos a qué prestar atención al usarlo desde una aplicación de escritorio y, por último, cómo tratar los activos existentes. Los lectores previstos son desarrolladores con experiencia en COM o en el desarrollo de escritorio para Windows, los requisitos previos son Windows 10/11 y .NET 6 o posterior (C#) o C++17 (C++/WinRT), y el nivel de dificultad es intermedio.

1. Primero la conclusión

Hay tres conclusiones que conviene retener.

  • WinRT no es un runtime administrado; es un ABI (un contrato binario) construido sobre COM. A ese contrato se añaden .winmd, que transporta información de tipos, y las proyecciones de lenguaje, que permiten a cada lenguaje llamarlo con naturalidad.1234
  • La mayoría de las API WinRT se pueden usar desde aplicaciones WPF, WinForms y Win32 existentes. Pero hay que comprobar tres requisitos previos: pasar un HWND, la identidad de paquete y la inicialización del subproceso.5678
  • Una migración completa de interfaz a WinUI y el uso selectivo de API WinRT son decisiones distintas. WinUI también se asienta en el ABI de WinRT, y los activos COM/ActiveX existentes y WinRT pueden coexistir sobre el mismo fundamento.921

A partir de aquí, leer en el orden siguiente hace más fácil seguir las conexiones.

Lo que quiere saber Dónde leer
¿Por qué se puede decir «WinRT es COM»? Secciones 2–3: lo que comparte con COM, e IInspectable
¿Por qué C# y C++ pueden llamarlo con tanta naturalidad? Secciones 4–5: .winmd y las proyecciones de lenguaje
¿Qué hay que comprobar para usarlo desde una aplicación existente? Sección 6: HWND, identidad de paquete, apartments
¿Hay que migrar a WinUI? Secciones 7–9: lo que sostiene a WinUI, la decisión de migración, las condiciones de registro de las notificaciones

El mapa de conocimiento siguiente sirve para revisar cómo se relacionan los elementos. Si prefiere leer primero la explicación, continúe en orden desde la sección 2.

En el diagrama, una línea continua marca una relación que siempre se cumple y una línea discontinua una relación condicional (las condiciones están en la explicación de cada relación en la página de detalle). La lista completa de relaciones (20 en total, con evidencia y grado de certeza) y las definiciones de los conceptos principales están reunidas en la página de detalle del mapa de conocimiento (en japonés). Datos: JSON-LD / Turtle

2. OLE y WinRT comparten en la raíz el mismo contrato binario

En este blog hemos seguido hasta ahora el mundo de COM clásico: la filosofía de diseño de COM, el modelo de subprocesos STA/MTA, el trato de ActiveX/OCX y los documentos compuestos OLE. Todas son tecnologías de la década de 1990.

WinRT, en cambio, es el fundamento de API introducido con Windows 8 (2012). Hoy proporciona notificaciones toast, Compartir, Bluetooth, OCR y más como las API de los espacios de nombres Windows.*, y es también aquello sobre lo que se apoyan WinUI y el Windows App SDK.10 Lo antiguo y lo nuevo parecen mundos enteramente separados, pero en sustancia son continuos.

Lo que comparten es la promesa de llamar a través de interfaces

Los componentes COM y las clases WinRT exponen ambos su funcionalidad a través de interfaces. Al comparar las interfaces base, la relación queda así.1

COM clásico WinRT
La base de cada interfaz es IUnknown La base de cada interfaz es IInspectable, cuya base es IUnknown

En otras palabras, WinRT no sustituyó COM por un mecanismo ajeno; es una capa más apilada sobre IUnknown. El recuento de referencias, QueryInterface y HRESULT siguen vivos tal como estaban.

La documentación de C++/WinRT también llama a las API WinRT «an evolution of COM» y explica que están diseñadas para consumirse, como API basadas en COM, a través de proyecciones de lenguaje.211

El linaje de COM clásico y WinRTSobre el fundamento compartido del contrato binario COM hecho de IUnknown y vtables se sostienen, lado a lado, el mundo COM clásico de OLE y ActiveX de la década de 1990 y el mundo de WinRT a partir de 2012, con WinUI y el Windows App SDK encima; los dos no se excluyen, son continuosContrato binario COM (IUnknown, vtable)COM clásico (OLE, ActiveX, COM propio)WinRT (IInspectable, .winmd)WinUI / Windows App SDKPueden coexistir sobre el mismo fundamento

Figura 1: COM clásico y WinRT no son mundos separados, sino dos generaciones, antigua y nueva, sobre el mismo contrato binario.

Por eso este artículo no es meramente «una introducción a una API nueva». Es un artículo que confirma dónde el conocimiento de COM que ya tiene se aplica en el desarrollo para Windows de 2026.

3. IInspectable encima de IUnknown

El fundamento inalterado y los tres métodos añadidos

La especificación oficial del sistema de tipos de WinRT estipula que cada interfaz WinRT exige implícitamente IInspectable, e IInspectable exige IUnknown. Lo que IUnknown define son, como siempre, los tres métodos QueryInterface, AddRef y Release.12

Encima, IInspectable añade los tres métodos siguientes.13

Método Papel
GetIids Devuelve la lista de IID de las interfaces que implementa este objeto
GetRuntimeClassName Devuelve el nombre de tipo WinRT plenamente calificado (como Windows.Storage.StorageFile) como HSTRING
GetTrustLevel Devuelve el nivel de confianza del objeto
IInspectable asentado sobre IUnknownCada interfaz WinRT exige IInspectable, e IInspectable exige IUnknown. IUnknown proporciona QueryInterface, AddRef y Release; IInspectable proporciona GetIids, GetRuntimeClassName y GetTrustLevel; y los métodos de cada interfaz WinRT se asientan encimaIUnknown (QI, AddRef, Release)IInspectable (GetIids, nombre de tipo, nivel de confianza)Métodos de cada interfaz WinRT

Figura 2: Un objeto WinRT apila los tres métodos de IInspectable sobre los tres métodos de IUnknown, y las API individuales se asientan encima.

A partir de un nombre de tipo se consultan las definiciones de métodos, propiedades y eventos

Lo que importa no es el número de métodos añadidos, sino la capacidad de conectar un nombre de tipo con metadatos.

En COM clásico, el modo estándar de conocer la identidad de un objeto en tiempo de ejecución era «conocer el IID y preguntar mediante QueryInterface». Para los lenguajes de scripting había una vía distinta, IDispatch.

En WinRT, el nombre de tipo obtenido de GetRuntimeClassName se puede resolver frente a los metadatos .winmd de la sección siguiente. De ahí se obtiene la definición completa de sus métodos, propiedades y eventos. La propia especificación dice que poder obtener un nombre de tipo WinRT resoluble mediante metadatos «enables language projection».12

De GetRuntimeClassName a la proyección de lenguajeCuando el llamador invoca GetRuntimeClassName en un objeto, vuelve el nombre de tipo WinRT plenamente calificado; resolver ese nombre de tipo frente a Windows Metadata da la definición completa del tipo, y eso es lo que hace posible la proyección a cada lenguajeObjeto WinRTNombre de tipo (GetRuntimeClassName)Resolver la definición de tipo en .winmdLa proyección de lenguaje se vuelve posible

Figura 3: «El nombre de tipo está disponible en tiempo de ejecución, y el nombre de tipo lleva a los metadatos» es el corazón del mecanismo de WinRT.

Separar «herencia» y «requires» en las interfaces definidas por el usuario

También hay una diferencia que quien está acostumbrado a COM debe notar. El sistema de tipos de WinRT no tiene herencia entre interfaces definidas por el usuario. Una derivación como IFileSystemBindData2 : IFileSystemBindData de COM clásico está deliberadamente ausente; en su lugar se expresa con la declaración «la interfaz A exige la interfaz B (requires)».121

Esto es un asunto distinto de la cadena ABI base IUnknownIInspectable vista hasta ahora. Esa base permanece como fundamento de cada interfaz WinRT.

Los contratos definidos por el usuario se desplazaron hacia una forma más laxa, que no depende del diseño de herencia de la vtable. Al mismo tiempo, las llamadas en sí siguen pasando por la vtable. Importa no confundir la forma de escribir los contratos con el mecanismo de la llamada.

4. Lo que resolvió .winmd — el infierno de enlace de la información de tipos

La dificultad de COM clásico estaba en «cómo distribuir la información de tipos»

En la práctica de COM clásico, más esfuerzo iba a cómo distribuir la información de tipos que a implementar las interfaces mismas.

Consumidor Vía para entregar la información de tipos
C++ Escribir el contrato en IDL, generar encabezados y proxy/stub con MIDL
VB6, scripting Distribuir una biblioteca de tipos (TLB)
.NET Construir un ensamblado de interop aparte

Una TLB tiene restricciones de tipos orientadas a automatización: cierta información que se puede escribir en IDL no cabe en una TLB. Y como cada lenguaje tiene su propia vía, cuando cualquiera de ellas queda desfasada aparecen desajustes de tipos. Tratamos estas dificultades en el artículo sobre bibliotecas de tipos y dscom y el artículo sobre compatibilidad retroactiva de interfaces DLL y COM.

.winmd es el contrato común que lee cada lenguaje

La respuesta de WinRT es Windows Metadata (.winmd). Las API se describen como metadatos legibles por máquina, y las herramientas y las proyecciones de lenguaje los leen para generar una proyección para cada lenguaje.3

Windows incluye los metadatos de cada API WinRT que proporciona el sistema y también ofrece API que resuelven espacios de nombres y tipos en tiempo de ejecución. El Windows SDK contiene una copia para el tiempo de compilación. Los terceros también pueden participar en la proyección de lenguaje por el mismo mecanismo que las API del sistema, adjuntando un .winmd a su propio componente WinRT.3

Lo que cambió aquí es la forma en que se distribuye la información de tipos. En lugar de TLB, encabezados y ensamblados de interop dispersos por lenguaje, un único .winmd lo leen ahora las proyecciones de cada lenguaje.

Dicho esto, IDL no se ha vuelto innecesario. Cuando se crea un componente WinRT, el contrato se sigue describiendo en IDL (MIDL 3.0, modernizado para WinRT), y el compilador MIDL genera el .winmd.14

El conducto de producción de un componente WinRTEl contrato de un componente WinRT se sigue escribiendo en IDL, es decir, MIDL 3.0, y el compilador MIDL lo compila en un .winmd. Lo que se distribuye es este .winmd, y la herramienta de proyección de cada lenguaje, como cppwinrt.exe o cswinrt.exe, lo lee para generar la proyección. Lo que se sustituyó no es IDL, sino la forma en que se distribuye la información de tiposEscribir el contrato (IDL, MIDL 3.0)Compilador MIDL.winmd (información de tipos distribuida)Generar la proyección de cada lenguaje

Figura 4: El punto de entrada del contrato (IDL) sigue en servicio; el punto de salida, la información de tipos distribuida, se unificó en .winmd.

El mismo formato de archivo que .NET, pero no un runtime administrado

El formato físico de un .winmd usa la especificación ECMA-335, la misma que un ensamblado CLR. Las reglas sobre qué combinaciones de datos son válidas difieren, no obstante, de las de los ensamblados CLR. Tomar prestado el formato y necesitar el CLR para ejecutarse son dos cosas distintas.3

Aquí hay que leer por separado las API del sistema y los componentes de terceros.

Objeto Relación entre el .winmd y la implementación
API WinRT que proporciona el sistema El .winmd es metadatos puros, sin código ejecutable. La implementación vive en DLL nativas del sistema operativo, y no se necesita el CLR para ejecutarla
Componentes WinRT de terceros El .winmd también puede contener código de implementación. En un componente administrado (escrito en C#) que contiene MSIL, se requiere el runtime de .NET correspondiente para ejecutarlo

Como un .winmd parece un ensamblado de .NET al abrirlo en una herramienta, es fácil caer en la idea falsa «WinRT = administrado». Pero el contenido de un .winmd que proporciona el sistema es un contrato para interfaces COM.3

Separación del .winmd y la implementaciónUn .winmd es un archivo de metadatos que toma prestado el formato físico ECMA-335; los que proporciona el sistema son contratos sin código ejecutable, y la implementación de las API WinRT que proporciona el sistema vive en DLL nativas del sistema operativo. Por esta separación, aunque un .winmd parezca un ensamblado de .NET, no se necesita el CLR para ejecutar las API WinRT del sistema (el .winmd de un componente administrado de terceros contiene MSIL y exige el runtime de .NET)Proporcionado por el sistema: sin códigoLas definiciones de tipo corresponden a la implementación.winmd (contrato, formato ECMA-335)DLL nativa del sistema operativo (implementación)Las API del sistema no necesitan CLR

Figura 5: En las API WinRT que proporciona el sistema, el .winmd es el contrato y la implementación una DLL nativa del sistema operativo. El formato parece .NET, pero la ejecución es COM nativo.

Información de tipos de COM clásico frente a .winmdEn COM clásico la vía de información de tipos estaba partida por lenguaje, de IDL a encabezados de C++, de bibliotecas de tipos a VB6 y scripting, de ensamblados de interop a .NET, lo que causaba incoherencias, mientras que en WinRT un único .winmd lo leen en común las proyecciones de cada lenguajeCOM clásico: una vía por lenguajeIDL a encabezados de C++TLB a VB6 y scriptingEnsamblado de interop a .NET.winmd (metadatos únicos)Leído en común por la proyección de cada lenguaje

Figura 6: .winmd plegó la dispersión de la información de tipos por lenguaje en «un archivo de metadatos que todos leen».

Las restricciones no desaparecieron; se desplazaron a un eje de proyectabilidad

Para quien conoce COM clásico, en una frase: .winmd es «la biblioteca de tipos, rehecha». Piénselo como el papel que una TLB intentaba desempeñar, rediseñado desde el principio como única fuente de verdad compartida por todos los lenguajes, sobre el formato acreditado ECMA-335, y su lugar queda claro.

Las restricciones, no obstante, no se han ido. Las restricciones orientadas a automatización de las TLB fueron sustituidas por las restricciones del sistema de tipos propio de WinRT, cuyo eje es «poder proyectarse con seguridad a cada lenguaje». La ausencia de herencia entre interfaces definidas por el usuario, vista en la sección 3, es un ejemplo.12

En consecuencia, un contrato COM/IDL existente no necesariamente se puede llevar a WinRT tal cual. Puede que haya que rediseñar la API.

Sustitución de las restricciones de biblioteca de tipos por las del sistema de tipos WinRTLas restricciones de expresividad orientadas a automatización de la TLB (biblioteca de tipos) no las eliminó .winmd, sino que las sustituyó por las restricciones del sistema de tipos propio de WinRT, cuyo eje es la proyección segura a cada lenguaje. Un ejemplo es la ausencia de herencia de interfaces definidas por el usuario; un contrato COM/IDL existente no necesariamente se puede retomar tal cual, y puede que haya que rediseñar la APISustituidas porRestricciones de TLB (orientadas a automatización)Restricciones del sistema de tipos WinRT (eje de proyectabilidad)Ejemplo: sin herencia definida por el usuarioLos contratos COM existentes pueden necesitar rediseño

Figura 7: Las restricciones de TLB no «desaparecieron»; se sustituyeron por otras restricciones cuyo eje es la proyectabilidad a cada lenguaje.

5. C++/WinRT y C#/WinRT son proyecciones, no «contenedores»

Mostrar el contrato común a cada lenguaje en su forma natural

Una vez que hay un contrato común en forma de .winmd, la vista para cada lenguaje se puede generar automáticamente con herramientas. Esto es una proyección de lenguaje (language projection). Expone las API WinRT en el idioma de cada lenguaje y oculta los detalles de COM, ofreciendo una experiencia de programación natural para ese lenguaje.411

Las proyecciones que Microsoft admite actualmente son las dos siguientes.4

Proyección Lo que genera Características
C++/WinRT cppwinrt.exe genera encabezados de proyección C++ a partir de .winmd Una proyección basada en archivos de encabezado en C++17 estándar. No se necesitan extensiones de lenguaje como las de C++/CX; el sucesor de C++/CX y WRL11
C#/WinRT (CsWinRT) cswinrt.exe genera código C# a partir de .winmd y lo convierte en un ensamblado de interop La proyección para .NET. Una cadena de herramientas independiente del runtime1516

La historia del lado de C# es un poco confusa, así que la desenredamos. Hasta .NET Core 3.x, el runtime de .NET tenía compatibilidad integrada para consumir WinRT/winmd. En .NET 5 se retiró esa compatibilidad integrada y el papel pasó a C#/WinRT.16 Las API WinRT no dejaron de ser usables; cambió dónde se trata la proyección.

Hoy, cuando se indica en C# un TFM como net8.0-windows10.0.19041.0, los ensamblados de proyección del Windows SDK se referencian automáticamente.10

Generación de las proyecciones para cada lenguaje a partir de .winmdCuando cppwinrt.exe lee el .winmd único, genera encabezados de proyección C++17; cuando cswinrt.exe lo lee, genera un ensamblado de interop C#; y las API WinRT se exponen de una forma que sigue el idioma de cada lenguaje.winmd (el contrato de API)cppwinrt.exe a encabezados C++17cswinrt.exe a ensamblado de interop C#Invocable en el idioma de C++Invocable en el idioma de C#

Figura 8: Una proyección no es un contenedor escrito a mano; las herramientas la generan de forma mecánica a partir del contrato (.winmd).

Aunque se genere, la llamada en sí sigue siendo COM

Este artículo dice «proyección» en lugar de «contenedor» para subrayar que es un mecanismo derivado de forma mecánica de los metadatos, no una capa de traducción que las personas mantienen API por API. Cualquier API presente en el .winmd se puede hacer usable desde el principio en cada lenguaje admitido.

Y desde el lenguaje que se llame, lo que ocurre bajo la proyección es la misma llamada COM. Por ejemplo, cuando se escribe await picker.PickSingleFolderAsync() en C#, la proyección tiende un puente entre el IAsyncOperation de WinRT y el mundo del Task de .NET. Aun así, en el lado ABI se usan llamadas a métodos de vtable y HRESULT. Por eso los errores aparecen en forma de excepciones COM (un HRESULT como 0x80070005).

Las capas del código C# a la API WinRT del sistema operativoEl código C# o C++ de la aplicación se convierte, a través de la proyección de lenguaje, en llamadas ABI de WinRT, es decir, llamadas de vtable sobre IInspectable, y llega a la API WinRT implementada por el sistema operativo. La proyección solo oculta los detalles de COM; la llamada en sí es COMCódigo de la aplicación (C#, C++)Proyección de lenguajeABI de WinRT (vtable de IInspectable)Implementación del sistema operativo de la API WinRT

Figura 9: Lo que oculta la proyección son los «detalles» de COM, no COM en sí.

Conocer esta estructura permite partir un incidente en dos capas. Un desajuste de versión del código generado o un TFM que falta es la capa de proyección; los HRESULT, los apartments y el recuento de referencias son la capa ABI. En esta última, la experiencia de desarrollo COM sirve de inmediato.

6. Dónde se atascan las aplicaciones de escritorio — HWND, identidad, apartments

Primero separar «poder referenciar la API» de «las condiciones para que funcione»

La mayoría de las API WinRT se pueden llamar desde aplicaciones de escritorio WPF, WinForms y Win32.5 El punto de entrada para llamarlas difiere entre C# y C++ como sigue.10

Entorno Configuración inicial
C#/.NET 6 o posterior Establecer TargetFramework en un TFM con versión de sistema operativo Windows, como net8.0-windows10.0.19041.0
C++ Añadir el paquete NuGet Microsoft.Windows.CppWinRT y usar C++/WinRT con C++17 o posterior

Poder referenciar las API, no obstante, no significa que cada API funcione tal cual. Compruebe los tres puntos siguientes. Las condiciones de registro de las notificaciones toast se tratan por separado en la sección 9.

Qué comprobar Remedio principal
¿Necesita una ventana en la que mostrarse? Pasar un HWND mediante el interop COM que corresponda a la interfaz
¿Necesita identidad de paquete? Conceder identidad con MSIX o un paquete con ubicación externa
¿Está el subproceso inicializado para WinRT? En código nativo, inicializar indicando STA/MTA. En C# el runtime suele encargarse

Punto de atasco 1: pasar un HWND a una interfaz como los selectores

Algunos selectores, cuadros de diálogo y la interfaz de Compartir esperan un CoreWindow de UWP como superficie de visualización. Una aplicación de escritorio no tiene CoreWindow, así que el HWND de la ventana propietaria debe pasarse de forma explícita antes de mostrar el objeto.6

El punto de entrada que se usa para selectores y similares es una interfaz COM llamada IInitializeWithWindow. Hereda de IUnknown y proporciona una ventana propietaria a los objetos WinRT usados en aplicaciones de escritorio.17

En C#, primero obtenga el HWND según el marco de interfaz en uso.186

Ventana propietaria Cómo obtener el HWND
Una Window de WinUI WinRT.Interop.WindowNative.GetWindowHandle
Una ventana WPF WindowInteropHelper
Un formulario WinForms La propiedad Handle del formulario

A continuación, páselo al selector con WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd) y solo entonces muéstrelo. En C++/WinRT, obtenga el objeto como as<IInitializeWithWindow>() y luego llame a Initialize(hwnd). Si se omite la inicialización, lanza o falla en silencio.1819

Inicializar un objeto WinRT moderno mediante QueryInterface a una interfaz COM clásica: que este puente sea la práctica oficial es otro lugar en el que WinRT muestra que es COM.

La interfaz de Compartir usa otra interfaz. Para DataTransferManager no se usa IInitializeWithWindow. Se usa el IDataTransferManagerInterop dedicado y se pasa el HWND a ShowShareUIForWindow.6

Los selectores más nuevos del Windows App SDK son otra vía distinta. Microsoft.Windows.Storage.Pickers recibe un WindowId en el constructor, así que el patrón InitializeWithWindow es innecesario. No son, no obstante, API que se puedan usar solo con un ajuste de TFM. Además de añadir el Windows App SDK, una aplicación sin empaquetar necesita que el runtime se despliegue e inicialice en las máquinas de destino.19

Pasos para mostrar un selector desde una aplicación de escritorioTras crear un selector, una aplicación de escritorio que llama PickSingleFolderAsync tal cual obtiene una excepción o un fallo silencioso, así que primero hay que obtener el HWND de la ventana propietaria y pasarlo mediante Initialize de IInitializeWithWindow antes de mostrar el selectorSelector (WinRT)Aplicación de escritorioSelector (WinRT)Aplicación de escritorioalt[Mostrar sin pasar un HWND][Pasar primero el HWND]CrearPickSingleFolderAsyncExcepción o fallo silenciosoEstablecer el HWND mediante IInitializeWithWindowPickSingleFolderAsyncSe muestra el selector

Figura 10: Cubrir el hueco de «no hay CoreWindow en el escritorio» pasando un HWND de forma explícita es la práctica oficial.

Punto de atasco 2: algunas API exigen identidad de paquete

Algunas API WinRT, como el historial de notificaciones toast (ToastNotificationHistory), las listas de accesos directos y los destinos de Compartir, solo funcionan en aplicaciones que tienen identidad de paquete (aplicaciones empaquetadas). Llamarlas desde una aplicación sin empaquetar distribuida con un instalador tradicional falla.7

Hay dos remedios. Empaquetar la aplicación con MSIX, o usar un «paquete con ubicación externa» (un sparse package), que concede una identidad y conserva el instalador existente.20 Qué API exigen identidad se puede comprobar en la lista oficial.7

Dos vías hacia las API que exigen identidad de paqueteLlamar una API WinRT que exige identidad de paquete desde una aplicación sin empaquetar falla, así que dé a la aplicación identidad de paquete empaquetándola con MSIX o con un paquete con ubicación externa, que solo concede el identificador y conserva el instalador existenteQuerer usar una API que exige identidadEmpaquetar con MSIXPaquete con ubicación externaObtener identidad de paqueteEl historial de notificaciones etc. funciona

Figura 11: El remedio para las API que exigen identidad es una elección entre dos: «MSIX» o «instalador existente + concesión de un identificador».

Punto de atasco 3: comprobar la inicialización del subproceso y STA/MTA

Un subproceso que trata objetos WinRT debe inicializarse para WinRT de antemano. En código nativo, use RoInitialize o winrt::init_apartment e indique el modelo de concurrencia, STA o MTA. En aplicaciones C# WPF/WinForms, el runtime suele encargarse de la inicialización.8

RoInitialize es el punto de entrada de la generación WinRT dentro del mismo marco que CoInitializeEx de COM. La propia documentación de CoInitialize indica llamar a RoInitialize o Windows::Foundation::Initialize en su lugar cuando se usa Windows Runtime.21

El subproceso de interfaz de WPF/WinForms es un STA; los objetos de interfaz deben tocarse en el subproceso de interfaz; las esperas bloqueantes en un STA invitan a interbloqueos. El pensamiento tratado en el artículo de STA/MTA se traslada sin cambios a las API WinRT.

Algunas API no se pueden usar ni con HWND e identidad en su sitio

Los remedios hasta aquí conciernen a API que tienen un punto de entrada para uso de escritorio. Las API que dependen de CoreWindow o ApplicationView en sí no se pueden usar desde aplicaciones de escritorio. En ese caso, en lugar de añadir inicialización, busque una API alternativa.57

Ramificación de los puntos de atasco al llamar API WinRT desde el escritorioPrimero confirmar que el subproceso está inicializado para WinRT (en código nativo, indicar STA/MTA de forma explícita con RoInitialize o similar; en C#, el runtime suele encargarse). Luego, si la API WinRT que se quiere llamar es una interfaz que presupone un CoreWindow, pasar un HWND mediante IInitializeWithWindow o usar los selectores nuevos; si exige identidad de paquete, conceder un identificador con MSIX o un paquete con ubicación externa; las API que dependen de CoreWindow o ApplicationView en sí no se pueden usar en el escritorio, así que hay que buscar una API alternativa; y el gran resto de API se pueden llamar tal cual solo con el ajuste de TFM o C++/WinRTInterfazIdentidad exigidaDepende de CoreWindow en síTodo lo demásInicialización del subproceso¿Qué tipo de API es?Nativo: explícito; C#: suele ser automáticoHWND o los selectores nuevosMSIX o conceder un identificadorBuscar una API alternativaInvocable tal cual

Figura 12: Con la inicialización del subproceso como requisito previo (explícita en código nativo, en C# normalmente dejada al runtime), los puntos de atasco caen en tres familias, cada una con un remedio tipo.

Correspondencia de la inicialización del subproceso entre COM y WinRTEn COM clásico un subproceso se inicializa con CoInitializeEx indicando STA o MTA, mientras que en WinRT se inicializa con RoInitialize indicando el mismo modelo de concurrencia STA o MTA. La documentación de CoInitialize también indica llamar a RoInitialize al usar WinRT, y el concepto de apartment es compartidoCOM clásico: CoInitializeExApartment (STA / MTA)WinRT: RoInitializeEl subproceso de interfaz es STA; cuidado con las esperas

Figura 13: El nombre de la API de inicialización cambió, pero se sigue usando el mismo concepto de apartment.

7. Lo que sostiene a WinUI — el desarrollo a la vista no cambia el contrato

Pasar al desarrollo a la vista no es un cambio del contrato binario

En el verano de 2025, Microsoft anunció oficialmente, como enfoque por fases, su política de llevar el desarrollo principal de WinUI a la vista en GitHub. Hay cuatro etapas: aumentar la frecuencia de actualización del espejo, hacer posibles las compilaciones locales, aceptar contribuciones de la comunidad una vez que las pruebas estén en su sitio y, por último, hacer de GitHub el hogar principal del desarrollo.22

A la fecha de publicación de este artículo (finales de agosto de 2026), la documentación oficial también afirma con claridad que WinUI está «built in the open». El avance cotidiano de la ingeniería se puede seguir ahora en el repositorio público.9

Los desarrolladores que han seguido los relevos generacionales de WinForms a WPF a UWP a WinUI sienten con naturalidad «¿el marco vuelve a cambiar?». Pero los marcos de interfaz que no han dejado de cambiar y el fundamento debajo hay que mirarlos por separado. Win32 y COM, y desde 2012 el ABI de WinRT, se han quedado en el mismo sitio.

Las ventanas WinUI también las sostiene un HWND

WinUI es un conjunto de API WinRT proporcionado como parte del Windows App SDK.923 Su Microsoft.UI.Xaml.Window es una ventana sostenida por un HWND, que sustituye el modelo de ventana basado en CoreWindow de la generación UWP. El tutorial oficial de interop también empieza por obtener el identificador de ventana.24

En otras palabras, una aplicación WinUI es una aplicación Win32 en la que un árbol de objetos que sigue el contrato IInspectable se ejecuta sobre una ventana HWND. El conocimiento de QueryInterface, recuento de referencias y apartments adquirido a través de COM, y el conocimiento de HWND y bucles de mensajes adquirido a través de Win32, se aplican ambos de inmediato a la resolución de problemas de WinUI.

Relevos generacionales de los marcos de interfaz y el fundamento inalteradoLos marcos de interfaz WinForms, WPF, XAML de UWP y WinUI han encadenado generaciones, pero WinForms y WPF se asientan directamente sobre el fundamento de Win32 y COM, mientras que el XAML de UWP y WinUI se asientan sobre el mismo fundamento de Win32 y COM a través del ABI de WinRT. Lo que cambió de generación es la capa superior; el contrato debajo no ha cambiadoRelevos generacionales de los marcos de interfazWinForms, WPFXAML de UWPWinUI (actual)ABI de WinRT (desde 2012)Fundamento inalterado (Win32 + COM)

Figura 14: Lo que subió y bajó fue la capa del marco. WinForms y WPF se asientan directamente sobre Win32 + COM; UWP y WinUI se asientan sobre el mismo fundamento a través del ABI de WinRT.

Las capas que sostienen una aplicación WinUIEl XAML y los controles de WinUI se proporcionan como parte del Windows App SDK, se ejecutan sobre el ABI de WinRT, es decir, el contrato IInspectable, y debajo está el fundamento de COM y el HWND de Win32. Bajo los relevos generacionales de los marcos de interfaz, este contrato subyacente no ha cambiadoWinUI (XAML, controles)Windows App SDKABI de WinRT (IInspectable)COM + Win32 (HWND)

Figura 15: Debajo de WinUI está el ABI de WinRT, y más abajo aún COM clásico y Win32. Solo cambió el apilado; el fundamento es el mismo.

Mezclar con XAML Islands: comprobar las restricciones de cada generación

La estrategia de «mezclar solo controles WinUI en pantallas WPF/WinForms existentes» hay que evaluarla por separado para cada generación de XAML Islands.

Generación Situación al usarla desde WPF/WinForms
XAML Islands de generación UWP Existen controles contenedor del Windows Community Toolkit. Pero las versiones WPF/WinForms se detuvieron en la generación .NET Core 3.x y no se admiten en el .NET actual25
Generación WinUI 3 Se puede hospedar desde WPF, WinForms y Win32 con DesktopWindowXamlSource del Windows App SDK. Pero no hay controles contenedor cómodos como los de la generación UWP, y recae en usted la carga de implementación y validación de tratar la API de hospedaje de forma directa26

Si planea una mezcla gradual, es más seguro no presuponer solo la mezcla a nivel de control. Organizar el plan en torno al uso de API WinRT a nivel de funcionalidad (sección 6) o a una separación a nivel de pantalla o de proceso es el enfoque más sólido a fecha de 2026.

8. Implicaciones para las aplicaciones de negocio — la migración completa y el uso selectivo son problemas distintos

«WinRT es un mundo nuevo y separado, así que usarlo significa tirar los activos existentes y reconstruir.» Esta idea falsa, que aparece en proyectos de desarrollo a medida, se disuelve una vez que se parte en dos.

Los activos COM existentes y WinRT pueden coexistir

A una aplicación WPF que usa automatización COM de Excel, hospeda controles ActiveX y llama a componentes COM internos se le pueden añadir API WinRT. No es ninguna acrobacia especial, porque se combinan funciones sobre el mismo fundamento COM.

Para las notificaciones toast, por ejemplo, establezca el TFM para referenciar la API y cumpla las condiciones de registro para mostrar notificaciones. Para que esto último no se olvide, la sección 9 lo expone vía por vía. En C++/WinRT, las interfaces WinRT y COM clásicas se pueden tratar con los mismos mecanismos, winrt::com_ptr y winrt::implements.21

Estimar por separado la renovación de interfaz y la adición de funciones

«¿Migrar la interfaz por completo a WinUI?» y «¿Usar API WinRT solo donde haga falta?» son decisiones que difieren en escala, duración y riesgo.

Una migración completa de interfaz es un problema de elección de marco que depende de los activos de pantalla, los controles de terceros y la organización de desarrollo. Tratamos esa decisión en Cómo elegir entre WinForms, WPF y WinUI. El uso selectivo de API WinRT, en cambio, es una mejora pequeña que se puede empezar hoy en una aplicación existente.

Para un proyecto que solo quiere notificaciones toast, no hace falta estimar una migración completa de interfaz. A la inversa, no hace falta aplazar el uso de API WinRT «porque no vamos a migrar a WinUI».

La migración completa y el uso selectivo son decisiones distintasUna migración completa de interfaz a WinUI es una gran decisión de elección de marco que depende de los activos de pantalla y de la organización, mientras que el uso selectivo de API WinRT es una decisión pequeña que se puede añadir hoy a una aplicación WPF o WinForms existente con un ajuste de TFM o similar; considere las dos por separado sin confundirlasQuerer usar funciones nuevas de WindowsMigración completa de interfaz (decisión grande)Uso selectivo de API WinRT (decisión pequeña)Depende de los activos de pantalla y de la organizaciónSe puede añadir a la aplicación existente hoy

Figura 16: Hay dos caminos hacia «funciones nuevas», y confundirlos desvía tanto la estimación como la decisión.

9. Tabla de decisión — la versión WinRT de conservar, envolver, reemplazar

Elegir solo los cambios que cada situación necesita

Como extensión de la tabla de decisión de ActiveX, aquí hay una guía de decisión por situación del lado WinRT.

Situación Recomendación Motivo
Querer usar API WinRT como toast, Compartir o Bluetooth desde WPF/WinForms Uso selectivo mediante un ajuste de TFM (o añadiendo C++/WinRT) Invocable hoy sin migración de interfaz. Pero la interfaz de Compartir pasa por IDataTransferManagerInterop (sección 6), y para toast vea el complemento bajo la tabla106
Excepción o fallo silencioso en un selector o un cuadro de diálogo Pasar un HWND mediante IInitializeWithWindow. Para código nuevo, los selectores nuevos basados en WindowId (exige añadir el Windows App SDK) La práctica oficial de cubrir el diseño basado en CoreWindow con un HWND619
El historial de notificaciones, las listas de accesos directos y similares no funcionan Conceder identidad de paquete con MSIX o un paquete con ubicación externa Las API que exigen identidad presuponen empaquetado720
Activos COM/ActiveX/OLE existentes No descartarlos. Decidir conservar, envolver o reemplazar por activo No son excluyentes de WinRT; coexisten sobre el mismo fundamento (sección 8)
Interfaz de una aplicación de escritorio nueva Evaluar WinUI como primer candidato (WPF sigue vigente) El desarrollo a la vista aclaró la dirección de inversión. Debajo está el ABI de WinRT229
Mezclar controles WinUI en pantallas WPF/WinForms existentes Evaluar con cautela, previendo la carga de implementación de la falta de contenedores Los Islands de generación UWP se detuvieron en .NET Core 3.x; la generación WinUI 3 solo ofrece la API de hospedaje2526
Un plan que presupone «WinRT terminó junto con UWP» Corregir el presupuesto WinRT es el fundamento de API actual, invocable desde el escritorio5

Las notificaciones toast no aparecen solo con un ajuste de TFM

El ajuste que hace la API invocable y el registro que hace aparecer una notificación son dos cosas distintas. Cuando «la compilación funciona pero no aparece ninguna notificación», compruebe no solo el código de llamada, sino la vía en uso, la forma de empaquetado y si el proceso está elevado.

Al usar el ToastNotificationManager clásico

Para una aplicación sin empaquetar y sin identidad de paquete, el requisito previo es registrar un acceso directo del menú Inicio con un AppUserModelID (AUMID) asignado. Sin él no se pueden mostrar toasts.27

Una aplicación empaquetada con MSIX o similar no necesita este registro manual, porque la identidad de paquete proporciona el AUMID.

Al usar AppNotificationManager del Windows App SDK

En esta vía recomendada actualmente se exigen añadir el Windows App SDK y llamar a Register() al arrancar. Más allá, las condiciones se ramifican según la forma de empaquetado.28

Forma de empaquetado Qué comprobar además
Sin empaquetar El runtime del Windows App SDK debe desplegarse en cada PC de destino. Register() realiza el registro del servidor COM
Empaquetada con MSIX El registro automático de Register() no funciona. Declare un activador COM en Package.appxmanifest

Además, en la vía del Windows App SDK las notificaciones desde un proceso elevado a administrador no están admitidas. Show falla en silencio sin lanzar. En una aplicación que necesita elevación, plantee separar las notificaciones en un proceso no elevado.28

Los detalles de implementación en torno al registro se tratan en la guía de implementación de iconos de bandeja y notificaciones.

Las dos vías de las notificaciones toast y el registro que cada una necesitaPara mostrar una notificación toast desde una aplicación de escritorio, la vía clásica ToastNotificationManager se ramifica según si la aplicación está empaquetada: una aplicación sin empaquetar debe pasar por el registro de un acceso directo del menú Inicio con un AppUserModelID asignado, mientras que para una empaquetada la identidad de paquete proporciona el AppUserModelID. En la vía AppNotificationManager del Windows App SDK, además de añadir el SDK y llamar a Register al arrancar, una aplicación sin empaquetar debe desplegar el runtime en cada PC de destino y una aplicación empaquetada con MSIX debe declarar un activador COM en el manifiesto antes de poder seguir. La vía del Windows App SDK se ramifica además según si el proceso está elevado: las notificaciones desde un proceso elevado a administrador no están admitidas y Show falla en silencio sin lanzar, así que en esta vía una notificación solo se muestra para un proceso no elevado, y una aplicación que necesita elevación separa las notificaciones en un proceso no elevadoNoNoNoQuerer mostrar un toastClásico: ToastNotificationManagerWASDK: AppNotificationManager¿Empaquetada?Registrar un acceso directo AUMIDLa identidad proporciona el AUMIDAñadir el SDK + Register()¿Empaquetada?Desplegar el runtimeDeclarar COM en el manifiestoSe muestra la notificación¿Proceso elevado?No admitido: Show falla en silencioSeparar las notificaciones en un proceso no elevado

Figura 17: En cualquiera de las dos vías, la notificación aparece solo después de satisfacer los requisitos previos de la forma de empaquetado. En la vía del Windows App SDK, incluso con todos los requisitos previos en su sitio, no se muestra nada desde un proceso elevado.

Por último, volver a conservar, envolver, reemplazar

¿Necesita una función nueva, o quiere renovar la interfaz en sí? Volver a esta pregunta permite decidir por separado sobre el uso de WinRT y sobre el reemplazo de los activos existentes.

Flujo de decisión sobre cómo encajan los activos existentes y WinRTPartiendo de una aplicación de escritorio existente, un flujo de decisión por pasos: si no se necesita una función nueva de Windows, conservar; si se necesita una función, envolver con uso selectivo de API WinRT (atentos a los puntos de atasco de la sección 6 de HWND, identidad de paquete e inicialización del subproceso); y solo cuando la interfaz en sí necesite renovarse, plantear reemplazarla por WinUIEl estado actual bastaUna función nuevaRenovación de interfazAplicación de escritorio existente¿Qué se necesita?Conservar (mantener tal cual)Envolver (uso selectivo de API WinRT)Reemplazar (evaluar WinUI)Atención a HWND, identidad, inicialización (sección 6)

Figura 18: La misma estructura «conservar, envolver, reemplazar» que la tabla de decisión de ActiveX se aplica de inmediato del lado WinRT.

10. Resumen

WinRT no es un entorno de ejecución nuevo, cortado de COM. Es un fundamento de API que combina el contrato binario de COM con metadatos y proyecciones a cada lenguaje.

Elemento Su papel según este artículo
IUnknown e IInspectable Construidos sobre QueryInterface y el recuento de referencias, añadiendo tres métodos que devuelven el nombre de tipo y más
.winmd El contrato compartido por cada lenguaje, a partir del cual un nombre de tipo lleva a una definición. Toma prestado el formato ECMA-335, pero los que proporciona el sistema no contienen código ejecutable
C++/WinRT y C#/WinRT Generan una API para cada lenguaje a partir del contrato. Desde .NET 5, la proyección de C# es una cadena de herramientas independiente del runtime

Aunque la superficie sea una API C# o C++ natural, debajo hay llamadas COM a través de vtables y HRESULT. Por eso entender el paso de HWND en el escritorio, la identidad de paquete y STA/MTA con la inicialización del subproceso hace más fácil aislar los incidentes de WinRT.

El esfuerzo de llevar el desarrollo principal de WinUI a la vista se anunció en el verano de 2025 como un enfoque por fases. A la fecha de publicación de este artículo la documentación oficial también dice «built in the open», pero el contrato debajo, IUnknownIInspectable, no ha cambiado.229

La conclusión para las aplicaciones de negocio es la misma. Los activos COM/ActiveX existentes y WinRT pueden coexistir. No confundir una migración completa de interfaz con el uso selectivo de API WinRT es lo que protege la precisión de las estimaciones y las decisiones.

Los documentos compuestos de la década de 1990 vistos en el artículo OLE y el WinUI de hoy se sostienen sobre el mismo IUnknown. Conocer COM no es meramente «estar versado en lo legado». Es poder leer el fundamento de Windows hoy.

Artículos relacionados

Áreas de consultoría relacionadas

KomuraSoft LLC se ocupa del desarrollo de componentes COM y del mantenimiento, la modificación y el reemplazo de aplicaciones de negocio para Windows que incluyen activos COM. Puede consultarnos desde la etapa en la que el contenido de este artículo es exactamente el tema en juego: «queremos notificaciones toast y selectores sin dejar WPF», «llamar a una API WinRT se detuvo con una excepción», «queremos decidir si migrar a WinUI o aprovechar los activos existentes».

Referencias

  1. Microsoft Learn, Author COM components with C++/WinRT. Sobre que los componentes COM y las clases WinRT exponen ambos la funcionalidad a través de interfaces; la afirmación explícita «The Windows Runtime is based on COM»; que las interfaces COM clásicas derivan de IUnknown y las interfaces WinRT de IInspectable, que deriva de IUnknown; y que la derivación entre interfaces definidas por el usuario como IFileSystemBindData2 : IFileSystemBindData es una característica de COM clásico deliberadamente ausente del sistema de tipos de WinRT.  2 3 4 5 6

  2. Microsoft Learn, Consume COM components with C++/WinRT. Sobre programar en COM a través de interfaces en lugar de objetos; que esto también vale entre bastidores de las API WinRT, que son «an evolution of COM»; y tratar WinRT y COM clásico en el mismo estilo con el puntero inteligente COM winrt::com_ptr.  2 3 4

  3. Microsoft Learn, Windows Metadata (WinMD) files. Sobre que las API WinRT se describen en metadatos legibles por máquina llamados .winmd, usados por herramientas y proyecciones de lenguaje; que Windows incluye los metadatos de cada API WinRT que proporciona el sistema y ofrece API de resolución; que los terceros pueden participar en la proyección de lenguaje con el mismo formato; y que el formato físico es la especificación ECMA-335 (la misma que los ensamblados CLR), siendo los archivos WinMD que proporciona el sistema metadatos puros.  2 3 4 5

  4. Microsoft Learn, Windows Runtime (WinRT) language projections. Sobre que las proyecciones de lenguaje exponen las API WinRT en el idioma de cada lenguaje; que .winmd define las API WinRT y las proyecciones lo leen; y que las dos proyecciones que admite Microsoft son C++/WinRT (C++17 o posterior) y C#/WinRT (.NET).  2 3

  5. Microsoft Learn, WinRT APIs callable from a desktop app. Sobre que la mayoría de las API WinRT son usables desde aplicaciones de escritorio .NET y C++ nativo, siendo excepciones las clases diseñadas específicamente para UWP, como CoreDispatcher, CoreWindow y ApplicationView.  2 3 4

  6. Microsoft Learn, Display WinRT UI objects that depend on CoreWindow. Sobre que algunos selectores, ventanas emergentes y cuadros de diálogo dependen de CoreWindow; que CoreWindow no se admite en aplicaciones de escritorio; que las clases que implementan IInitializeWithWindow (o el equivalente IDataTransferManagerInterop) permiten establecer el HWND de la ventana propietaria antes de mostrarlas; y los pasos para WinUI 3, WPF y WinForms respectivamente.  2 3 4 5 6

  7. Microsoft Learn, WinRT APIs not supported in desktop apps. Sobre las dos familias de API WinRT inutilizables en aplicaciones de escritorio, las que dependen de funciones de interfaz exclusivas de UWP y las que exigen identidad de paquete (como ToastNotificationHistory y JumpList), y sobre que estas últimas solo se admiten en aplicaciones empaquetadas con MSIX.  2 3 4 5

  8. Microsoft Learn, RoInitialize function (roapi.h). Sobre que RoInitialize inicializa el subproceso actual para Windows Runtime con el modelo de concurrencia indicado (RO_INIT_SINGLETHREADED/RO_INIT_MULTITHREADED); que cada subproceso que activa y opera sobre objetos WinRT necesita una inicialización previa; y que una indicación contradictoria en un subproceso ya inicializado como MTA da RPC_E_CHANGED_MODE.  2

  9. Microsoft Learn, WinUI 3. Sobre que WinUI es el marco de interfaz nativo recomendado para aplicaciones de escritorio Windows nuevas, que se proporciona como parte del Windows App SDK, que se ejecuta en Windows 10 versión 1809 y posterior, y que se desarrolla a la vista.  2 3 4 5

  10. Microsoft Learn, Call Windows Runtime APIs in desktop apps. Sobre que indicar un TFM con versión de sistema operativo Windows (como net10.0-windows10.0.22621.0) en .NET 6 o posterior hace que se referencie el paquete de destino del Windows SDK de modo que se puedan llamar API WinRT, y que C++ usa C++/WinRT con el paquete NuGet Microsoft.Windows.CppWinRT y C++17 o posterior.  2 3 4

  11. Microsoft Learn, Introduction to C++/WinRT. Sobre que C++/WinRT es una proyección de lenguaje en C++17 enteramente estándar y moderno, implementada como biblioteca basada en archivos de encabezado; que es el sucesor recomendado de C++/CX y WRL; que WinRT se basa en API COM y está diseñado para accederse a través de proyecciones de lenguaje; que las proyecciones ocultan los detalles de COM; y que cppwinrt.exe genera encabezados de proyección a partir de .winmd.  2 3

  12. Microsoft Learn, The Windows Runtime (WinRT) type system. Sobre que cada interfaz WinRT exige implícitamente IInspectable e IInspectable exige IUnknown; que IUnknown define QueryInterface, AddRef y Release; los tres métodos que añade IInspectable, GetIids, GetRuntimeClassName y GetTrustLevel; que GetRuntimeClassName devuelve un nombre de tipo resoluble mediante metadatos, lo que habilita la proyección de lenguaje; y que la herencia entre interfaces definidas por el usuario está ausente del sistema de tipos de WinRT y se expresa con requires en su lugar.  2 3 4

  13. Microsoft Learn, IInspectable interface (inspectable.h). Sobre que IInspectable proporciona funcionalidad exigida por cada clase WinRT, hereda de IUnknown y tiene los tres métodos GetIids, GetRuntimeClassName y GetTrustLevel. 

  14. Microsoft Learn, Introduction to Microsoft Interface Definition Language 3.0. Sobre que MIDL 3.0 es una sintaxis concisa y moderna para declarar tipos WinRT, y que los contratos WinRT se siguen escribiendo en IDL, generando el compilador MIDL Windows Metadata (.winmd). 

  15. Microsoft Learn, C#/WinRT. Sobre que cswinrt.exe, incluido en el paquete NuGet C#/WinRT, procesa .winmd para generar código C# y compilarlo en un ensamblado de interop, y que esto se sitúa de la misma forma que C++/WinRT generando encabezados para C++. 

  16. Microsoft Learn, Built-in support for WinRT is removed from .NET. Sobre que la compatibilidad integrada con Windows Runtime se retiró de .NET en .NET 5 y se trasladó a la cadena de herramientas CsWinRT.  2

  17. Microsoft Learn, IInitializeWithWindow interface (shobjidl_core.h). Sobre que esta es la interfaz para proporcionar una ventana propietaria a objetos WinRT usados en aplicaciones de escritorio, que hereda de IUnknown y que tiene un método Initialize(HWND). 

  18. Microsoft Learn, Use WinRT COM interop classes in .NET. Sobre que algunos objetos WinRT, como selectores de archivos y cuadros de diálogo, necesitan un HWND antes de funcionar en una aplicación de escritorio, y que las clases C# seguras respecto al tipo WinRT.Interop.WindowNative y WinRT.Interop.InitializeWithWindow permiten la inicialización sin llamadas QueryInterface escritas a mano.  2

  19. Microsoft Learn, Tutorial: Open files and folders with pickers in WinUI. Sobre que los Windows.Storage.Pickers clásicos deben inicializarse con un HWND antes de mostrarlos cuando se usan en una aplicación de escritorio (WinUI 3), o de lo contrario lanzan o fallan en silencio; que las aplicaciones de escritorio WinUI 3 no tienen CoreWindow; y que los selectores nuevos del Windows App SDK (Microsoft.Windows.Storage.Pickers) reciben un WindowId en el constructor, de modo que el patrón InitializeWithWindow es innecesario.  2 3

  20. Microsoft Learn, Features that require package identity. Sobre que algunas funciones de Windows y API WinRT exigen identidad de paquete en tiempo de ejecución, y que una identidad se puede obtener no solo distribuyendo un paquete MSIX, sino también con un paquete con ubicación externa.  2

  21. Microsoft Learn, CoInitialize function (objbase.h). Sobre que CoInitialize inicializa la biblioteca COM como STA; que se espera que las aplicaciones nuevas llamen a CoInitializeEx; y que hay que llamar a RoInitialize o Windows::Foundation::Initialize en su lugar cuando se usa Windows Runtime. 

  22. GitHub, WinUI: Now Developing in the Open (microsoft/microsoft-ui-xaml Discussion #10700). El anuncio oficial de finales de julio de 2025. Sobre el enfoque por fases para abrir el repositorio de WinUI (aumentar la frecuencia de actualización del espejo, permitir compilaciones locales, aceptar contribuciones de la comunidad una vez que las pruebas estén en su sitio y, por último, hacer de GitHub el hogar principal del desarrollo).  2 3

  23. Microsoft Learn, Windows App SDK. Sobre que el Windows App SDK es el conjunto actual de bibliotecas de desarrollo de aplicaciones para Windows que incluye WinUI. 

  24. Microsoft Learn, Walkthrough: WinUI 3 app with Win32 interop. Sobre que la clase Window de WinUI se amplió para admitir ventanas de escritorio, y que Window en una aplicación de escritorio WinUI 3 la sostiene un identificador de ventana Win32 (HWND), de modo que el identificador se puede obtener y manipular con API Win32. 

  25. Microsoft Learn, Host UWP XAML controls in desktop apps (UWP XAML Islands). Sobre que los XAML Islands de generación UWP son el mecanismo para hospedar controles XAML de UWP en aplicaciones de escritorio WPF, WinForms y C++, y que su uso desde WPF/WinForms se limita a aplicaciones que tienen como destino .NET Core 3.x, no admitido en el .NET actual ni en .NET Framework.  2

  26. Microsoft Learn, DesktopWindowXamlSource Class (Microsoft.UI.Xaml.Hosting). Sobre que esta es la clase central de la API de hospedaje XAML del Windows App SDK, capaz de hospedar controles WinUI en cualquier elemento de interfaz asociado a un HWND, y usable desde aplicaciones de escritorio construidas con WPF, Windows Forms y Win32 (API de Windows).  2

  27. Microsoft Learn, Quickstart: Sending a toast notification from the desktop. Sobre que enviar un toast desde una aplicación de escritorio presupone un acceso directo del menú Inicio con System.AppUserModel.ID establecido, y que ese AppUserModelID hay que pasarlo a la llamada CreateToastNotifier, sin el cual el toast no se muestra. 

  28. Microsoft Learn, Use app notifications with a .NET app. Sobre que usar AppNotificationManager del Windows App SDK en una aplicación WPF/WinForms exige una llamada a Register() tras registrar el controlador NotificationInvoked; que Register() realiza automáticamente, para aplicaciones sin empaquetar, el registro del servidor COM que inicia la aplicación al hacer clic en una notificación; y que los requisitos previos son añadir el Windows App SDK y configurar las llamadas a API WinRT.  2

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Es WinRT un runtime administrado como .NET?
No. WinRT (Windows Runtime) no es un entorno de ejecución con una máquina virtual o un recolector de basura; es un ABI (un contrato binario) construido sobre COM. La propia documentación de Microsoft afirma con claridad que "The Windows Runtime is based on COM", y cada interfaz WinRT exige IInspectable, que deriva de IUnknown. Cada llamada sigue siendo una llamada COM a través de una vtable, basada en el recuento de referencias (AddRef/Release) y QueryInterface. El nombre ".winmd" y el encaje cercano con .NET hacen fácil tomar WinRT por un entorno administrado, pero un .winmd es un archivo de metadatos que toma prestado el mismo formato físico que ECMA-335, y los archivos .winmd que proporciona el sistema no contienen código ejecutable. Que C# pueda llamarlo con tanta naturalidad se debe a que una proyección de lenguaje genera una vista C# a partir de esos metadatos.
Oigo que UWP ya no es lo principal. ¿Sigue teniendo sentido aprender WinRT?
Sí. UWP, el modelo de aplicación, y WinRT, el fundamento de API, son dos cosas distintas. Incluso después de que UWP se recortara, la mayoría de las API WinRT siguen ofreciéndose de forma que las aplicaciones de escritorio WPF, WinForms y Win32 puedan llamarlas, y muchas de las capacidades de Windows hoy, como las notificaciones toast, Compartir, Bluetooth y OCR, se exponen como API WinRT. Además, WinUI (Windows App SDK), el marco de interfaz nativo que Microsoft recomienda actualmente, está construido sobre el ABI de WinRT. En otras palabras, la maquinaria de WinRT (IInspectable, .winmd, proyecciones de lenguaje) no es un vestigio de UWP; es precisamente el fundamento del desarrollo actual de aplicaciones para Windows.
¿Puede una aplicación WPF o WinForms llamar a API WinRT?
Sí. En .NET 6 o posterior basta con establecer el TargetFramework del proyecto en un TFM con versión de sistema operativo Windows, por ejemplo net8.0-windows10.0.19041.0; entonces se referencian los ensamblados de proyección del Windows SDK y las API WinRT de los espacios de nombres Windows.* se pueden llamar directamente desde C#. En C++, añada el paquete NuGet Microsoft.Windows.CppWinRT y use C++/WinRT con C++17 o posterior. Hay, no obstante, tres puntos de atasco. Primero, las clases de interfaz que presuponen un CoreWindow, como selectores y cuadros de diálogo, necesitan que el HWND de la ventana propietaria se pase mediante IInitializeWithWindow antes de mostrarlas (la excepción es DataTransferManager de la interfaz de Compartir: en lugar de IInitializeWithWindow usa el IDataTransferManagerInterop dedicado, una vía distinta que pasa el HWND a ShowShareUIForWindow). Segundo, algunas API, como el historial de notificaciones y las listas de accesos directos, exigen identidad de paquete (empaquetado MSIX o un paquete con ubicación externa). Tercero, un subproceso que trata objetos WinRT debe inicializarse primero. En código nativo, indique el modelo de concurrencia STA/MTA con winrt::init_apartment o RoInitialize (en aplicaciones C# WPF/WinForms el runtime suele encargarse). Tenga en cuenta que las API que dependen de CoreWindow o ApplicationView en sí no se pueden usar en absoluto desde aplicaciones de escritorio.
Llamar a un selector como FolderPicker desde una aplicación de escritorio lanza una excepción. ¿Por qué?
Porque algunas clases WinRT de selector y de cuadro de diálogo se diseñaron para mostrarse en un CoreWindow de UWP. Una aplicación de escritorio no tiene CoreWindow, así que hay que indicar explícitamente la ventana propietaria antes de mostrar el objeto. En concreto, primero obtenga el HWND de la ventana propietaria (WinRT.Interop.WindowNative.GetWindowHandle para una Window de WinUI, WindowInteropHelper para WPF, la propiedad Handle del formulario para WinForms) y, en C#, páselo al selector con WinRT.Interop.InitializeWithWindow.Initialize. En C++/WinRT, haga QueryInterface del objeto a IInitializeWithWindow (as<IInitializeWithWindow>()) y llame a Initialize(hwnd). Sin esta inicialización, la llamada lanza o falla en silencio. Tenga en cuenta que los selectores más nuevos del Windows App SDK (Microsoft.Windows.Storage.Pickers) se rediseñaron para recibir un WindowId en el constructor, lo que hace innecesario este patrón de inicialización (pero, como son API del Windows App SDK, un ajuste de TFM no basta: hay que añadir el SDK y, para una aplicación sin empaquetar, desplegar e inicializar el runtime en las máquinas de destino).
He oído que el desarrollo de WinUI se ha abierto en GitHub. ¿Hay que tirar las aplicaciones WPF/WinForms existentes?
No hay que precipitarse. Abrir el desarrollo principal de WinUI es una declaración de dirección, de que Microsoft invierte en serio en su marco nativo, no un aviso de fin de los marcos existentes. WPF y WinForms siguen admitidos hoy como parte de .NET. Divida la decisión en dos. Una es si migrar el marco de interfaz, una decisión grande que depende del tamaño de los activos de pantalla, los controles de terceros y la organización de desarrollo. La otra es si usar API WinRT de forma selectiva desde la aplicación existente, lo que puede empezar hoy con un ajuste de TFM. Tenga en cuenta que el TFM solo hace las API invocables; las notificaciones toast, por ejemplo, requieren un registro además de la llamada (en la vía clásica, una aplicación sin empaquetar debe registrar un acceso directo con AppUserModelID — una empaquetada no, porque la identidad de paquete proporciona el AppUserModelID; en la vía del Windows App SDK hay que añadir el SDK — y para una aplicación sin empaquetar también instalar el runtime del Windows App SDK en cada PC de destino — y llamar a Register() de AppNotificationManager, mientras que en una aplicación empaquetada con MSIX el registro automático de Register() no funciona y además hay que declarar un activador COM en Package.appxmanifest). Además, en la vía del Windows App SDK las notificaciones desde un proceso elevado a administrador no están admitidas, y Show falla en silencio sin lanzar — una aplicación que necesita elevación debería plantearse separar las notificaciones en un proceso no elevado. Aun así, si solo quiere notificaciones toast, no hace falta migrar a WinUI. Tenga en cuenta que mezclar controles WinUI en pantallas WPF/WinForms existentes (XAML Islands) exige distinguir generaciones. Los Islands de generación UWP se detuvieron en .NET Core 3.x para WPF/WinForms, y para la generación WinUI 3 la API de hospedaje XAML del Windows App SDK (DesktopWindowXamlSource) es usable desde WPF/WinForms, pero no hay controles contenedor cómodos y la carga de implementación es alta, así que evaluarlo con cautela es por ahora el camino seguro.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog