Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 13 Min.

Ahead-of-time-Übersetzung für .NET Core

Native Anwendungen für Windows, Linux und macOS erstellen, ohne die bevorzugte .NET-Sprache aufgeben zu müssen.
© Foto: dotnetpro
Anwendungen auf Basis des .NET Frameworks basieren typischerweise auf einem zweistufigen Kompilierungsprozess. In einem ersten Schritt wird aus Quellcode der Common-Intermediate-Language-Code (CIL-Code) erzeugt, der nicht an eine bestimmte Laufzeitumgebung gebunden ist. In einem zweiten Schritt werden zur Laufzeit die erforderlichen Teile des CIL-Codes durch die Common Language Runtime (CLR) des .NET Frameworks in nativen Code übersetzt. Ab diesem Zeitpunkt wird die spezifische Laufzeitumgebung adressiert. Bei .NET Core übernimmt CoreCLR diese Aufgabe und ermöglicht dadurch den für Windows, Linux und macOS plattformübergreifenden sowie architekturübergreifenden Einsatz (x86, x64, ARM). Sofern beim Erstellen die Plattform bekannt ist, besteht mit CoreRT die Möglichkeit, durch AOT-Kompilierung (AOT: Ahead of time) die Vorteile der systemeigenen Kompilierung zur nutzen und gleichzeitig nicht auf die bevorzugte .NET-Programmiersprache verzichten zu müssen [1]. Was sich genau hinter CoreRT verbirgt, wie es im Vergleich zu eigenständigen Bereitstellungen oder .NET Native einzuordnen ist und wie Sie es in Ihrer eigenen Anwendung nutzen können, klärt dieser Artikel.

Einordnung in das .NET-Ökosystem

