WinRT und UWP mit purem C
« | 03 Jan 2021 | »Metro Apps
, WinRT
(Windows Runtime), Immersive
siehe
IsImmersiveProcess
und jetzt also UWP
(Universal Windows Platform).
So viele Namen für eine Technologie, mit der Microsoft in die Zukunft gehen
wollte.
Sinnvoll oder nicht, darüber kann man streiten. Aber wie kommt man nun da ran?
Mit fertigen Wizards für Apps möchte ich mich nicht aufhalten, schließlich sind nur für MSVC wirksam. Und dotNet Zusammenklickereien sind mir offen gesagt zu trivial, dass ich meine Zeit damit verschwende … das sollen die Anfänger machen.
Bei COM konnte man
stets auf das “gute alte” C
Interface zurücksteigen und damit jedem, und zwar wirklich jedem Compiler
dessen Features eröffnen.
CoInitializeEx()
machte eine Thread COM-fähig, dann brauchte man noch den Interface-Header
mit der Klasse und der richtigen Klassen-GUID und die Funktion
CoCreateInstance()
erzeugte auch schon eine IUnknown
Instanz mit Referenzzähler 1
.
Jetzt wechselte man noch (falls nötig) mit QueryInterface
und der richtigen Interface-ID (IID
) zu der Schnittstelle, die man haben
wollte, und schon konnte man COM-Methoden aufrufen.
WinRT bzw. die “neue” UWP API baut bekanntlich auf COM auf und erweitert
IUnknown
zu IInspectable
,
damit ein Objekt zur Laufzeit fragen kann, welche Interfaces es unterstützt,
weil QueryInterface
nur Ja oder Nein zu einer Interface Anfrage sagen kann.
Aber WIE lädt man nun WinRT/UWP Klassen in C bzw. purem C++?
WinRT ohne C++/CX?
Microsofts Compiler Erweiterungen sind ja alle recht nett (so wie auch C++/CLI), aber was soll man mit diesen Non-Standards, wenn z.B. den MinGW nutzen möchte?
Alles beginnt mit RoInitialize()
um einen Thread auf WinRT vorzubereiten. Zuerst braucht man nun den HSTRING
,
also einen konstanten (Immutable) Unicodestring, der Klassenpfade beschreiben
kann. Die Funktionen WindowsCreateString
und WindowsDeleteString
lassen einen solchen entstehen.
Die wichtigste Funktion zum laden und verwalten von WinRT/UWP Modulen ist
RoGetActivationFactory()
.
Ihr übergibt man den HSTRING
Klassenpfad um eine WinRT Klasse nutzen zu
können.
Hier gibt es einen wichtigen Unterschied zu COM, denn es gibt zwei Arten von Methoden:
- WinRT Klassen können quasi “statische” Methoden haben, die es COM nicht gab.
Das Ergebnis vonRoGetActivationFactory()
ist einIInspectable
, von dem man nun perQueryInterface
auf das Static-Interface der Klasse wechselt. Dort hat man dann Zugriff auf die statischen Methoden. - Für WinRT Instanz-Objekte nutzt man
QueryInterface
um vom zurückgegebenenIInspectable
vonRoGetActivationFactory()
auf das InterfaceIActivationFactory
zu wechseln. Dieses unterstützt nämlich die MethodeActivateInstance()
mit der eine neue Instanz des gewünschten WinRT Objektes erzeugt wird.
(Natürlich kann man QueryInterface()
auch überspringen, wenn man
RoGetActivationFactory()
gleich mit der korrekten IID
befüttert).
Nun braucht man ganz analog zu COM immer die richtigen Interface-Header und Interface-IIDs um an die benötigten Methoden zu kommen.
Klassensuche
Schlimmer ist es, sich in dem Chaos von Headern zurecht zu finden. Und auch die Namen von Klassen sind in elendslangen Konstanten deklariert.
Angenommen, man braucht Zufallszahlen. Die erhält man aus einer statischen
Methode des CryptographicBuffer
.
Dazu braucht man:
#include <windows.security.cryptography.h>
- Der Klassenpfad ist in der Konstante
RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer
und lautet einfach auf:"Windows.Security.Cryptography.CryptographicBuffer"
- Die Interface-ID findet man in
IID___x_ABI_CWindows_CSecurity_CCryptography_CICryptographicBufferStatics
- Das C-Interface struct-typedef heißt:
__x_ABI_CWindows_CSecurity_CCryptography_CICryptographicBufferStatics
- Und der VTBL-Pointer des Types
__x_ABI_CWindows_CSecurity_CCryptography_CICryptographicBufferStaticsVtbl
lässt uns dann endlich zur MethodGenerateRandomNumber()
.
… also “schön” sehen dieses langen Namen nicht aus … es handelt sich eben um generierten C Code, der objektorientierte Features wie Namensräume abbilden soll.
Fazit
OK … da sieht man also recht deutlich, warum das Programmieren mit der
C++ Erweiterung empfohlen wird.
Dort hat man dann zwar auch lange Namensräume für Klassen, wie
ABI::Windows::Security::Cryptography::ICryptographicBufferStatics
doch die
lassen sich leichter per IntelliSense nutzen als die hässlichen generierten
C Strukturen. Und wenn man noch using namespace
an erlaubten Stellen
einsetzt, ist es fast so leicht wie in C#.
However … für Extrembergsteiger sind steile Klippen kein Grund abzuspringen,
und so soll man sich auch nicht davon abhalten lassen, mit Nicht-MSVC
Compilern
in die WinRT Welt vorzudringen.
Es geht nämlich! Quod erat demonstrandum!
PS: Übelste Hardcore-Challenge: Die nötigen RT-Interfaces selbst schreiben und dort “bessere” Namen nutzen. Das hilft manchmal auch sehr.