<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Personal Project on kenji.blog</title><link>http://kenji.blog/de/tags/personal-project/</link><description>Recent content in Personal Project on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 04:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/tags/personal-project/index.xml" rel="self" type="application/rss+xml"/><item><title>Aus der LogicPad-Entwicklung lernen: Der Weg zur Veröffentlichung einer privat entwickelten Windows-App</title><link>http://kenji.blog/de/p/logicpad-windows-app-development-journey/</link><pubDate>Sun, 13 Sep 2026 04:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/logicpad-windows-app-development-journey/</guid><description>&lt;img src="http://kenji.blog/p/logicpad-windows-app-development-journey/img/eyecatch.jpg" alt="Featured image of post Aus der LogicPad-Entwicklung lernen: Der Weg zur Veröffentlichung einer privat entwickelten Windows-App" />&lt;h2 id="1-einführung-warum-heute-noch-eine-native-windows-app-entwickeln">1. Einführung: Warum heute noch eine native Windows-App entwickeln?
&lt;/h2>&lt;p>In der modernen Anwendungsentwicklung besteht kein Zweifel daran, dass plattformübergreifende Technologien wie Electron, Tauri und React Native den Mainstream bilden. Der Ansatz „Write Once, Run Anywhere“ (einmal schreiben, überall ausführen) unter Verwendung von Webtechnologien ist im Hinblick auf Entwicklungsgeschwindigkeit und Wartbarkeit äußerst rational. Dennoch habe ich mich bewusst dafür entschieden, die Anwendung „LogicPad“ als vollständig auf Windows optimierte, native Anwendung zu entwickeln.&lt;/p>
&lt;p>LogicPad ist ein digitaler Logikschaltungs-Simulator und Texteditor, der sich an Hardware-Ingenieure und Lernende im Bereich Logikschaltungen richtet. Es ist erforderlich, Zehntausende von Logikgattern in Echtzeit zu simulieren und gleichzeitig komplexe Wellenformdaten ohne Verzögerung zu rendern. In einem Bereich, der eine solch extreme Leistung erfordert, führen Pausen von wenigen Millisekunden (Micro-Stuttering) durch die Garbage Collection oder der Rendering-Overhead einer Web-View zu einer fatalen Verschlechterung des Benutzererlebnisses.&lt;/p>
&lt;p>In diesem Artikel werde ich den Weg von der Konzeption von LogicPad über die Implementierung mit C++ und WinUI 3 (Windows App SDK), die Überwindung spezifischer technischer Hürden, das MSIX-Packaging bis hin zur weltweiten Verbreitung über den Microsoft Store mit sehr detaillierten technischen Erklärungen Revue passieren lassen. Ich hoffe, dass das Teilen dieses Prozesses – wie ein Einzelentwickler eine native Windows-App in Enterprise-Qualität erstellt – als Wegweiser für diejenigen dient, die sich ebenfalls der nativen Entwicklung stellen.&lt;/p>
&lt;h2 id="2-projekt-zeitplan">2. Projekt-Zeitplan
&lt;/h2>&lt;p>Die Entwicklung von LogicPad wurde als persönliches Projekt an Wochenenden und Abenden vorangetrieben. Der gesamte Zeitplan erstreckte sich über etwa ein halbes Jahr (6 Monate). Unten sehen Sie ein Gantt-Diagramm, das den Fortschritt des Projekts zeigt.&lt;/p>
&lt;pre class="mermaid">
gantt
title LogicPad-Entwicklungszeitplan
dateFormat YYYY-MM-DD
section &amp;#34;Planung &amp;amp; Design&amp;#34;
&amp;#34;Anforderungsanalyse &amp;amp; Prototyping&amp;#34; :a1, 2025-10-01, 30d
&amp;#34;Architekturdesign (Core C++)&amp;#34; :a2, after a1, 20d
section &amp;#34;Kernentwicklung&amp;#34;
&amp;#34;Simulations-Engine (C++)&amp;#34; :a3, after a2, 45d
&amp;#34;WinUI 3 XAML-Integration&amp;#34; :a4, after a3, 40d
section &amp;#34;UI/UX-Verfeinerung&amp;#34;
&amp;#34;Benutzerdefinierte Titelleiste &amp;amp; Fensterverwaltung&amp;#34; :a5, after a4, 15d
&amp;#34;Asynchroner Dispatcher &amp;amp; Multithreading&amp;#34; :a6, after a5, 15d
section &amp;#34;Release &amp;amp; Store&amp;#34;
&amp;#34;MSIX-Packaging &amp;amp; Berechtigungen&amp;#34; :a7, after a6, 10d
&amp;#34;Microsoft Store Zertifizierung&amp;#34; :a8, after a7, 14d
&lt;/pre>
&lt;p>Wie Sie sehen können, wurde der Großteil der Entwicklungszeit für die Optimierung der Core-Engine und die Integration der asynchronen Verarbeitung zwischen WinUI 3 und C++ aufgewendet. Obwohl die native Entwicklung im Vergleich zur plattformübergreifenden Entwicklung höhere anfängliche Einrichtungs- und Lernkosten mit sich bringt, zahlt sich diese Investition in Form der endgültigen Leistung definitiv aus.&lt;/p>
&lt;h2 id="3-technologieauswahl-die-tiefen-von-c--winui-3--windows-app-sdk">3. Technologieauswahl: Die Tiefen von C++ / WinUI 3 / Windows App SDK
&lt;/h2>&lt;p>Bei der Entwicklung von LogicPad war die Auswahl des Technologie-Stacks eine der wichtigsten Entscheidungen. Für native UI-Frameworks auf der Windows-Plattform gibt es historisch gesehen verschiedene Optionen wie die Win32 API (User32/GDI), MFC, Windows Forms, WPF und UWP. Derzeit empfiehlt Microsoft &lt;strong>WinUI 3&lt;/strong>, das im &lt;strong>Windows App SDK&lt;/strong> enthalten ist, für die Entwicklung moderner Windows-Desktopanwendungen.&lt;/p>
&lt;h3 id="31-architektur-des-windows-app-sdk-und-winui-3">3.1. Architektur des Windows App SDK und WinUI 3
&lt;/h3>&lt;p>Das Windows App SDK ist eine Sammlung von Bibliotheken, die aktuelle Windows-APIs unabhängig von der Betriebssystemversion bereitstellen. Während die bisherige UWP (Universal Windows Platform) stark an Betriebssystem-Updates gebunden war, wird das Windows App SDK zusammen mit der Anwendung verteilt, wodurch ein konsistentes Verhalten von Windows 10 (ab Version 1809) bis Windows 11 gewährleistet wird.&lt;/p>
&lt;p>WinUI 3 ist ein natives UI-Framework, das auf diesem Windows App SDK läuft und das Fluent Design System vollständig unterstützt. Das Innere von WinUI 3 ist mit C++ und DirectX aufgebaut und arbeitet extrem schnell.&lt;/p>
&lt;h3 id="32-warum-bewusst-c-cwinrt-statt-c-gewählt-wurde">3.2. Warum bewusst C++ (C++/WinRT) statt C# gewählt wurde
&lt;/h3>&lt;p>Als Entwicklungssprachen für WinUI 3 werden C# und C++ unterstützt. Während die Entwicklungseffizienz mit C# und .NET dramatisch steigen würde, habe ich mich bei LogicPad aus folgenden Gründen für &lt;strong>C++/WinRT&lt;/strong> entschieden:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Deterministische Speicherverwaltung&lt;/strong>: Da es keinen Garbage Collector (GC) gibt, kann der Zeitpunkt der Speicherzuweisung und -freigabe vollständig kontrolliert werden. Dies verhindert GC-Pausen während der Simulationsschleife.&lt;/li>
&lt;li>&lt;strong>SIMD- und Cache-Optimierung&lt;/strong>: In C++ kann das physische Layout des Speichers (z. B. Struct of Arrays) strikt definiert werden, um die CPU-Cache-Trefferrate zu maximieren.&lt;/li>
&lt;li>&lt;strong>Native ABI-Grenze&lt;/strong>: C++/WinRT ist eine moderne C++-Projektion von COM (Component Object Model). Es ermöglicht den direkten Aufruf nativer OS-APIs ohne den P/Invoke-Overhead, den man bei C# hätte.&lt;/li>
&lt;/ol>
&lt;p>An der Basis von C++/WinRT steht COM. Alle WinRT-Objekte sind im Wesentlichen COM-Objekte, die die &lt;code>IUnknown&lt;/code>-Schnittstelle implementieren, und intelligente Zeiger wie &lt;code>winrt::com_ptr&lt;/code> in C++/WinRT verwalten die Referenzzählung (&lt;code>AddRef&lt;/code> / &lt;code>Release&lt;/code>) automatisch.&lt;/p>
&lt;h2 id="4-die-größte-hürde-und-der-durchbruch-in-der-winui-3-entwicklung">4. Die größte Hürde und der Durchbruch in der WinUI 3-Entwicklung
&lt;/h2>&lt;p>Die WinUI 3-Entwicklung mit C++/WinRT ist zwar leistungsstark, bringt jedoch spezifische Komplexitäten mit sich. Hier werde ich zwei große technische Herausforderungen und deren Lösungen, mit denen ich bei der Entwicklung von LogicPad besonders zu kämpfen hatte, detailliert erläutern.&lt;/p>
&lt;h3 id="41-der-schrecken-asynchroner-ui-aktualisierungen-in-c-und-coroutinen">4.1. Der Schrecken asynchroner UI-Aktualisierungen in C++ und Coroutinen
&lt;/h3>&lt;p>Eine eiserne Regel für moderne UI-Anwendungen lautet: „Der UI-Thread darf nicht blockiert werden.“ In LogicPad ist es erforderlich, die Berechnungen für die Simulation riesiger Schaltungen in einem Hintergrund-Thread auszuführen und die Ergebnisse im UI-Thread zu reflektieren.&lt;/p>
&lt;p>Mit C# ließe sich dies mithilfe von &lt;code>async/await&lt;/code> und der &lt;code>DispatcherQueue&lt;/code> relativ einfach umsetzen. In C++ wird dies jedoch durch die Kombination von C++20-Coroutinen (Coroutines) und &lt;code>winrt::apartment_context&lt;/code> erreicht. Ein Verständnis des COM-Apartment-Modells (STA: Single-Threaded Apartment und MTA: Multi-Threaded Apartment) ist hierbei unerlässlich.&lt;/p>
&lt;p>Der folgende Code ist ein Muster, das aus der tatsächlichen Codebasis von LogicPad extrahiert wurde, und das Berechnungen im Hintergrund und die nahtlose Rückkehr zum UI-Thread durchführt.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;winrt/Windows.Foundation.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;winrt/Microsoft.UI.Dispatching.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;winrt/Microsoft.UI.Xaml.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">winrt&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Xaml&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Dispatching&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Event-Handler für den Button-Klick
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">fire_and_forget&lt;/span> &lt;span class="n">MainWindow&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">OnRunSimulationClicked&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">IInspectable&lt;/span> &lt;span class="k">const&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="cm">/* sender */&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RoutedEventArgs&lt;/span> &lt;span class="k">const&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="cm">/* args */&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Den Apartment-Kontext (STA) des aktuellen UI-Threads erfassen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">apartment_context&lt;/span> &lt;span class="n">ui_thread&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// UI-Status aktualisieren (dies wird im UI-Thread ausgeführt)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">StatusTextBlock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">Text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;Simulation läuft...&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ProgressBar&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">IsIndeterminate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Kontextwechsel zum Threadpool (MTA)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">co_await&lt;/span> &lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">resume_background&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Sehr rechenintensive Simulation (wird im Hintergrund-Thread ausgeführt)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Währenddessen ist der UI-Thread frei und die Anwendung friert nicht ein
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">LogicResult&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CoreEngine&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">RunMassiveSimulation&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Simulationsergebnisse als String formatieren (weiterhin im Hintergrund)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">hstring&lt;/span> &lt;span class="n">outputText&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">FormatResults&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">results&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Kontext zurück zum UI-Thread wechseln
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">co_await&lt;/span> &lt;span class="n">ui_thread&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ab hier wird wieder im UI-Thread ausgeführt, sicherer Zugriff auf XAML-Controls
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ResultTextBlock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">Text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">outputText&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ProgressBar&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">IsIndeterminate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">StatusTextBlock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">Text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;Abgeschlossen&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">hresult_error&lt;/span> &lt;span class="k">const&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">ex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Auch bei einer Ausnahme zurück zum UI-Thread, um die Fehlermeldung anzuzeigen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">co_await&lt;/span> &lt;span class="n">ui_thread&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">StatusTextBlock&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">Text&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;Fehler: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">ex&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ProgressBar&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">IsIndeterminate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Das Verhalten von &lt;code>winrt::apartment_context&lt;/code> wirkt wie Magie, aber intern nutzt es die &lt;code>IContextCallback&lt;/code>-Schnittstelle, um sich den ursprünglichen Thread-Kontext zu merken. Beim &lt;code>co_await&lt;/code> kommt ein fortschrittlicher C++-Mechanismus zum Einsatz, der die Verarbeitung an diesen Kontext delegiert (in die Warteschlange einreiht). Dadurch können asynchrone Prozesse im prozeduralen Stil geschrieben werden, ohne in der Callback-Hölle zu landen.&lt;/p>
&lt;h3 id="42-die-vollständige-implementierung-einer-benutzerdefinierten-titelleiste">4.2. Die vollständige Implementierung einer benutzerdefinierten Titelleiste
&lt;/h3>&lt;p>Bei Anwendungen im Zeitalter von Windows 11 ist eine „benutzerdefinierte Titelleiste“, bei der Tabs oder eine Suchleiste im Bereich der Titelleiste (Caption-Bereich) platziert werden, eine wesentliche Anforderung für eine moderne UX. Die Anpassung der Titelleiste in WinUI 3 ist zwar einfach, wenn man nur die Farbe ändern möchte, aber wenn man die Anforderung erfüllen will, „den Client-Bereich in die Titelleiste auszudehnen und dabei das Ziehen des Fensters und die Snap-Layouts (automatisches Anpassen beim Verschieben des Fensters an den Bildschirmrand) beizubehalten“, steigt der Schwierigkeitsgrad enorm.&lt;/p>
&lt;p>In LogicPad habe ich die &lt;code>ExtendsContentIntoTitleBar&lt;/code> API verwendet, um die Titelleiste mit eigenen XAML-Elementen zu erstellen. Der folgende Code zeigt die Schritte zur Anpassung der Titelleiste mithilfe der &lt;code>AppWindow&lt;/code>-Klasse des Windows App SDK.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;winrt/Microsoft.UI.Windowing.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;winrt/Microsoft.UI.Interop.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;microsoft.ui.interop.h&amp;gt;&lt;/span>&lt;span class="cp"> &lt;/span>&lt;span class="c1">// für GetWindowIdFromWindow
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">MainWindow&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">InitializeCustomTitleBar&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// HWND (Window Handle) des aktuellen Fensters abrufen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">windowNative&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">try_as&lt;/span>&lt;span class="o">&amp;lt;::&lt;/span>&lt;span class="n">IWindowNative&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">HWND&lt;/span> &lt;span class="n">hwnd&lt;/span>&lt;span class="p">{&lt;/span> &lt;span class="k">nullptr&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">windowNative&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">get_WindowHandle&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">hwnd&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// HWND in WindowId umwandeln und AppWindow-Instanz abrufen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">WindowId&lt;/span> &lt;span class="n">windowId&lt;/span> &lt;span class="o">=&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GetWindowIdFromWindow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">hwnd&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">appWindow&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Windowing&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AppWindow&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GetFromWindowId&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">windowId&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Prüfen, ob die OS-Version die Anpassung unterstützt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Windowing&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AppWindowTitleBar&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">IsCustomizationSupported&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">titleBar&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">appWindow&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">TitleBar&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Client-Bereich (Inhalt) in die Titelleiste ausdehnen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">titleBar&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ExtendsContentIntoTitleBar&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Hintergrund der Standard-Caption-Buttons (Minimieren, Maximieren, Schließen) transparent machen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">titleBar&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ButtonBackgroundColor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Colors&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Transparent&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">titleBar&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ButtonInactiveBackgroundColor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">winrt&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Microsoft&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">UI&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Colors&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Transparent&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Das im XAML definierte UI-Element (AppTitleBar) als ziehbaren Bereich festlegen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// *Für diesen Teil muss das UIElement auf der XAML-Seite vom Dispatcher des UI-Threads überwacht werden,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// und SetDragRectangles() muss aufgerufen werden, um dem OS den ziehbaren Bereich mitzuteilen.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Die größte Falle bei dieser Implementierung besteht darin, dass jedes Mal, wenn sich die Größe des XAML-UI-Elements ändert (z. B. beim Ändern der Fenstergröße), der Hit-Test-Bereich neu berechnet und das OS mithilfe von &lt;code>InputNonClientPointerSource&lt;/code> oder &lt;code>SetDragRectangles&lt;/code> darüber informiert werden muss: „Dies ist der Bereich, der gezogen werden kann“. Wenn man dies versäumt, treten Fehler auf, wie z. B. dass sich das Fenster beim Ziehen der Titelleiste nicht bewegt oder umgekehrt Klicks auf Schaltflächen fälschlicherweise als Fensterziehen interpretiert werden.&lt;/p>
&lt;h2 id="5-die-tiefen-des-msix-packagings-und-das-appxmanifest">5. Die Tiefen des MSIX-Packagings und das AppXManifest
&lt;/h2>&lt;p>Um das fertiggestellte LogicPad zu vertreiben, muss ein Installer erstellt werden. Anstelle herkömmlicher MSI- oder EXE-Installer habe ich das moderne &lt;strong>MSIX&lt;/strong>-Format gewählt. MSIX sorgt für eine absolut saubere Installation und Deinstallation (ohne die Registry zu verschmutzen) und verfügt zudem über eine automatische Update-Funktion, was es für den Benutzer sehr sicher und komfortabel macht.&lt;/p>
&lt;p>Wenn man jedoch eine in C++ geschriebene native App mit MSIX verpackt, ist die Konfiguration der &lt;code>Package.appxmanifest&lt;/code> (Manifest-Datei) der wichtigste Punkt, auf den man achten muss.&lt;/p>
&lt;p>LogicPad muss große Projektdateien lesen und schreiben können, die im lokalen Dateisystem gespeichert sind (z. B. im Dokumente-Ordner des Benutzers). In der standardmäßigen UWP-Sandbox-Umgebung ist nur der Zugriff auf den isolierten Datenordner der App (AppContainer) erlaubt. Um vollen Zugriff als native Desktop-Anwendung zu erhalten, muss im Manifest die Funktion &lt;code>runFullTrust&lt;/code> deklariert werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;Package&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/manifest/foundation/windows10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:uap=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/manifest/uap/windows10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">xmlns:rescap=&lt;/span>&lt;span class="s">&amp;#34;http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">IgnorableNamespaces=&lt;/span>&lt;span class="s">&amp;#34;uap rescap&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Identity&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;LogicPad.Studio&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Publisher=&lt;/span>&lt;span class="s">&amp;#34;CN=Kenji&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0.0&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;DisplayName&amp;gt;&lt;/span>LogicPad&lt;span class="nt">&amp;lt;/DisplayName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PublisherDisplayName&amp;gt;&lt;/span>Kenji&lt;span class="nt">&amp;lt;/PublisherDisplayName&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Logo&amp;gt;&lt;/span>Assets\StoreLogo.png&lt;span class="nt">&amp;lt;/Logo&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;TargetDeviceFamily&lt;/span> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;Windows.Desktop&amp;#34;&lt;/span> &lt;span class="na">MinVersion=&lt;/span>&lt;span class="s">&amp;#34;10.0.17763.0&amp;#34;&lt;/span> &lt;span class="na">MaxVersionTested=&lt;/span>&lt;span class="s">&amp;#34;10.0.22621.0&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Capabilities&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Allgemeine Funktionseinschränkungen --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Capability&lt;/span> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;internetClient&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Eingeschränkte Funktion zum Ausführen als native Desktop-Anwendung --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;rescap:Capability&lt;/span> &lt;span class="na">Name=&lt;/span>&lt;span class="s">&amp;#34;runFullTrust&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Capabilities&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/Package&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Diese &lt;code>&amp;lt;rescap:Capability Name=&amp;quot;runFullTrust&amp;quot; /&amp;gt;&lt;/code> wird als „eingeschränkte Funktion“ (Restricted Capability) bezeichnet. Bei der Einreichung im Microsoft Store muss ein Begründungsschreiben eingereicht werden, in dem dem Prüfer erklärt wird, warum diese Berechtigung erforderlich ist. Ich erklärte, dass „diese App ein professionelles Werkzeug ist, das beliebige Logikschaltungs-Projektdateien auf der lokalen Festplatte des Benutzers liest, schreibt und exportiert“, und erhielt problemlos die Genehmigung.&lt;/p>
&lt;h2 id="6-der-weg-zum-microsoft-store-und-der-zertifizierungsprozess">6. Der Weg zum Microsoft Store und der Zertifizierungsprozess
&lt;/h2>&lt;p>Sobald die Anwendung fertiggestellt und das MSIX-Paket erstellt ist, folgt schließlich die Einreichung im Microsoft Store. Für einen Einzelentwickler bietet die Verteilung über den Store unschätzbare Vorteile wie die automatische Bereitstellung von Updates, garantierte Zuverlässigkeit und die Nutzung des Zahlungssystems.&lt;/p>
&lt;p>Der Einreichungsprozess im Microsoft Store erfolgt über das Partner Center. Das folgende Flussdiagramm zeigt das Gesamtbild vom Build bis zur Veröffentlichung.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Entwickler (Visual Studio)&amp;#34;] --&amp;gt;|Build &amp;amp; Signieren| B[&amp;#34;MSIX-Bundle-Generierung&amp;#34;]
B --&amp;gt; C[&amp;#34;Windows App Certification Kit (WACK)&amp;#34;]
C --&amp;gt;|Bestanden| D[&amp;#34;Partner Center Dashboard&amp;#34;]
C --&amp;gt;|Nicht bestanden| E[&amp;#34;Code- / Manifest-Probleme beheben&amp;#34;]
E --&amp;gt; A
D --&amp;gt;|Paket hochladen| F[&amp;#34;Store-Zertifizierungsprozess&amp;#34;]
F --&amp;gt; G[&amp;#34;Automatisierter Malware-Scan&amp;#34;]
G --&amp;gt; H[&amp;#34;Manuelle Inhaltsprüfung&amp;#34;]
H --&amp;gt;|Genehmigt| I[&amp;#34;Veröffentlichung im Microsoft Store&amp;#34;]
H --&amp;gt;|Abgelehnt| J[&amp;#34;Feedback bearbeiten &amp;amp; neu einreichen&amp;#34;]
J --&amp;gt; D
I --&amp;gt; K[&amp;#34;App ist live!&amp;#34;]
&lt;/pre>
&lt;h3 id="61-die-hürde-des-wack-windows-app-certification-kit">6.1. Die Hürde des WACK (Windows App Certification Kit)
&lt;/h3>&lt;p>Bevor Sie in das Partner Center hochladen, müssen Sie lokal das &lt;strong>WACK (Windows App Certification Kit)&lt;/strong> ausführen und den Vorabtest bestehen. WACK ist ein Tool, das automatisch testet, ob die App abstürzt, ob unzulässige APIs aufgerufen werden und ob die Leistungsanforderungen erfüllt sind.&lt;/p>
&lt;p>Bei einer nativen C++-App ist insbesondere der Fehler „Verwendung nicht unterstützter APIs“ zu beachten. Wenn eine ältere C++-Bibliothek eines Drittanbieters statisch verlinkt ist, werden möglicherweise veraltete Win32-APIs innerhalb dieser Bibliothek verwendet, was dazu führen kann, dass die App bei der WACK-Prüfung abgelehnt wird. Um dieses Problem zu umgehen, habe ich die abhängigen Bibliotheken auf die neueste Version aktualisiert und einige Funktionen in Ersatz-APIs umgeschrieben, die vom Windows App SDK bereitgestellt werden.&lt;/p>
&lt;h3 id="62-zertifizierung-und-veröffentlichung">6.2. Zertifizierung und Veröffentlichung
&lt;/h3>&lt;p>Die Konfiguration im Partner Center umfasst die Preisgestaltung der App, die Alterseinstufung (IARC-Rating), sowie Screenshots und Beschreibungen für den Store. Da LogicPad ein technisches Werkzeug ist, konnte ich sofort ein Rating für alle Altersgruppen erhalten.&lt;/p>
&lt;p>Vom Einreichen des Pakets bis zum Abschluss der Prüfung dauerte es etwa drei Werktage. Nach dem automatischen Malware-Scan und der Funktionsprüfung führte das Microsoft-Prüfungsteam eine manuelle Überprüfung der Funktionalität durch. Auch die Berechtigungsanfrage für &lt;code>runFullTrust&lt;/code> wurde problemlos genehmigt, und das Gefühl der Erfüllung in dem Moment, als der Status endlich auf „Veröffentlicht“ (In the Store) wechselte, war unersetzlich.&lt;/p>
&lt;h2 id="7-persönliche-entwicklung-als-business-mathematische-modelle-für-leistung-und-einnahmen">7. Persönliche Entwicklung als Business: Mathematische Modelle für Leistung und Einnahmen
&lt;/h2>&lt;p>Um sich nicht einfach mit der Erstellung einer App zufrieden zu geben, sondern LogicPad kontinuierlich zu aktualisieren und als Geschäft aufzubauen, ist es notwendig, sowohl technische als auch geschäftliche Kennzahlen quantitativ zu bewerten.&lt;/p>
&lt;h3 id="71-das-von-c-ermöglichte-modell-zur-optimierung-der-speichernutzung">7.1. Das von C++ ermöglichte Modell zur Optimierung der Speichernutzung
&lt;/h3>&lt;p>Die größte Stärke von LogicPad ist seine extreme Leichtgewichtigkeit im Vergleich zu Electron-basierten Editoren (z. B. VSCode). Der Speicherbedarf der Anwendung, $M_{total}$, kann wie folgt modelliert werden:&lt;/p>
$$
M_{total} = M_{UI} + M_{engine} + M_{cache}
$$&lt;p>Hierbei ist $M_{UI}$ durch das native Rendering von WinUI 3 dramatisch kleiner als bei Electron, welches eine Browser-Engine laden muss (nur etwa 50 MB).
Darüber hinaus skaliert der Speicher der C++-Engine $M_{engine}$ linear mit der Anzahl der Logikgatter $N$, dank optimierter Strukturen und der Eliminierung von Zeigern.&lt;/p>
$$
M_{engine} = N \times \text{sizeof(LogicNode)}
$$&lt;p>Durch die Verwendung des &lt;code>#pragma pack&lt;/code> in C++ zur Optimierung der Strukturausrichtung konnte der Speicher pro Knoten auf das absolute Minimum reduziert werden.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#pragma pack(push, 1)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1">// Keine virtuelle Funktionstabelle (vtable) und Packen, um den Speicher zu minimieren
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">LogicNode&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint32_t&lt;/span> &lt;span class="n">id&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 4 Bytes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint16_t&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 2 Bytes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">isActive&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// 1 Byte
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Die Strukturgröße beträgt 7 Bytes (ohne Alignment-Padding)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#pragma pack(pop)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Da die Speicher-Schicht für den Cache $M_{cache}$ den Simulationsverlauf beibehält, wächst sie proportional zu $\mathcal{O}(N \log N)$. Da die Basis-Speichernutzung jedoch so gering ist, bleibt der RAM-Verbrauch des gesamten Systems selbst bei Schaltungen mit Zehntausenden von Knoten unter 200 MB.&lt;/p>
&lt;h3 id="72-ltv-und-cac-kalkulation-des-marketings">7.2. LTV und CAC: Kalkulation des Marketings
&lt;/h3>&lt;p>Bei der Monetarisierungsstrategie in der individuellen Entwicklung ist das Gleichgewicht zwischen Customer Lifetime Value (LTV) und Customer Acquisition Cost (CAC) alles. LogicPad verwendet kein Abonnement, sondern ein Kauflizenzmodell (Freemium).&lt;/p>
&lt;p>Der LTV wird berechnet als die Summe der Barwerte der Wahrscheinlichkeiten zukünftiger Upgrade-Käufe, abgewertet mit dem Abzinsungssatz $d$. Über den Zeitraum $T$ lässt sich dies mit folgender Formel ausdrücken:&lt;/p>
$$
LTV = \sum_{t=1}^{T} \frac{ARPU_t \times Margin}{(1+d)^t}
$$&lt;p>Auf der anderen Seite sind die CAC die gesamten Marketingkosten für Dinge wie Twitter-Werbung (jetzt X) oder Traffic von Blog-Beiträgen, geteilt durch die Anzahl der neuen Benutzer.&lt;/p>
$$
CAC = \frac{Total\ Marketing\ Spend}{Number\ of\ New\ Users}
$$&lt;p>Der Vorteil der persönlichen Entwicklung besteht darin, dass die für die Entwicklung aufgewendeten Personalkosten als „Hobbyzeit“ und somit als versunkene Kosten (Sunk Costs) betrachtet werden können. Daher können die CAC rein auf der Basis der tatsächlichen Marketingausgaben berechnet werden. Derzeit erzielen wir dank eines organischen Wachstums, das sich auf Mundpropaganda in Nischen-Tech-Communitys stützt, eine gesunde Unit-Economics von $LTV > CAC$ bei einem Status nahe $CAC \approx 0$.&lt;/p>
&lt;h2 id="8-fazit-die-ungeschliffene-aber-schöne-welt-der-nativen-app-entwicklung">8. Fazit: Die ungeschliffene, aber schöne Welt der nativen App-Entwicklung
&lt;/h2>&lt;p>Wenn man auf den Weg von der Entwicklung von LogicPad bis zur Veröffentlichung im Microsoft Store zurückblickt, war es sicherlich kein einfacher Weg – vom Kampf mit unverständlichen Kompilierfehlern in C++/WinRT über das Aufspüren von Speicherlecks aufgrund von COM-Referenzzählungs-Bugs bis hin zur Untersuchung der XML-Spezifikationen des MSIX-Manifests.&lt;/p>
&lt;p>Aber gerade weil wir uns durch die Entwicklung der Webtechnologien in einem Zeitalter befinden, in dem „alles im Browser gemacht werden kann“, stärkt die Erfahrung der nativen Entwicklung, bei der man die OS-APIs direkt aufruft und auf jedes Byte Speicher und jeden CPU-Takt achtet, die grundlegenden Fähigkeiten eines Ingenieurs ungemein.&lt;/p>
&lt;p>WinUI 3 und das Windows App SDK werden derzeit aktiv weiterentwickelt und sind die besten Werkzeuge, um schöne Anwendungen zu erstellen, die das UI-Paradigma von Windows 11 voll ausschöpfen. Ich hoffe aufrichtig, dass dieser Blogbeitrag Entwicklern, die die Entwicklung von nativen Windows-Apps in Angriff nehmen wollen, eine Hilfe sein wird und dass dadurch noch mehr großartige Apps im Store erscheinen.&lt;/p>
&lt;p>Die Entwicklung ist noch nicht abgeschlossen. Für die nächste Version von LogicPad ist die Integration einer eigenen Wellenform-Rendering-Engine unter Verwendung von Direct2D geplant. Im nächsten Artikel werde ich tief in die Interoperabilität zwischen DirectX und WinUI 3 (Nutzung des SwapChainPanels) eintauchen. Bleiben Sie gespannt.&lt;/p></description></item></channel></rss>