Das .NET-Ökosystem (Bild 1) bietet viele Möglichkeiten, um moderne Applikationen zu erstellen. Die Versions- und Systemvielfalt, die sich entwickelt hat, kann allerdings schnell zu einem undurchsichtigen Dschungel werden.
Das umfangreiche.NET-Ökosystem im Überblick(Bild 1) © Autor
Das .NET-System besteht heute aus drei Säulen: dem .NET Framework zum Entwickeln für Windows, .NET Core für plattformübergreifende Anwendungen mit dem Fokus auf Windows, Linux und macOS sowie Xamarin zum Erstellen von Apps beispielsweise für Android und iOS.Diese Säulen bauen auf einem gemeinsamen Fundament auf. Hierzu zählen zum Beispiel die Compiler-Plattform Roslyn, die Microsoft unter Open Source gestellt hat. Diese Plattform erzeugt aus C#, F# oder Visual Basic CIL-Code. Dieser wird beim Ausführen durch die jeweilige Laufzeitumgebung interpretiert und durch den dazugehörenden Just-in-time-Compiler in plattform- und architekturspezifischen Code umgewandelt. Unter der Überschrift „Schnelligkeit vor Optimierung“ betrachtet die Laufzeitumgebung nicht den gesamten Code, sondern lediglich die gerade erforderlichen Segmente und kompiliert sie beim erstmaligen Zugriff. Im Falle des .NET Frameworks wird diese Runtime als Common Language Runtime (CLR) bezeichnet.Auf Xamarin basierende Anwendungen werden durch die Mono-Laufzeitumgebung ausgeführt. Je nach Zielplattform unterscheiden sich die Form und die Mechanismen der Kompilierung und somit auch die Ausführung der Anwendung. So unterliegen iOS-Apps aufgrund von Restriktionen des Betriebssystems einer vollständigen Ahead-of-time-Kompilierung. Bei Anwendungen für Android ist die Ausführung über einen JIT-Mechanismus der übliche Weg. Es besteht zwar ebenfalls die Option von Ahead-of-time, dies ist im Gegensatz zu iOS aber keine vollwertige AOT-Kompilierung [2]. Da sowohl die Erstellungs- und Ausführungsprozesse im Fall von Xamarin als auch die Grundausrichtung der Mono-Laufzeitumgebung differenziert zu betrachten sind, werden diese beiden Themen hier nicht weiter verfolgt.Was die CLR für das .NET Framework ist, ist CoreCLR für .NET Core. Das Grundprinzip der zweistufigen Kompilierung über Roslyn und JIT gibt es auch hier. Der eingesetzte JIT-Compiler bei beiden Laufzeitumgebungen (CLR und CoreCLR) ist RyuJIT, der mittlerweile alle vorgesehenen Prozessorarchitekturen, x86, x64, ARM32 und ARM64, unterstützt [3].Im Unterschied zu JIT-Compilern wie RyuJIT konvertieren AOT-Compiler bereits zum Zeitpunkt des Kompilierens den gesamten CIL-Code in nativen plattformspezifischen Code. „Plattformspezifisch“ bezieht sich in diesem Zusammenhang sowohl auf das Betriebssystem als auch auf die Architektur des Zielsystems, wodurch die Voraussetzung einer spezifischen Runtime auf dem Zielsystem entfällt. Da AOT-Kompilierung nicht zeitkritisch ist, ergibt sich bei dieser Form in den meisten Fällen hochoptimierter Code, ausgerichtet auf das jeweilige Zielsystem.Mit Blick auf die native Unterstützung ohne zusätzliche Laufzeitumgebung gibt es seit .NET Core unterschiedliche Formen, eine Anwendung bereitzustellen: die Framework-abhängige Bereitstellung (framework-dependend deployment, FDD), die eigenständige Bereitstellung (self-contained deployment, SCD) und seit .NET Core 2.2 die Framework-abhängig ausführbare Bereitstellung (framework-dependent executables, FDE) [4].Für das Framework-abhängige Bereitstellen muss auf dem Zielsystem eine Laufzeitumgebung installiert werden. Bei der Veröffentlichung der Anwendung erhält man allerdings keine ausführbare Datei wie beispielsweise EXE-Dateien für Windows.Die eigenständige Bereitstellung (SCD) kann als eine Kombination aus JIT und AOT betrachtet werden. Diese Anwendungsart wird speziell für ein Zielsystem veröffentlicht und enthält plattformspezifische Laufzeitbibliotheken des .NET-Core-Frameworks sowie einen ausführbaren Wrapper. Dieser Wrapper, eine umbenannte Kopie des plattformspezifischen Hosts der .NET-Core-Laufzeitumgebung, lädt das .NET-Core-Framework und startet die Anwendung, als ob .NET Core auf dem Zielsystem installiert wäre. Somit starten AOT-Komponenten die Laufzeitumgebung und die .NET-Anwendung werden mithilfe des JIT-Compilers der CoreCLR ausgeführt.Die letzte Bereitstellungsvariante erzeugt vom Framework abhängige ausführbare Dateien. Dabei muss auf dem Zielsystem eine Laufzeitumgebung vorhanden sein, ein zusätzlich bereitgestellter Wrapper vereinfacht den Anwendungsstart. Somit ist die letzte Variante eine Mischform aus den ersten beiden.Im Kontext von AOT-Kompilierung ist .NET Native anzusiedeln. Dabei handelt es sich um einen vollwertigen AOT-Compiler mit dem Fokus auf UWP und Windows-10-Apps [5]. Wie .NET Native ist auch CoreRT ein vollwertiger AOT-Compiler, genauer gesagt die Open-Source-Variante von .NET Native. Beide Arten haben daher viel gemeinsam und teilen sich in weiten Teilen die Codebasis [6]. Der wesentlichste Unterschied liegt in den verwendeten Compiler-Technologien. Bei .NET Native ist der UTC-Compiler fester Bestandteil, wohingegen bei CoreRT unterschiedliche Compiler zum Einsatz kommen können. Einer davon ist RyuJIT, der innerhalb der CoreCLR als JIT-Compiler verwendet wird. Neben den verwendeten Technologien liegt bei CoreRT, wie der Name bereits verrät, der Fokus auf der Unterstützung sämtlicher .NET-Core-Szenarien. Dadurch wird es künftig möglich sein, die Vorteile der systemeigenen Kompilierung für Windows, Linux und macOS zu nutzen und gleichzeitig die bevorzugten .NET-Programmiersprachen mit Merkmalen wie zum Beispiel generischen Klassen oder async/await einzusetzen [1].

CoreRT in Kürze

