<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>App Development on kenji.blog</title><link>http://kenji.blog/fr/categories/app-development/</link><description>Recent content in App Development on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 04:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/fr/categories/app-development/index.xml" rel="self" type="application/rss+xml"/><item><title>Apprendre du développement de LogicPad : le parcours de la création et du lancement d'une application Windows personnelle</title><link>http://kenji.blog/fr/p/logicpad-windows-app-development-journey/</link><pubDate>Sun, 13 Sep 2026 04:00:00 +0900</pubDate><guid>http://kenji.blog/fr/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 Apprendre du développement de LogicPad : le parcours de la création et du lancement d'une application Windows personnelle" />&lt;h2 id="1-introduction--pourquoi-créer-une-application-native-windows-aujourdhui">1. Introduction : pourquoi créer une application native Windows aujourd&amp;rsquo;hui
&lt;/h2>&lt;p>Il est indéniable que dans le développement d&amp;rsquo;applications modernes, les technologies multiplateformes telles qu&amp;rsquo;Electron, Tauri et React Native sont devenues la norme. L&amp;rsquo;approche consistant à utiliser les technologies web pour écrire une fois et exécuter partout (Write Once, Run Anywhere) est extrêmement rationnelle du point de vue de la vitesse de développement et de la maintenabilité. Cependant, j&amp;rsquo;ai délibérément choisi de développer &amp;ldquo;LogicPad&amp;rdquo; en tant qu&amp;rsquo;application native entièrement optimisée pour Windows.&lt;/p>
&lt;p>LogicPad est un simulateur de circuits logiques numériques et un éditeur de texte ciblant les ingénieurs matériel et les étudiants en circuits logiques. Il doit simuler des dizaines de milliers de portes logiques en temps réel tout en effectuant un rendu de données de formes d&amp;rsquo;ondes complexes sans latence. Dans ce domaine nécessitant des performances extrêmes, les pauses de quelques millisecondes (micro-saccades) causées par le ramasse-miettes (garbage collection) et le surcoût de rendu des vues web entraînent une dégradation fatale de l&amp;rsquo;expérience utilisateur.&lt;/p>
&lt;p>Cet article retrace le parcours du projet LogicPad, depuis sa conception jusqu&amp;rsquo;à l&amp;rsquo;implémentation en C++ et WinUI 3 (Windows App SDK), la résolution des défis techniques spécifiques, l&amp;rsquo;empaquetage MSIX et enfin la distribution mondiale via le Microsoft Store, avec des explications techniques très détaillées. J&amp;rsquo;espère qu&amp;rsquo;en partageant le processus par lequel un développeur indépendant peut créer une application Windows native de qualité professionnelle, cela servira de guide à ceux qui souhaitent également se lancer dans le développement natif.&lt;/p>
&lt;h2 id="2-chronologie-du-projet">2. Chronologie du projet
&lt;/h2>&lt;p>Le développement de LogicPad a été mené comme un projet personnel, profitant des week-ends et des soirées. La chronologie globale s&amp;rsquo;étend sur environ six mois. Voici un diagramme de Gantt illustrant l&amp;rsquo;avancement de ce projet.&lt;/p>
&lt;pre class="mermaid">
gantt
title &amp;#34;Chronologie de développement de LogicPad&amp;#34;
dateFormat YYYY-MM-DD
section &amp;#34;Planification &amp;amp; Conception&amp;#34;
&amp;#34;Analyse des besoins et prototypage&amp;#34; :a1, 2025-10-01, 30d
&amp;#34;Conception de l&amp;#39;architecture (C++ Core)&amp;#34; :a2, after a1, 20d
section &amp;#34;Développement de base&amp;#34;
&amp;#34;Moteur de simulation (C++)&amp;#34; :a3, after a2, 45d
&amp;#34;Intégration WinUI 3 XAML&amp;#34; :a4, after a3, 40d
section &amp;#34;Amélioration UI/UX&amp;#34;
&amp;#34;Barre de titre personnalisée et fenêtrage&amp;#34; :a5, after a4, 15d
&amp;#34;Répartiteur asynchrone et multithreading&amp;#34; :a6, after a5, 15d
section &amp;#34;Publication et Store&amp;#34;
&amp;#34;Empaquetage MSIX et capacités&amp;#34; :a7, after a6, 10d
&amp;#34;Certification Microsoft Store&amp;#34; :a8, after a7, 14d
&lt;/pre>
&lt;p>Ainsi, la majeure partie du temps de développement a été consacrée à l&amp;rsquo;optimisation du moteur de base et à l&amp;rsquo;intégration du traitement asynchrone entre WinUI 3 et C++. Bien que le développement natif implique une configuration initiale et une courbe d&amp;rsquo;apprentissage plus élevées par rapport au développement multiplateforme, cet investissement est assurément rentabilisé par les performances finales.&lt;/p>
&lt;h2 id="3-choix-technologiques--au-cœur-de-c--winui-3--windows-app-sdk">3. Choix technologiques : au cœur de C++ / WinUI 3 / Windows App SDK
&lt;/h2>&lt;p>Lors du développement de LogicPad, le choix de la pile technologique a été l&amp;rsquo;une des décisions les plus importantes. Historiquement, il existe diverses options pour les frameworks d&amp;rsquo;interface utilisateur native sur la plateforme Windows, telles que l&amp;rsquo;API Win32 (User32/GDI), MFC, Windows Forms, WPF et UWP. Actuellement, la recommandation de Microsoft pour le développement moderne d&amp;rsquo;applications de bureau Windows est &lt;strong>WinUI 3&lt;/strong>, qui est inclus dans le &lt;strong>Windows App SDK&lt;/strong>.&lt;/p>
&lt;h3 id="31-architecture-du-windows-app-sdk-et-winui-3">3.1. Architecture du Windows App SDK et WinUI 3
&lt;/h3>&lt;p>Le Windows App SDK est un ensemble de bibliothèques permettant de fournir les dernières API Windows indépendamment de la version du système d&amp;rsquo;exploitation. Contrairement à l&amp;rsquo;ancienne UWP (Universal Windows Platform) qui était fortement liée aux mises à jour de l&amp;rsquo;OS, le Windows App SDK est distribué avec l&amp;rsquo;application, ce qui garantit un comportement cohérent de Windows 10 (version 1809 et ultérieures) à Windows 11.&lt;/p>
&lt;p>WinUI 3 est un framework d&amp;rsquo;interface utilisateur native fonctionnant sur ce Windows App SDK et prenant entièrement en charge le Fluent Design System. L&amp;rsquo;intérieur de WinUI 3 est construit avec C++ et DirectX, ce qui le rend extrêmement rapide.&lt;/p>
&lt;h3 id="32-pourquoi-choisir-délibérément-c-cwinrt-plutôt-que-c">3.2. Pourquoi choisir délibérément C++ (C++/WinRT) plutôt que C#
&lt;/h3>&lt;p>C# et C++ sont tous deux pris en charge comme langages de développement pour WinUI 3. Bien que l&amp;rsquo;utilisation de C# et .NET améliore considérablement l&amp;rsquo;efficacité du développement, &lt;strong>C++/WinRT&lt;/strong> a été adopté pour LogicPad pour les raisons suivantes :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Gestion déterministe de la mémoire&lt;/strong> : l&amp;rsquo;absence de ramasse-miettes (GC) permet de contrôler totalement le moment de l&amp;rsquo;allocation et de la libération de la mémoire. Cela empêche les pauses du GC pendant la boucle de simulation.&lt;/li>
&lt;li>&lt;strong>SIMD et optimisation du cache&lt;/strong> : en C++, la disposition physique de la mémoire (comme les Struct of Arrays) peut être strictement définie, maximisant ainsi le taux de réussite du cache CPU.&lt;/li>
&lt;li>&lt;strong>Frontières ABI natives&lt;/strong> : C++/WinRT est une projection C++ moderne de COM (Component Object Model). Il permet d&amp;rsquo;appeler directement les API natives de l&amp;rsquo;OS sans la surcharge du P/Invoke comme en C#.&lt;/li>
&lt;/ol>
&lt;p>À la base de C++/WinRT se trouve COM. Tous les objets WinRT sont intrinsèquement des objets COM implémentant l&amp;rsquo;interface &lt;code>IUnknown&lt;/code>, et les pointeurs intelligents comme &lt;code>winrt::com_ptr&lt;/code> de C++/WinRT gèrent automatiquement le comptage de références (&lt;code>AddRef&lt;/code> / &lt;code>Release&lt;/code>).&lt;/p>
&lt;h2 id="4-les-plus-grands-obstacles-et-percées-dans-le-développement-winui-3">4. Les plus grands obstacles et percées dans le développement WinUI 3
&lt;/h2>&lt;p>Bien que puissant, le développement WinUI 3 utilisant C++/WinRT s&amp;rsquo;accompagne d&amp;rsquo;une complexité spécifique. J&amp;rsquo;explique ici en détail deux défis techniques majeurs rencontrés lors du développement de LogicPad, ainsi que leurs solutions.&lt;/p>
&lt;h3 id="41-la-terreur-des-mises-à-jour-asynchrones-de-linterface-utilisateur-en-c-et-les-coroutines">4.1. La terreur des mises à jour asynchrones de l&amp;rsquo;interface utilisateur en C++ et les coroutines
&lt;/h3>&lt;p>La règle d&amp;rsquo;or des applications d&amp;rsquo;interface utilisateur modernes est : &amp;ldquo;ne jamais bloquer le thread de l&amp;rsquo;interface utilisateur&amp;rdquo;. Dans LogicPad, d&amp;rsquo;énormes calculs de simulation de circuits doivent être exécutés dans un thread en arrière-plan, et les résultats doivent être reflétés dans le thread de l&amp;rsquo;interface utilisateur.&lt;/p>
&lt;p>En C#, cela peut être écrit assez facilement en utilisant &lt;code>async/await&lt;/code> et &lt;code>DispatcherQueue&lt;/code>, mais en C++, cela est réalisé en combinant les coroutines C++20 avec &lt;code>winrt::apartment_context&lt;/code>. La compréhension du modèle d&amp;rsquo;appartement de COM (STA : Single-Threaded Apartment et MTA : Multi-Threaded Apartment) est essentielle.&lt;/p>
&lt;p>Le code suivant est un modèle extrait de la base de code réelle de LogicPad, effectuant de manière transparente des calculs en arrière-plan puis revenant au thread de l&amp;rsquo;interface utilisateur.&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">// Gestionnaire d&amp;#39;événement de clic de bouton
&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">// Capturer le contexte d&amp;#39;appartement (STA) du thread de l&amp;#39;interface utilisateur actuel
&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">// Mettre à jour le statut de l&amp;#39;interface utilisateur (exécuté sur le thread UI)
&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 en cours...&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">// Déplacer le contexte vers le pool de threads (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">// Processus de simulation très lourd (exécuté dans un thread en arrière-plan)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Pendant ce temps, le thread de l&amp;#39;interface utilisateur est libéré, évitant le gel de l&amp;#39;application
&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">// Formater le résultat de la simulation en chaîne de caractères (toujours exécuté en arrière-plan)
&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">// Ramener le contexte au thread de l&amp;#39;interface utilisateur
&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">// Désormais exécuté sur le thread UI, l&amp;#39;accès aux contrôles XAML est sûr
&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;Terminé&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">// En cas d&amp;#39;exception, retourner au thread UI pour afficher le message d&amp;#39;erreur
&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;Erreur : &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>Le comportement de ce &lt;code>winrt::apartment_context&lt;/code> ressemble à de la magie, mais en interne, un mécanisme C++ avancé est à l&amp;rsquo;œuvre : il utilise l&amp;rsquo;interface &lt;code>IContextCallback&lt;/code> pour mémoriser le contexte de thread d&amp;rsquo;origine et, lors du &lt;code>co_await&lt;/code>, répartit (place en file d&amp;rsquo;attente) le traitement vers ce contexte. Cela permet d&amp;rsquo;écrire des processus asynchrones avec l&amp;rsquo;apparence d&amp;rsquo;un code procédural sans tomber dans l&amp;rsquo;enfer des rappels (callback hell).&lt;/p>
&lt;h3 id="42-implémentation-complète-de-la-barre-de-titre-personnalisée">4.2. Implémentation complète de la barre de titre personnalisée
&lt;/h3>&lt;p>Dans les applications de l&amp;rsquo;ère Windows 11, une &amp;ldquo;barre de titre personnalisée&amp;rdquo; (la zone de légende de la fenêtre) plaçant des onglets ou une barre de recherche est une exigence essentielle pour une UX moderne. Cependant, dans WinUI 3, changer simplement la couleur de la barre de titre est facile, mais la difficulté augmente considérablement si l&amp;rsquo;on souhaite satisfaire l&amp;rsquo;exigence &amp;ldquo;d&amp;rsquo;étendre la zone client dans la barre de titre tout en conservant le déplacement par glissement de la fenêtre et la disposition d&amp;rsquo;ancrage (redimensionnement automatique lorsqu&amp;rsquo;on colle la fenêtre au bord de l&amp;rsquo;écran)&amp;rdquo;.&lt;/p>
&lt;p>Dans LogicPad, j&amp;rsquo;ai utilisé l&amp;rsquo;API &lt;code>ExtendsContentIntoTitleBar&lt;/code> pour construire la barre de titre avec des éléments XAML personnalisés. Le code suivant est la procédure de personnalisation de la barre de titre à l&amp;rsquo;aide de la classe &lt;code>AppWindow&lt;/code> du 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">// for 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">// Obtenir le HWND (handle de la fenêtre) de la fenêtre actuelle
&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">// Convertir HWND en WindowId et obtenir l&amp;#39;instance AppWindow
&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">// Vérifier si la version de l&amp;#39;OS prend en charge la personnalisation
&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">// Étendre la zone client (contenu) dans la barre de titre
&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">// Rendre l&amp;#39;arrière-plan des boutons de légende par défaut (minimiser, maximiser, fermer) transparent
&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">// Définir l&amp;#39;élément UI défini côté XAML (AppTitleBar) comme zone de glissement
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// *Pour les détails de cette partie, il faut surveiller le UIElement côté XAML avec le Dispatcher du thread UI,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// et appeler SetDragRectangles() pour indiquer à l&amp;#39;OS la zone glissable.
&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>Le plus grand piège de cette implémentation est que chaque fois que la taille de l&amp;rsquo;élément UI côté XAML change (par exemple lors du redimensionnement de la fenêtre), il faut recalculer la zone de test d&amp;rsquo;impact à l&amp;rsquo;aide de &lt;code>InputNonClientPointerSource&lt;/code> ou &lt;code>SetDragRectangles&lt;/code> et notifier l&amp;rsquo;OS &amp;ldquo;voici la zone qui peut être glissée&amp;rdquo;. Si l&amp;rsquo;on omet cela, des bugs surviennent, comme l&amp;rsquo;impossibilité de déplacer la fenêtre en glissant la barre de titre, ou au contraire, une tentative de cliquer sur un bouton considérée comme un glissement de fenêtre.&lt;/p>
&lt;h2 id="5-les-profondeurs-de-lempaquetage-msix-et-appxmanifest">5. Les profondeurs de l&amp;rsquo;empaquetage MSIX et AppXManifest
&lt;/h2>&lt;p>Pour distribuer LogicPad une fois le développement terminé, il est nécessaire de créer un programme d&amp;rsquo;installation. Au lieu des installateurs traditionnels MSI ou EXE, j&amp;rsquo;ai adopté le format moderne &lt;strong>MSIX&lt;/strong>. Le MSIX est très sûr et confortable pour l&amp;rsquo;utilisateur, car l&amp;rsquo;installation et la désinstallation se font proprement (sans polluer le registre) et il dispose d&amp;rsquo;une fonctionnalité de mise à jour automatique.&lt;/p>
&lt;p>Cependant, lors de l&amp;rsquo;empaquetage d&amp;rsquo;une application native écrite en C++ avec MSIX, la configuration du &lt;code>Package.appxmanifest&lt;/code> (fichier manifeste) est ce qui nécessite le plus d&amp;rsquo;attention.&lt;/p>
&lt;p>LogicPad doit lire et écrire d&amp;rsquo;énormes fichiers de projets sauvegardés dans le système de fichiers local (comme le dossier Documents de l&amp;rsquo;utilisateur). Dans l&amp;rsquo;environnement sandbox standard de l&amp;rsquo;UWP, seule l&amp;rsquo;accès au dossier de données isolé de l&amp;rsquo;application (AppContainer) est possible. Pour obtenir des privilèges d&amp;rsquo;accès complets en tant qu&amp;rsquo;application de bureau native, il faut déclarer la capacité &lt;code>runFullTrust&lt;/code> dans le manifeste.&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;!-- Capacités générales --&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;!-- Capacité restreinte pour fonctionner comme une application de bureau native --&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>Ce &lt;code>&amp;lt;rescap:Capability Name=&amp;quot;runFullTrust&amp;quot; /&amp;gt;&lt;/code> est appelé une &amp;ldquo;capacité restreinte&amp;rdquo; (Restricted Capability), et lors de la soumission au Microsoft Store, vous devez soumettre un document de justification aux examinateurs expliquant pourquoi cette autorisation est requise. J&amp;rsquo;ai expliqué que &amp;ldquo;cette application est un outil pour les professionnels qui lit, écrit et exporte des fichiers de projets de circuits logiques arbitraires sur le disque local de l&amp;rsquo;utilisateur&amp;rdquo;, et elle a été approuvée sans problème.&lt;/p>
&lt;h2 id="6-le-chemin-vers-le-microsoft-store-et-le-processus-de-certification">6. Le chemin vers le Microsoft Store et le processus de certification
&lt;/h2>&lt;p>Une fois l&amp;rsquo;application terminée et le package MSIX construit, il est enfin temps de la soumettre au Microsoft Store. Pour les développeurs indépendants, la distribution via le Store présente des avantages inestimables, tels que la distribution automatique des mises à jour, la garantie de fiabilité et l&amp;rsquo;utilisation du système de paiement.&lt;/p>
&lt;p>Le processus de soumission au Microsoft Store se fait via le Partner Center. L&amp;rsquo;organigramme suivant illustre l&amp;rsquo;ensemble du processus, de la construction à la publication.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Développeur (Visual Studio)&amp;#34;] --&amp;gt;|Construction et signature| B[&amp;#34;Génération du bundle MSIX&amp;#34;]
B --&amp;gt; C[&amp;#34;Windows App Certification Kit (WACK)&amp;#34;]
C --&amp;gt;|Succès| D[&amp;#34;Tableau de bord du Partner Center&amp;#34;]
C --&amp;gt;|Échec| E[&amp;#34;Correction du code / Problèmes de manifeste&amp;#34;]
E --&amp;gt; A
D --&amp;gt;|Téléversement du package| F[&amp;#34;Processus de certification du Store&amp;#34;]
F --&amp;gt; G[&amp;#34;Analyse automatique des logiciels malveillants&amp;#34;]
G --&amp;gt; H[&amp;#34;Examen manuel du contenu&amp;#34;]
H --&amp;gt;|Approuvé| I[&amp;#34;Publication sur le Microsoft Store&amp;#34;]
H --&amp;gt;|Rejeté| J[&amp;#34;Traiter les retours et soumettre à nouveau&amp;#34;]
J --&amp;gt; D
I --&amp;gt; K[&amp;#34;L&amp;#39;application est en ligne !&amp;#34;]
&lt;/pre>
&lt;h3 id="61-le-mur-du-wack-windows-app-certification-kit">6.1. Le mur du WACK (Windows App Certification Kit)
&lt;/h3>&lt;p>Avant de télécharger sur le Partner Center, vous devez exécuter le &lt;strong>WACK (Windows App Certification Kit)&lt;/strong> localement pour réussir le pré-test. Le WACK est un outil qui teste automatiquement si l&amp;rsquo;application ne plante pas, n&amp;rsquo;appelle pas d&amp;rsquo;API non autorisées et répond aux exigences de performance.&lt;/p>
&lt;p>Pour les applications natives C++, ce qu&amp;rsquo;il faut particulièrement surveiller, c&amp;rsquo;est l&amp;rsquo;erreur &amp;ldquo;Utilisation d&amp;rsquo;API non prises en charge&amp;rdquo;. Si vous liez statiquement d&amp;rsquo;anciennes bibliothèques C++ tierces, elles peuvent utiliser des API Win32 obsolètes en interne, ce qui peut vous faire recaler lors de l&amp;rsquo;examen WACK. Pour contourner ce problème, j&amp;rsquo;ai mis à jour les bibliothèques dépendantes vers leurs dernières versions et réécrit certaines fonctions vers des API alternatives fournies par le Windows App SDK.&lt;/p>
&lt;h3 id="62-examen-et-publication">6.2. Examen et publication
&lt;/h3>&lt;p>La configuration dans le Partner Center comprend la tarification de l&amp;rsquo;application, les classifications par âge (notation IARC), et la saisie de captures d&amp;rsquo;écran et de descriptions pour le Store. Comme LogicPad est un outil technique, j&amp;rsquo;ai pu obtenir immédiatement une classification &amp;ldquo;pour tous les âges&amp;rdquo;.&lt;/p>
&lt;p>Il a fallu environ trois jours ouvrables pour que l&amp;rsquo;examen soit terminé après la soumission du package. Après l&amp;rsquo;analyse automatique des malwares et la vérification des fonctionnalités, une vérification manuelle du comportement est effectuée par l&amp;rsquo;équipe d&amp;rsquo;examen de Microsoft. La demande d&amp;rsquo;autorisation &lt;code>runFullTrust&lt;/code> est également passée sans problème, et le sentiment d&amp;rsquo;accomplissement au moment où le statut est passé à &amp;ldquo;Publié&amp;rdquo; (In the Store) était irremplaçable.&lt;/p>
&lt;h2 id="7-le-développement-indépendant-comme-entreprise--modèles-mathématiques-de-performance-et-de-rentabilité">7. Le développement indépendant comme entreprise : modèles mathématiques de performance et de rentabilité
&lt;/h2>&lt;p>Pour ne pas simplement se contenter d&amp;rsquo;avoir créé une application, mais mettre à jour continuellement LogicPad et en faire une activité viable, il est nécessaire d&amp;rsquo;évaluer quantitativement les indicateurs techniques et commerciaux.&lt;/p>
&lt;h3 id="71-modèle-doptimisation-de-lutilisation-de-la-mémoire-apporté-par-c">7.1. Modèle d&amp;rsquo;optimisation de l&amp;rsquo;utilisation de la mémoire apporté par C++
&lt;/h3>&lt;p>Le plus grand atout de LogicPad est qu&amp;rsquo;il est extrêmement léger par rapport aux éditeurs basés sur Electron (ex: VSCode). L&amp;rsquo;empreinte mémoire totale de l&amp;rsquo;application $M_{total}$ peut être modélisée comme suit :&lt;/p>
$$
M_{total} = M_{UI} + M_{engine} + M_{cache}
$$&lt;p>Ici, $M_{UI}$, grâce au rendu natif de WinUI 3, est considérablement plus petit par rapport à Electron qui charge un moteur de navigation (environ 50 Mo).
De plus, la mémoire de la partie moteur C++ $M_{engine}$ évolue linéairement par rapport au nombre de portes logiques $N$, grâce à des structures optimisées et à l&amp;rsquo;élimination des pointeurs.&lt;/p>
$$
M_{engine} = N \times \text{sizeof(LogicNode)}
$$&lt;p>En utilisant &lt;code>#pragma pack&lt;/code> en C++ pour optimiser l&amp;rsquo;alignement des structures, j&amp;rsquo;ai réduit la mémoire par nœud à son extrême limite.&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">// Minimiser la mémoire en compactant sans avoir de table de fonctions virtuelles (vtable)
&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 octets
&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 octets
&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 octet
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// La taille de la structure est de 7 octets (sans remplissage d&amp;#39;alignement)
&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>La mémoire de la couche de cache $M_{cache}$ augmente proportionnellement à $\mathcal{O}(N \log N)$ pour conserver l&amp;rsquo;historique de simulation, mais comme l&amp;rsquo;empreinte de base est petite, même avec un circuit de dizaines de milliers de nœuds, l&amp;rsquo;utilisation globale de la RAM du système reste inférieure à 200 Mo.&lt;/p>
&lt;h3 id="72-ltv-et-cac--les-calculs-du-marketing">7.2. LTV et CAC : les calculs du marketing
&lt;/h3>&lt;p>Dans la stratégie de monétisation pour le développement indépendant, l&amp;rsquo;équilibre entre la valeur vie client (LTV : Lifetime Value) et le coût d&amp;rsquo;acquisition client (CAC : Customer Acquisition Cost) est primordial. LogicPad n&amp;rsquo;utilise pas un modèle d&amp;rsquo;abonnement, mais un modèle de licence à achat unique (Freemium).&lt;/p>
&lt;p>La LTV est calculée comme la somme de la valeur actualisée de la probabilité d&amp;rsquo;acheter de futures versions de mise à niveau, actualisée par le taux d&amp;rsquo;actualisation $d$. Pour une période $T$, cela est exprimé par la formule suivante :&lt;/p>
$$
LTV = \sum_{t=1}^{T} \frac{ARPU_t \times Margin}{(1+d)^t}
$$&lt;p>D&amp;rsquo;un autre côté, le CAC est le montant total des coûts de marketing engagés via des publicités sur Twitter (actuellement X) ou le flux provenant d&amp;rsquo;articles de blog, divisé par le nombre de nouveaux utilisateurs.&lt;/p>
$$
CAC = \frac{D\acute{e}penses\ de\ marketing\ totales}{Nombre\ de\ nouveaux\ utilisateurs}
$$&lt;p>La force du développement indépendant réside dans le fait que le coût de la main-d&amp;rsquo;œuvre pour le développement peut être considéré comme des coûts irrécupérables en tant que &amp;ldquo;temps de loisir&amp;rdquo;, de sorte que le CAC peut être calculé uniquement sur la base des coûts de marketing purs. Actuellement, grâce à un flux organique centré sur le bouche-à-oreille dans des communautés techniques de niche, nous atteignons une économie unitaire saine avec $LTV > CAC$ dans un état proche de $CAC \approx 0$.&lt;/p>
&lt;h2 id="8-conclusion--le-monde-fastidieux-mais-magnifique-du-développement-dapplications-natives">8. Conclusion : le monde fastidieux mais magnifique du développement d&amp;rsquo;applications natives
&lt;/h2>&lt;p>En regardant en arrière sur le parcours depuis le développement de LogicPad jusqu&amp;rsquo;à sa sortie sur le Microsoft Store, ce ne fut en aucun cas un chemin facile, avec la lutte contre les erreurs de compilation complexes de C++/WinRT, la traque des fuites de mémoire dues aux bugs de comptage de références COM, et la recherche des spécifications XML du manifeste MSIX.&lt;/p>
&lt;p>C&amp;rsquo;est précisément parce que l&amp;rsquo;évolution des technologies web a rendu possible de &amp;ldquo;tout créer dans le navigateur&amp;rdquo; que l&amp;rsquo;expérience du développement natif, qui appelle directement les API de l&amp;rsquo;OS et prête attention à chaque octet de mémoire et à chaque cycle d&amp;rsquo;horloge du CPU, améliore considérablement les capacités fondamentales en tant qu&amp;rsquo;ingénieur.&lt;/p>
&lt;p>WinUI 3 et le Windows App SDK sont toujours activement développés et constituent les meilleurs outils pour créer de belles applications qui tirent pleinement parti du paradigme d&amp;rsquo;interface utilisateur de Windows 11. J&amp;rsquo;espère sincèrement que cet article de blog aidera les développeurs qui sont sur le point de relever le défi du développement d&amp;rsquo;applications Windows natives, et qu&amp;rsquo;il contribuera à l&amp;rsquo;arrivée d&amp;rsquo;encore plus d&amp;rsquo;applications fantastiques sur le Store.&lt;/p>
&lt;p>Le développement n&amp;rsquo;est pas encore terminé. Dans la prochaine version de LogicPad, nous prévoyons d&amp;rsquo;intégrer notre propre moteur de rendu de formes d&amp;rsquo;ondes tirant parti de Direct2D. Dans le prochain article, nous approfondirons l&amp;rsquo;interopérabilité entre DirectX et WinUI 3 (utilisation de SwapChainPanel). Restez à l&amp;rsquo;écoute !&lt;/p></description></item></channel></rss>