Bei näherer Betrachtung ist CoreRT eine Laufzeitumgebung für .NET Core. Dies bedeutet, dass sich CoreRT als ein Derivat der CoreCLR und der darin enthaltenen Bibliotheken, wie beispielsweise dem JIT-Compiler RyuJIT, betrachten lässt. Resultat dieser Laufzeitumgebung ist eine einzelne ausführbare Datei oder native Bibliothek, welche die Anwendung mit sämtlichen Abhängigkeiten enthält und nativ auf dem jeweiligen System läuft.Ziel der Laufzeitumgebung ist demnach das Erstellen von nativem Code auf Basis von CIL-Code (Bild 2). Dabei kommt RyuJIT als AOT-Compiler zum Einsatz, alternativ auch IL-to-C++, ein Transpiler zum Erstellen von C++ aus CIL-Bytecode, oder LLILC [1]. Letzterer ist ein JIT-Compiler für .NET, der auf dem Compiler-Frameworks LLVM basiert und somit auf die Stärken des CLang-Compilers zurückgreifen kann. Künftig sollen sich auf Basis dieses Compiler-Frameworks AOT-Szenarien abbilden lassen. RyuJIT hingegen ist für den Einsatz im .NET-Umfeld optimiert. [7].
Schematische Darstellungder Funktionsweise von CoreRT(Bild 2) © Autor
Sobald CoreRT in ein Projekt eingebunden ist, klinkt es sich über eigene MSBuild-Targets in den Veröffentlichungsprozess ein. Dies bedeutet, dass beim Bereitstellen über dotnet publish die weitere Verarbeitung durch den IL-Compiler CoreRT von MSBuild unmittelbar nach dem Erstellungsprozess durch das Roslyn-Compiler-System angestoßen wird. Diese Verarbeitung lässt sich grundsätzlich in folgende Phasen einteilen:
  • Ermitteln von Einstiegspunkten für das Kompilieren
  • Einstiegspunkt der Assembly ermitteln (zum Beispiel Main() oder eine mit dem Attribut [NativeCallable] versehene Methode)
  • Integration weiterer Laufzeitanweisungen in der Datei rd.xml (siehe unten), um Reflection zuzulassen
  • Ermitteln der Module, Typen und Klassen über einen IL-Scanner inklusive Auflösen der Laufzeitanweisungen für Reflection. Dabei wird nur der Code betrachtet, der auch aktiv verwendet wird [8].
  • Kompilieren des IL-Codes durch den situationsbezogenen Compiler
  • RyuJIT für das Kompilieren und (Compiler-interne) ObjectWriter-Objekte (auf Basis LLVM) für das Erstellen der Ausgabe
  • CppCodeGen für das Kompilieren und CppWriter für das Erstellen der Ausgabe
  • WebAssembly-CodeGen (WASM) für das Kompilieren und ObjectWriter (auf Basis LLVM) für das Erstellen der Ausgabe
Um die Lauffähigkeit zu wahren, kommt es neben der Umwandlung des Codes durch den Core­RT-IL-Compiler auch zum Austausch bestimmter Codesegmente wie beispielsweise Assembly.GetExecutingAssembly. Sobald das Ergebnis durch den jeweiligen Ausgabemechanismus geschrieben wurde, greift die weitere Verarbeitung mit MSBuild. Beispielsweise wird beim Erstellen mit RyuJIT zum Finalisieren der native Linker angestoßen. Dieser verbindet die CoreRT-spezifische Laufzeitumgebung mit dem auszuführenden Code. Als Ergebnis steht eine an ein Betriebssystem und Architektur gebundene ausführbare Assembly. Tiefergehende technische Details über den Aufbau und die Funktionsweise von CoreRT hat der Entwickler Matt Warren zusammengestellt [9].

Einschränkungen

CoreRT befindet sich derzeit im Alpha-Stadium. Als dieser Artikel entstand, gab es noch keine Roadmap, aus der ein offizielles Release ersichtlich wird. Dies ist auch der Grund, warum es hier und da etwas klemmt und derzeit nicht alle Plattformen unterstützt werden. Zu den Systemen, für die sich eine native Anwendung erstellen lässt, zählen Windows x64, Linux x64 oder macOS x64. Auf x86 basierende Plattformen werden – zumindest im Moment – nicht unterstützt [10]. Das Erstellen von nativen Anwendungen für macOS oder Linux, ausgehend von einem Windows-basierten System oder umgekehrt, ist derzeit ebenfalls nicht möglich [11].Weitere Einschränkungen ergeben sich durch den Grad der Einsatzbereitschaft einzelner Bausteine. So können Konsolenanwendungen im Gegensatz zu ASP.NET Core bereits ein sehr gutes Ergebnis erzielen. Darüber hinaus befindet sich der WebAssembly-Compiler-Baustein in einer sehr frühen Phase und kann, Stand heute, nur sehr einfache Anwendungen ausführen. Innerhalb des Moduls CppCodeGen fehlen im Moment grundlegende Elemente wie die Unterstützung von Reflection, eine Speicherbereinigung oder das Behandeln von Exceptions [12].Auf Reflection ist bei AOT-Szenarien grundsätzlich ein besonderes Augenmerk zu legen. Selbst bei Einsatz des RyuJIT-Compilers sind einige Besonderheiten zu beachten. Wie oben aufgeführt, werden bei der Kompilierung nur Module, Typen und Klassen eingebunden, die explizit verwendet werden. Beim Einsatz von Reflection im eigenen Quellcode, wie dies beispielsweise bei der Serialisierung und Deserialisierung implizit erfolgt, müssen Sie dies dem CoreRT-Compiler mitteilen. Dazu werden die Laufzeitanweisungen über die Datei rd.xml bekannt gemacht. In der Konsequenz ergänzt der Compiler das finale ausführbare Programm um entsprechende Metadaten, die zur Laufzeit aufgelöst und vom Reflections-Stack verwendet werden [13]. Das folgende Beispiel zeigt einen einfachen Aufbau dieser Datei, welche die notwendigen Laufzeitanweisungen auflistet:
<span class="php"><span class="hljs-meta">&lt;?</span>xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span><span class="hljs-meta">?&gt;</span></span> 
<span class="hljs-tag">&lt;<span class="hljs-name">Directives</span>&gt;</span> 
  <span class="hljs-tag">&lt;<span class="hljs-name">Application</span>&gt;</span> 
    <span class="hljs-tag">&lt;<span class="hljs-name">Assembly</span> <span class="hljs-attr">Name</span>=<span class="hljs-string">"SampleApp"</span>&gt;</span> 
      <span class="hljs-tag">&lt;<span class="hljs-name">Type</span> <span class="hljs-attr">Name</span>=<span class="hljs-string">"SampleApp.SampleType"</span> </span>
<span class="hljs-tag">        <span class="hljs-attr">Dynamic</span>=<span class="hljs-string">"Required All"</span>/&gt;</span> 
    <span class="hljs-tag">&lt;/<span class="hljs-name">Assembly</span>&gt;</span> 
  <span class="hljs-tag">&lt;/<span class="hljs-name">Application</span>&gt;</span> 
<span class="hljs-tag">&lt;/<span class="hljs-name">Directives</span>&gt;</span> 
Das Attribut Dynamic legt fest, wie der Laufzeitzugriff auf Konstruktoren, Methoden, Felder, Eigenschaften und Ereignisse erfolgt. Required All führt dazu, dass alle Typen und Member unabhängig von ihrer Verwendung beibehalten werden [14].Damit die Datei von CoreRT verwendet wird, ist die Projektdatei um folgenden Eintrag zu ergänzen:
<span class="hljs-tag">&lt;<span class="hljs-name">Project</span> <span class="hljs-attr">Sdk</span>=<span class="hljs-string">"Microsoft.NET.Sdk"</span>&gt;</span> 
  ... 
  <span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">RdXmlFile</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"rd.xml"</span> /&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span> 
  ... 
<span class="hljs-tag">&lt;/<span class="hljs-name">Project</span>&gt;</span> 
Sobald der Verweis auf die Datei der Laufzeitanweisungen vorhanden ist, erzeugt CoreRT für die aufgeführten Definitionen die zuvor beschriebenen Metadaten. Zu beachten ist, dass die Form der Verarbeitung dieser Datei vor dem ersten Release geändert wird [15].

Hello CoreRT – Ein Beispiel

Die genannten Einschränkungen sollen nicht von einem praktischen Versuch abschrecken. Das Beispiel HelloCoreRT zeigt anhand einer kleinen Konsolenanwendung auf Basis von .NET Core, wie Sie mit CoreRT eine vollwertig native Anwendung für x64-basierte Systeme erzeugen. Das Grundgerüst der Anwendung, bestehend aus der Projektdatei in Listing 1 und der Klasse Program:
Listing 1: Die Projektdatei HelloCoreRT.csproj
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;Project&lt;/span&gt; &lt;span class="hljs-attr"&gt;Sdk&lt;/span&gt;=&lt;span class="hljs-string"&gt;"Microsoft.NET.Sdk"&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;PropertyGroup&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;    &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;OutputType&lt;/span&gt;&amp;gt;&lt;/span&gt;Exe&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;OutputType&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;    &lt;span class="hljs-tag"&gt;&amp;lt;&lt;span class="hljs-name"&gt;TargetFramework&lt;/span&gt;&amp;gt;&lt;/span&gt;netcoreapp2.1&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;TargetFramework&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;  &lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;PropertyGroup&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;br/&gt;&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span class="hljs-name"&gt;Project&lt;/span&gt;&amp;gt;&lt;/span&gt; 
using System; 

namespace HelloCoreRT 
{ 
  public class Program 
  { 
    public static void Main(string[] args) 
    { 
      Console.WriteLine("Hello CoreRT!"); 
    } 
  } 
} 
Mit diesem Stand lässt sich über die Befehlszeilenanweisung dotnet publish --configuration Release die Anwendung erstellen. Dabei werden die Abhängigkeiten aus der Projekt­datei aufgelöst, die Applikation kompiliert und die resultierenden Dateien in einem Verzeichnis abgelegt. Das Ergebnis umfasst mit dieser Konfiguration folgende Dateien:
  • HelloCoreRT.dll: die Assembly, die den Intermediate-Language-Code (CIL) enthält
  • HelloCoreRT.deps.json: listet alle Abhängigkeiten des Projekts auf
  • HelloCoreRT.runtimeconfig.json: spezifiziert die Laufzeit und weitere Konfigurationen
  • Weitere Abhängigkeiten, falls NuGet-Pakete referenziert werden
Wie aus den Dateien erkennbar ist, enthält die Bereitstellung bei frameworkabhängigen Bereitstellungen (FDD) nur den eigenen Code. Dies bedeutet, dass eine gemeinsame systemweite Version von .NET Core auf dem Zielsystem vorhanden sein muss. Das Ausführen der Anwendung erfolgt mit dem Aufruf von dotnet HelloCoreRT.dll.Wie oben erläutert, gibt es unter .NET Core verschiedene Arten der Bereitstellung. Bei der Verwendung der eigenständigen Bereitstellung (SCD) muss im Gegensatz zum FDD das .NET-Core-Framework nicht auf dem Zielsystem installiert sein. Daher muss bei dieser Option ein Runtime-Bezeichner angegeben werden. Die Anweisung dotnet publish --configuration Release --self-contained --runtime win-x64 erstellt und veröffentlicht die Anwendung inklusive der passenden .NET-Core-Laufzeitumgebung für das angegebene Zielsystem. Das Bereitstellen umfasst ohne weitere Optimierungen nun 217 Dateien. Die Anwendung verfügt mit dieser Methode über einen Wrapper, hier HelloCoreRT.exe, der die Laufzeitumgebung lädt und danach intern die Anwendung startet.Mit Blick auf eine laufzeitunabhängige Anwendung wird hinsichtlich des Umfangs bei eigenständiger Bereitstellung (SCD) die Bedeutung von CoreRT greifbar. Hiermit ist es möglich, eine einzige ausführbare Datei zu erzeugen, ohne eine .NET-Runtime auf dem Zielsystem haben zu müssen. Dazu muss allerdings das Zielsystem, unter dem die Applikation ausgeführt wird, bereits zum Erstellungszeitpunkt bekannt sein.Falls die Anwendung auf einem auf Windows basierenden System erstellt wird, ist es zunächst notwendig, Visual Studio 2017 inklusive des Pakets Desktopentwicklung mit C++ zu installieren, damit der native Linker zur Verfügung steht. Anschließend muss das NuGet-Paket Microsoft.DotNet.ILCompiler, wie nachfolgend gezeigt, der Projektdatei aus Listing 1 hinzugefügt werden:
&lt;Project Sdk="Microsoft.NET.Sdk"&gt; 
  ... 
  &lt;ItemGroup&gt; 
    &lt;PackageReference 
      Include="Microsoft.DotNet.ILCompiler" 
      Version="1.0.0-alpha-*" /&gt; 
  &lt;/ItemGroup&gt; 
&lt;/Project&gt; 
Da CoreRT noch nicht fertig und somit auch nicht über NuGet verfügbar ist, ist eine nutzerdefinierte NuGet-Konfiguration erforderlich. Der Kommandozeilenbefehl dotnet new nuget fügt dem Projekt die Datei nuget.config hinzu, in der anschließend der Abschnitt packageSources anzupassen ist. Eine funktionierende Konfiguration kann dem folgenden Listing entnommen werden:
&lt;?xml version="1.0" encoding="utf-8"?&gt; 
&lt;configuration&gt; 
  &lt;packageSources&gt; 
    &lt;clear /&gt; 
    &lt;add 
      key="dotnet-core" 
      value="https://dotnet.myget.org/F/dotnet-core/
            api/v3/index.json" /&gt; 
    &lt;add 
      key="nuget.org" 
      value="https://api.nuget.org/v3/index.json" 
      protocolVersion="3" /&gt; 
  &lt;/packageSources&gt; 
&lt;/configuration&gt; 
Die Abhängigkeit von Microsoft.DotNet.ILCompiler führt zu einer Anpassung des Bereitstellungsprozesses. Je nach angegebenem Runtime-Bezeichner wird beim Aufruf von dotnet publish --configuration Release --runtime win-x64 die plattformspezifische Laufzeitkomponente nachgeladen, hier runtime.win-x64.Microsoft.DotNet.ILCompiler. Beim Veröffentlichen wird schließlich, wie in den vorherigen Abschnitten dargelegt, eine einzige auszuführende native x64-Anwendung HelloCoreRT.exe erzeugt.Eine Anpassung der Anwendung wie in Listing 2 stellt die auszugebende Nachricht über ein XML-Dokument bereit, was den Umgang mit Reflection verdeutlichen soll. Das bereitgestellte XML-Dokument sieht wie folgt aus.
Listing 2: Abgewandelte Anwendung unter Verwendung von Reflexion
using System; &lt;br/&gt;using System.Reflection; &lt;br/&gt;using System.Xml.Serialization; &lt;br/&gt;&lt;br/&gt;namespace HelloCoreRT &lt;br/&gt;{ &lt;br/&gt;  public class Program &lt;br/&gt;  { &lt;br/&gt;    public static void Main(string[] args) &lt;br/&gt;    { &lt;br/&gt;      var assembly = Assembly.GetExecutingAssembly(); &lt;br/&gt;      var resourceName =&lt;br/&gt;        $"{typeof(Program).Namespace}.content.xml"; &lt;br/&gt;&lt;br/&gt;      using (var stream = assembly.&lt;br/&gt;        GetManifestResourceStream(resourceName)) &lt;br/&gt;      { &lt;br/&gt;        var serializer =&lt;br/&gt;          new XmlSerializer(typeof(InputDocument)); &lt;br/&gt;        var document = serializer.&lt;br/&gt;          Deserialize(stream) as InputDocument; &lt;br/&gt;        if (document != null) &lt;br/&gt;        { &lt;br/&gt;          Console.WriteLine(document.Greeting); &lt;br/&gt;        } &lt;br/&gt;      } &lt;br/&gt;    } &lt;br/&gt;  } &lt;br/&gt;&lt;br/&gt;  [XmlRoot("content")] &lt;br/&gt;  public class InputDocument &lt;br/&gt;  { &lt;br/&gt;    [XmlElement("greeting")] &lt;br/&gt;    public string Greeting { get; set; } &lt;br/&gt;  } &lt;br/&gt;} 
&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt; 
&lt;content&gt; 
  &lt;greeting&gt;Hello CoreRT!&lt;/greeting&gt; 
&lt;/content&gt; 
Beim Deserialisieren des XML-Dokuments wird intern Reflection verwendet. Die Anwendung lässt sich in dieser Zusammenstellung weiterhin erfolgreich kompilieren, zur Laufzeit erzeugt sie aber eine Ausnahme, aus der sich nicht immer unmittelbar auf den tatsächlichen Fehler schließen lässt (Bild 3). Um weitere Angaben zu den Ursachen zu erhalten, bietet Core­RT die Möglichkeit, den Stack-Trace mit auszugeben. Dazu ist die Projektdatei HelloCoreRT.csproj zu erweitern:
Verwendungvon Reflexion ohne und mit Ausgabe des Stack-Trace(Bild 3) © Autor
&lt;Project Sdk="Microsoft.NET.Sdk"&gt; 
  ... 
  &lt;ItemGroup&gt; 
    &lt;IlcArg 
      Include="--stacktracedata" 
      Condition="'$(Configuration)' == 'Debug'" /&gt; 
  &lt;/ItemGroup&gt; 
&lt;/Project&gt; 
Im Stack-Trace zeigt sich, weshalb die Deserialisierung nicht erfolgreich war. Die Ursache liegt unter anderem darin, dass für die Ausführung der Methode GetSetMemberValueDelegateWithType() des Typs System.Xml.Serialization.ReflectionXmlSerializationReaderHelper()(Bild 3) die notwendigen Definitionen fehlen.Damit die Beispielanwendung nun auch weiterhin läuft, muss das Projekt um die notwendigen Laufzeitanweisungen ergänzt werden. In Listing 3 ist die Datei rd.xml zu sehen, in der alle für die Ausführung erforderlichen Anweisungen enthalten sind, auch die der Methode GetSetMemberValueDelegateWithType().
Listing 3: Laufzeitanweisungen zur Unterstützung von Reflexion
&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt; &lt;br/&gt;&amp;lt;Directives&amp;gt; &lt;br/&gt; &amp;lt;Application&amp;gt; &lt;br/&gt;  &amp;lt;Assembly Name="HelloCoreRT"&lt;br/&gt;  Dynamic="Required All" /&amp;gt; &lt;br/&gt;  &amp;lt;Assembly Name="System.Xml"&amp;gt; &lt;br/&gt;   &amp;lt;Type &lt;br/&gt;    Name="System.Xml.Serialization.&lt;br/&gt;          XmlElementAttribute" &lt;br/&gt;     Dynamic="Required All" /&amp;gt; &lt;br/&gt;  &amp;lt;/Assembly&amp;gt; &lt;br/&gt;  &amp;lt;Assembly Name="System.Private.Xml"&amp;gt; &lt;br/&gt;   &amp;lt;Type Name="System.Xml.Serialization.&lt;br/&gt;              ReflectionXmlSerializationReaderHelper"&amp;gt; &lt;br/&gt;    &amp;lt;Method Name="GetSetMemberValueDelegateWithType"&amp;gt; &lt;br/&gt;     &amp;lt;GenericArgument &lt;br/&gt;      Name="System.Object, System.Private.CoreLib" &lt;br/&gt;      Dynamic="Required All" /&amp;gt; &lt;br/&gt;     &amp;lt;GenericArgument &lt;br/&gt;      Name="System.String, System.Private.CoreLib" &lt;br/&gt;      Dynamic="Required All" /&amp;gt; &lt;br/&gt;    &amp;lt;/Method&amp;gt; &lt;br/&gt;   &amp;lt;/Type&amp;gt; &lt;br/&gt;  &amp;lt;/Assembly&amp;gt; &lt;br/&gt; /Application&amp;gt; &lt;br/&gt;&amp;lt;/Directives&amp;gt; 
Nachdem der Projektdatei HelloCoreRT.csproj der Verweis auf die Laufzeitanweisungen hinzugefügt wurde, ist die Anwendung nach dem Erstellen wieder als native x64-Anwendung lauffähig.
&lt;Project Sdk="Microsoft.NET.Sdk"&gt; 
  ... 
  &lt;ItemGroup&gt; 
    &lt;RdXmlFile Include="rd.xml" /&gt; 
  &lt;/ItemGroup&gt; 
  ... 
&lt;/Project&gt; 
Dem Erstellungsprozess geschuldet, fehlen in der fertigen plattformspezifischen ausführbaren Datei HelloCoreRT.exe die Assembly-Daten, wie zum Beispiel Autor, Version oder Beschreibung. Eine Möglichkeit, diese Angaben im Nachhinein hinzuzufügen, ist rcedit [16]. Hierbei handelt es sich um einen Bestandteil des Electron-Projekts, ein Framework zum Erstellen plattformübergreifender Desktop-Anwendungen mit JavaScript, HTML und CSS.Durch eine Erweiterung der Projektdatei HelloCoreRT.csproj kann der Build-Prozess durch ein zusätzliches MSBuild-Target (siehe Listing 4) angepasst werden, sodass rcedit automatisiert nach Erzeugen der Assembly durch LinkNa­tive von CoreRT ausgeführt und wird die ursprünglichen Werte gesetzt werden.
Listing 4: MSBuild-Target zum Hinzufügen von Metainformationen
&amp;lt;Target Name="InjectMetadataToExecutable"&lt;br/&gt;    AfterTargets="LinkNative"&amp;gt; &lt;br/&gt;  &amp;lt;PropertyGroup&amp;gt; &lt;br/&gt;    &amp;lt;RcEditPath&amp;gt;rcedit-x86.exe&amp;lt;/RcEditPath&amp;gt; &lt;br/&gt;    &amp;lt;ExecutablePath&amp;gt;&lt;br/&gt;      $(MSBuildThisFileDirectory)$(NativeBinary)&lt;br/&gt;    &amp;lt;/ExecutablePath&amp;gt; &lt;br/&gt;  &amp;lt;/PropertyGroup&amp;gt; &lt;br/&gt;  &amp;lt;Message &lt;br/&gt;    Text="Modifying metadata of executable&lt;br/&gt;          '$(ExecutablePath)'." &lt;br/&gt;    Importance="High" /&amp;gt; &lt;br/&gt;  &amp;lt;Exec Command=" &lt;br/&gt;    $(RcEditPath) "$(ExecutablePath)" ^ &lt;br/&gt;     --set-file-version "$(FileVersion)" ^ &lt;br/&gt;     --set-version-string "CompanyName" "$(Company)" ^ &lt;br/&gt;     --set-version-string "LegalCopyright"&lt;br/&gt;      "$(Copyright)" ^ &lt;br/&gt;     --set-version-string "ProductName" "$(Product)" ^ &lt;br/&gt;     --set-version-string "ProductVersion"&lt;br/&gt;      "$(InformationalVersion)" ^ &lt;br/&gt;     --set-version-string "FileDescription"&lt;br/&gt;      "$(AssemblyName)" ^ &lt;br/&gt;     --set-version-string "OriginalFilename"&lt;br/&gt;      "$(AssemblyName).exe"" /&amp;gt; &lt;br/&gt;&amp;lt;/Target&amp;gt; 
Das Ergebnis dieses Beispiels ist nun eine native EXE-Datei für x64-basierende Windows-Systeme, die neben den Vorteilen der systemeigenen Kompilierung auch die Nachvollziehbarkeit über Metainformationen, wie beispielsweise die Versionsnummern, ermöglicht.

Fazit

CoreRT befindet sich derzeit in einer sehr frühen Phase und besitzt daher noch einige Einschränkungen. Der Einsatz steht daher in sehr starker Abhängigkeit zur Komplexität der zu erstellenden Anwendung. Die vorgestellte Beispielanwendung zeigt, dass CoreRT bei einfachen Anwendungen auf Konsolenbasis bereits heute eine Option sein kann. Dies ermöglicht es .NET-Entwicklern zukünftig völlig neue Szenarien der Applikationsbereitstellung. Eine einzige für das Zielsystem optimierte Datei vereinfacht die Verteilung enorm und bietet gleichzeitig die Optimierungen der Ahead-of-time-Kompilierung.Seien Sie sich aber darüber im Klaren, dass sich das Tool in der Alpha-Phase befindet. Bis zur Veröffentlichung einer fertigen Version von CoreRT kann die seit .NET Core 2.2 verfügbare frameworkabhängig ausführbare Bereitstellung für einige Anwendungsfälle aber sicherlich eine gute Alternative sein.
Projektdateien herunterladen

Fussnoten

  1. Intro to CoreRT, http://www.dotnetpro.de/SL1905CoreRT1
  2. How Xamarin.Android AOT Works, http://www.dotnetpro.de/SL1905CoreRT2
  3. The RyuJIT transition is complete!, http://www.dotnetpro.de/SL1905CoreRT3
  4. .NET Core-Anwendungsbereitstellung, http://www.dotnetpro.de/SL1905CoreRT4
  5. The .NET Native Tool-Chain, http://www.dotnetpro.de/SL1905CoreRT5
  6. GitHub Issue ‚.Net Native F# Support‘, http://www.dotnetpro.de/SL1905CoreRT6
  7. LLILC FAQ, http://www.dotnetpro.de/SL1905CoreRT7
  8. GitHub Issue ‚Could be let the compiled output be smaller?‘, http://www.dotnetpro.de/SL1905CoreRT8
  9. Matt Warren: CoreRT – A .NET Runtime for AOT, http://www.dotnetpro.de/SL1905CoreRT9
  10. GitHub Issue ‚X86 runtime build fails‘, http://www.dotnetpro.de/SL1905CoreRT10
  11. GitHub Issue ‚Cross compiling fails‘, http://www.dotnetpro.de/SL1905CoreRT11
  12. CoreRT GitHub repository, http://www.dotnetpro.de/SL1905CoreRT12
  13. GitHub Issue ‚CppCodeGen Enable reflection‘, http://www.dotnetpro.de/SL1905CoreRT13
  14. Runtime Directives (rd.xml) Configuration File Reference, http://www.dotnetpro.de/SL1905CoreRT14
  15. RD.XML processing will change before release, http://www.dotnetpro.de/SL1905CoreRT15
  16. Command line tool to edit resources of exe file on Windows, http://www.dotnetpro.de/SL1905CoreRT16

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Bausteine guter Architektur - Entwurf und Entwicklung wartbarer Softwaresysteme, Teil 2
Code sauberer gestalten anhand von wenigen Patterns und Grundhaltungen.
6 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige