<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>WinUI 3 on kenji.blog</title><link>http://kenji.blog/es/tags/winui-3/</link><description>Recent content in WinUI 3 on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>es</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 04:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/es/tags/winui-3/index.xml" rel="self" type="application/rss+xml"/><item><title>Aprendiendo del desarrollo de LogicPad: El camino para lanzar una aplicación de Windows como proyecto personal</title><link>http://kenji.blog/es/p/logicpad-windows-app-development-journey/</link><pubDate>Sun, 13 Sep 2026 04:00:00 +0900</pubDate><guid>http://kenji.blog/es/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 Aprendiendo del desarrollo de LogicPad: El camino para lanzar una aplicación de Windows como proyecto personal" />&lt;h2 id="1-introducción-por-qué-atreverse-a-crear-una-aplicación-nativa-de-windows-ahora">1. Introducción: ¿Por qué atreverse a crear una aplicación nativa de Windows ahora?
&lt;/h2>&lt;p>En el desarrollo de aplicaciones moderno, no hay duda de que las tecnologías multiplataforma como Electron, Tauri y React Native se han convertido en la corriente principal. El enfoque de escribir una vez usando tecnologías web y ejecutar en cualquier lugar (Write Once, Run Anywhere) es muy racional desde la perspectiva de la velocidad de desarrollo y el mantenimiento. Sin embargo, me atreví a elegir el camino de desarrollar una aplicación llamada &amp;ldquo;LogicPad&amp;rdquo; como una aplicación nativa completamente optimizada para Windows.&lt;/p>
&lt;p>LogicPad es un simulador de circuitos lógicos digitales y editor de texto dirigido a ingenieros de hardware y estudiantes de circuitos lógicos. Es necesario simular decenas de miles de puertas lógicas en tiempo real y, al mismo tiempo, renderizar datos de formas de onda complejas sin demora. En dominios donde se requiere un rendimiento tan extremo, las pausas de unos pocos milisegundos debidas a la recolección de basura (micro-tartamudez) y la sobrecarga de renderizado de las vistas web provocan una degradación fatal de la experiencia del usuario.&lt;/p>
&lt;p>En este artículo, repasaré la trayectoria desde el concepto de desarrollo de este LogicPad, la implementación usando C++ y WinUI 3 (Windows App SDK), la superación de barreras técnicas específicas, el empaquetado MSIX y, finalmente, la distribución mundial a través de la Microsoft Store, junto con explicaciones técnicas muy detalladas. Al compartir el proceso de cómo un desarrollador individual crea una aplicación nativa de Windows de calidad empresarial, espero que sirva de guía para aquellos que también estén asumiendo el reto del desarrollo nativo.&lt;/p>
&lt;h2 id="2-cronología-del-proyecto">2. Cronología del proyecto
&lt;/h2>&lt;p>El desarrollo de LogicPad se llevó a cabo como un proyecto personal aprovechando los fines de semana y las noches. La cronología general abarcó aproximadamente medio año (6 meses). A continuación se muestra un diagrama de Gantt que ilustra el progreso del proyecto.&lt;/p>
&lt;pre class="mermaid">
gantt
title Cronología del desarrollo de LogicPad
dateFormat YYYY-MM-DD
section &amp;#34;Planificación y diseño&amp;#34;
&amp;#34;Análisis de requisitos y prototipado&amp;#34; :a1, 2025-10-01, 30d
&amp;#34;Diseño de arquitectura (C++ central)&amp;#34; :a2, after a1, 20d
section &amp;#34;Desarrollo central&amp;#34;
&amp;#34;Motor de simulación (C++)&amp;#34; :a3, after a2, 45d
&amp;#34;Integración de WinUI 3 XAML&amp;#34; :a4, after a3, 40d
section &amp;#34;Refinamiento de UI/UX&amp;#34;
&amp;#34;Barra de título personalizada y ventanas&amp;#34; :a5, after a4, 15d
&amp;#34;Despachador asíncrono y multihilo&amp;#34; :a6, after a5, 15d
section &amp;#34;Lanzamiento y tienda&amp;#34;
&amp;#34;Empaquetado y capacidades de MSIX&amp;#34; :a7, after a6, 10d
&amp;#34;Certificación de Microsoft Store&amp;#34; :a8, after a7, 14d
&lt;/pre>
&lt;p>Como se puede ver, la mayor parte del tiempo de desarrollo se invirtió en la optimización del motor central y la integración del procesamiento asíncrono entre WinUI 3 y C++. Aunque el desarrollo nativo tiene un mayor costo de configuración y aprendizaje inicial en comparación con el desarrollo multiplataforma, esa inversión definitivamente se recupera en forma de rendimiento final.&lt;/p>
&lt;h2 id="3-selección-de-tecnología-las-profundidades-de-c--winui-3--windows-app-sdk">3. Selección de tecnología: Las profundidades de C++ / WinUI 3 / Windows App SDK
&lt;/h2>&lt;p>Al desarrollar LogicPad, la selección de la pila tecnológica fue una de las decisiones más importantes. En cuanto a los marcos de interfaz de usuario (UI) nativos en la plataforma Windows, históricamente existen varias opciones como las API Win32 (User32/GDI), MFC, Windows Forms, WPF, UWP, etc. Actualmente, Microsoft recomienda &lt;strong>WinUI 3&lt;/strong>, que se incluye en el &lt;strong>Windows App SDK&lt;/strong>, para el desarrollo de aplicaciones de escritorio modernas de Windows.&lt;/p>
&lt;h3 id="31-arquitectura-de-windows-app-sdk-y-winui-3">3.1. Arquitectura de Windows App SDK y WinUI 3
&lt;/h3>&lt;p>Windows App SDK es un conjunto de bibliotecas para proporcionar las API más recientes de Windows de forma independiente a la versión del sistema operativo. Mientras que el UWP (Universal Windows Platform) tradicional estaba fuertemente vinculado a las actualizaciones del sistema operativo, Windows App SDK se distribuye con la aplicación, lo que garantiza un comportamiento consistente desde Windows 10 (versión 1809 y posteriores) hasta Windows 11.&lt;/p>
&lt;p>WinUI 3 es un marco de interfaz de usuario nativo que se ejecuta sobre este Windows App SDK y es totalmente compatible con el Fluent Design System. El interior de WinUI 3 está construido con C++ y DirectX, por lo que funciona de manera extremadamente rápida.&lt;/p>
&lt;h3 id="32-por-qué-atreverse-a-elegir-c-cwinrt-en-lugar-de-c">3.2. Por qué atreverse a elegir C++ (C++/WinRT) en lugar de C#
&lt;/h3>&lt;p>Tanto C# como C++ son compatibles como lenguajes de desarrollo para WinUI 3. El uso de C# y .NET mejoraría drásticamente la eficiencia del desarrollo, pero para LogicPad adoptamos &lt;strong>C++/WinRT&lt;/strong> por las siguientes razones:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Gestión de memoria determinista&lt;/strong>: Dado que no hay recolector de basura (GC), el momento de asignación y liberación de memoria se puede controlar por completo. Esto evita que ocurran pausas del GC durante los bucles de simulación.&lt;/li>
&lt;li>&lt;strong>SIMD y optimización de caché&lt;/strong>: En C++, el diseño físico de la memoria (como Estructura de Arreglos) se puede definir estrictamente, maximizando la tasa de aciertos de la caché de la CPU.&lt;/li>
&lt;li>&lt;strong>Límite de la ABI nativa&lt;/strong>: C++/WinRT es una proyección moderna de C++ de COM (Component Object Model). Permite llamadas directas a las API nativas del sistema operativo sin la sobrecarga de P/Invoke como en C#.&lt;/li>
&lt;/ol>
&lt;p>COM se encuentra en la base de C++/WinRT. Todos los objetos WinRT son esencialmente objetos COM que implementan la interfaz &lt;code>IUnknown&lt;/code>, y los punteros inteligentes de C++/WinRT, como &lt;code>winrt::com_ptr&lt;/code>, gestionan automáticamente el recuento de referencias (&lt;code>AddRef&lt;/code> / &lt;code>Release&lt;/code>).&lt;/p>
&lt;h2 id="4-las-mayores-barreras-y-avances-en-el-desarrollo-de-winui-3">4. Las mayores barreras y avances en el desarrollo de WinUI 3
&lt;/h2>&lt;p>El desarrollo con WinUI 3 utilizando C++/WinRT, si bien es poderoso, conlleva sus propias complejidades. Aquí explicaré en detalle dos grandes desafíos técnicos con los que luché particularmente durante el desarrollo de LogicPad, y sus soluciones.&lt;/p>
&lt;h3 id="41-el-terror-y-las-corrutinas-de-las-actualizaciones-de-ui-asíncronas-en-c">4.1. El terror y las corrutinas de las actualizaciones de UI asíncronas en C++
&lt;/h3>&lt;p>La regla de oro de las aplicaciones de interfaz de usuario modernas es &amp;ldquo;nunca bloquear el hilo de la UI&amp;rdquo;. En LogicPad, es necesario ejecutar cálculos masivos de simulación de circuitos en un hilo de fondo y reflejar los resultados en el hilo de la UI.&lt;/p>
&lt;p>En C#, esto se puede escribir de manera relativamente simple usando &lt;code>async/await&lt;/code> y &lt;code>DispatcherQueue&lt;/code>, pero en C++, se logra combinando las corrutinas de C++20 y &lt;code>winrt::apartment_context&lt;/code>. Es esencial comprender el modelo de apartamento de COM (STA: Single-Threaded Apartment y MTA: Multi-Threaded Apartment).&lt;/p>
&lt;p>El siguiente código es un patrón extraído de la base de código real de LogicPad que realiza cálculos en segundo plano y regresa sin problemas al hilo de la UI.&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">// Manejador de eventos del clic del botón
&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">// Captura el contexto del apartamento (STA) del hilo de la UI actual
&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">// Actualiza el estado de la UI (esto se ejecuta en el hilo de la 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;Ejecutando simulación...&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">// Transfiere el contexto al grupo de hilos (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">// Procesamiento de simulación muy pesado (ejecutado en un hilo de fondo)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// Durante este tiempo, el hilo de la UI se libera, evitando que la aplicación se congele
&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">// Da formato a los resultados de la simulación como una cadena (sigue ejecutándose en segundo plano)
&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">// Devuelve el contexto al hilo de la UI
&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">// A partir de aquí se ejecuta en el hilo de la UI, por lo que es seguro acceder a los controles XAML
&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;Completado&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">// Incluso si ocurre una excepción, regresa al hilo de la UI para mostrar el mensaje de error
&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;Error: &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>El comportamiento de este &lt;code>winrt::apartment_context&lt;/code> parece mágico, pero internamente funciona a través de un mecanismo avanzado de C++ que utiliza la interfaz &lt;code>IContextCallback&lt;/code> para recordar el contexto del hilo original y despacha (pone en cola) el procesamiento a ese contexto en el momento del &lt;code>co_await&lt;/code>. Esto permite escribir procesos asíncronos con la apariencia de código procedimental sin caer en el infierno de los callbacks.&lt;/p>
&lt;h3 id="42-implementación-completa-de-una-barra-de-título-personalizada">4.2. Implementación completa de una barra de título personalizada
&lt;/h3>&lt;p>En las aplicaciones de la era de Windows 11, una &amp;ldquo;barra de título personalizada&amp;rdquo; que coloca pestañas y cuadros de búsqueda en la barra de título de la ventana (área de subtítulo) es un requisito esencial para una UX moderna. Sin embargo, personalizar la barra de título en WinUI 3 es fácil si solo se cambia el color, pero si intentas cumplir con el requisito de &amp;ldquo;extender el área del cliente a la barra de título mientras mantienes el movimiento de arrastre de la ventana y los diseños de acoplamiento (cambio de tamaño automático cuando la ventana se ajusta al borde de la pantalla)&amp;rdquo;, el nivel de dificultad aumenta considerablemente.&lt;/p>
&lt;p>En LogicPad, construimos la barra de título con nuestros propios elementos XAML utilizando la API &lt;code>ExtendsContentIntoTitleBar&lt;/code>. El siguiente código es el procedimiento para personalizar la barra de título usando la clase &lt;code>AppWindow&lt;/code> de 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">// Obtiene el HWND (manejador de ventana) de la ventana actual
&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">// Convierte el HWND a WindowId y obtiene la instancia de 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">// Comprueba si es una versión del SO que admite la personalización
&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">// Extiende el área del cliente (contenido) a la barra de título
&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">// Hace transparente el fondo de los botones de subtítulo predeterminados (minimizar, maximizar, cerrar)
&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">// Establece el elemento de UI (AppTitleBar) definido en el lado XAML como área de arrastre
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// *Los detalles de esta parte requieren observar el UIElement del lado XAML en el Dispatcher del hilo de la 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">// y llamar a SetDragRectangles() para indicar al SO el área arrastrable.
&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>La mayor trampa en esta implementación es que cada vez que cambia el tamaño del elemento de la interfaz de usuario en el lado XAML (como cuando se cambia el tamaño de la ventana), se debe usar &lt;code>InputNonClientPointerSource&lt;/code> y &lt;code>SetDragRectangles&lt;/code> para recalcular el área de prueba de impacto y notificar al sistema operativo &amp;ldquo;esta es el área que se puede arrastrar&amp;rdquo;. Si se descuida esto, se producirán errores en los que la ventana no se mueve incluso cuando se arrastra la barra de título, o por el contrario, intentar hacer clic en un botón se juzgará como arrastrar la ventana.&lt;/p>
&lt;h2 id="5-el-abismo-del-empaquetado-msix-y-appxmanifest">5. El abismo del empaquetado MSIX y AppXManifest
&lt;/h2>&lt;p>Para distribuir LogicPad una vez finalizado el desarrollo, es necesario crear un instalador. En lugar del instalador tradicional MSI o EXE, adopté el moderno formato &lt;strong>MSIX&lt;/strong>. MSIX permite instalaciones y desinstalaciones completamente limpias (sin ensuciar el registro) y tiene capacidades de actualización automática, lo que lo hace muy seguro y cómodo para los usuarios.&lt;/p>
&lt;p>Sin embargo, al empaquetar una aplicación nativa escrita en C++ con MSIX, en lo que hay que prestar más atención es a la configuración del &lt;code>Package.appxmanifest&lt;/code> (archivo de manifiesto).&lt;/p>
&lt;p>LogicPad necesita leer y escribir archivos de proyecto de circuitos enormes guardados en el sistema de archivos local (como la carpeta Documentos del usuario). En el entorno estándar de la caja de arena (sandbox) de UWP, solo se puede acceder a la propia carpeta de datos aislada de la aplicación (AppContainer). Para obtener derechos de acceso total como una aplicación de escritorio nativa, debes declarar la capacidad &lt;code>runFullTrust&lt;/code> en el manifiesto.&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;!-- Restricciones de características generales --&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;!-- Característica restringida para ejecutarse como una aplicación de escritorio nativa --&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>Esta &lt;code>&amp;lt;rescap:Capability Name=&amp;quot;runFullTrust&amp;quot; /&amp;gt;&lt;/code> se llama &amp;ldquo;Capacidad restringida&amp;rdquo; (Restricted Capability), y cuando la envías a Microsoft Store, debes enviar una justificación a los revisores de por qué se necesita este permiso. Expliqué: &amp;ldquo;Debido a que esta aplicación es una herramienta profesional para leer, escribir y exportar archivos arbitrarios de proyectos de circuitos lógicos ubicados en el disco local del usuario&amp;rdquo;, y fui aprobado sin problemas.&lt;/p>
&lt;h2 id="6-el-camino-hacia-microsoft-store-y-el-proceso-de-revisión">6. El camino hacia Microsoft Store y el proceso de revisión
&lt;/h2>&lt;p>Una vez que se completa la aplicación y se compila el paquete MSIX, finalmente es el momento de enviarlo a la Microsoft Store. Para los desarrolladores individuales, la distribución a través de la tienda tiene innumerables beneficios: distribución automática de actualizaciones, garantía de confiabilidad y el uso del sistema de pago.&lt;/p>
&lt;p>El proceso de envío a Microsoft Store se realiza a través del Partner Center (Centro de socios). El siguiente diagrama de flujo muestra todo el panorama desde la compilación hasta la publicación.&lt;/p>
&lt;pre class="mermaid">
flowchart TD
A[&amp;#34;Desarrollador (Visual Studio)&amp;#34;] --&amp;gt;|Compilación y firma| B[&amp;#34;Generación de paquete MSIX&amp;#34;]
B --&amp;gt; C[&amp;#34;Kit de certificación de aplicaciones de Windows (WACK)&amp;#34;]
C --&amp;gt;|Aprobado| D[&amp;#34;Panel del Centro de socios&amp;#34;]
C --&amp;gt;|Rechazado| E[&amp;#34;Corregir problemas de código / manifiesto&amp;#34;]
E --&amp;gt; A
D --&amp;gt;|Cargar paquete| F[&amp;#34;Proceso de certificación de la tienda&amp;#34;]
F --&amp;gt; G[&amp;#34;Análisis automatizado de malware&amp;#34;]
G --&amp;gt; H[&amp;#34;Revisión manual de contenido&amp;#34;]
H --&amp;gt;|Aprobado| I[&amp;#34;Publicación en Microsoft Store&amp;#34;]
H --&amp;gt;|Rechazado| J[&amp;#34;Abordar comentarios y volver a enviar&amp;#34;]
J --&amp;gt; D
I --&amp;gt; K[&amp;#34;¡La aplicación está en vivo!&amp;#34;]
&lt;/pre>
&lt;h3 id="61-la-barrera-del-wack-kit-de-certificación-de-aplicaciones-de-windows">6.1. La barrera del WACK (Kit de certificación de aplicaciones de Windows)
&lt;/h3>&lt;p>Antes de cargarlo en el Partner Center, siempre debes ejecutar localmente el &lt;strong>WACK (Windows App Certification Kit)&lt;/strong> y pasar la prueba previa. WACK es una herramienta que prueba automáticamente si la aplicación no falla, no llama a API no válidas y cumple con los requisitos de rendimiento.&lt;/p>
&lt;p>Para las aplicaciones nativas de C++, un error en el que hay que tener especial cuidado es el &amp;ldquo;Uso de API no compatibles&amp;rdquo;. Si estás vinculando estáticamente una biblioteca de C++ antigua de terceros, podría estar usando API de Win32 obsoletas internamente, y la certificación WACK podría rechazarla. Para evitar este problema, actualicé las bibliotecas dependientes a las últimas versiones y reescribí algunas funciones a las API alternativas proporcionadas por el Windows App SDK.&lt;/p>
&lt;h3 id="62-revisión-y-publicación">6.2. Revisión y publicación
&lt;/h3>&lt;p>La configuración en el Partner Center incluye establecer el precio de la aplicación, la clasificación por edades (clasificación IARC) e ingresar capturas de pantalla y descripciones para la tienda. Dado que LogicPad es una herramienta técnica, pude obtener una calificación para todas las edades de inmediato.&lt;/p>
&lt;p>Tomó aproximadamente 3 días hábiles desde que envié el paquete hasta que se completó la revisión. Después del análisis automatizado de malware y las verificaciones de funcionalidad, el equipo de revisión de Microsoft realiza verificaciones manuales de funcionamiento. La solicitud de permiso &lt;code>runFullTrust&lt;/code> también pasó sin problemas, y la sensación de logro en el momento en que el estado cambió finalmente a &amp;ldquo;Publicado (In the Store)&amp;rdquo; fue irremplazable.&lt;/p>
&lt;h2 id="7-el-desarrollo-personal-como-negocio-un-modelo-matemático-de-rendimiento-e-ingresos">7. El desarrollo personal como negocio: Un modelo matemático de rendimiento e ingresos
&lt;/h2>&lt;p>Para no simplemente estar satisfecho con la creación de una aplicación, sino actualizar continuamente LogicPad y hacerlo viable como negocio, es necesario evaluar cuantitativamente tanto las métricas técnicas como las comerciales.&lt;/p>
&lt;h3 id="71-el-modelo-de-optimización-del-uso-de-memoria-que-aporta-c">7.1. El modelo de optimización del uso de memoria que aporta C++
&lt;/h3>&lt;p>La mayor fortaleza de LogicPad es que es extremadamente ligero en comparación con los editores basados en Electron (por ejemplo, VSCode). La huella de memoria $M_{total}$ de la aplicación se puede modelar de la siguiente manera.&lt;/p>
$$
M_{total} = M_{UI} + M_{engine} + M_{cache}
$$&lt;p>Aquí, el $M_{UI}$ debido al renderizado nativo de WinUI 3 es drásticamente menor en comparación con Electron que carga un motor de navegador (alrededor de 50 MB).
Además, la memoria de la sección del motor C++, $M_{engine}$, escala linealmente con respecto al número de puertas lógicas $N$, mediante el uso de estructuras optimizadas y la eliminación de punteros.&lt;/p>
$$
M_{engine} = N \times \text{sizeof(LogicNode)}
$$&lt;p>Al optimizar la alineación de la estructura utilizando &lt;code>#pragma pack&lt;/code> de C++, redujimos la memoria por nodo al límite absoluto.&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">// Minimizar la memoria empacando, sin tener una tabla de funciones virtuales (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 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">// El tamaño de la estructura es 7 bytes (sin relleno de alineación)
&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 memoria de la capa de caché, $M_{cache}$, aumenta proporcionalmente a $\mathcal{O}(N \log N)$ para mantener el historial de simulación, pero debido a que la huella base es pequeña, el uso de RAM de todo el sistema se mantiene por debajo de 200 MB incluso para circuitos con decenas de miles de nodos.&lt;/p>
&lt;h3 id="72-ltv-y-cac-cálculos-de-marketing">7.2. LTV y CAC: Cálculos de marketing
&lt;/h3>&lt;p>En la estrategia de monetización para el desarrollo personal, el equilibrio entre el valor de vida del cliente (LTV: Lifetime Value) y el costo de adquisición de clientes (CAC: Customer Acquisition Cost) lo es todo. LogicPad adopta un modelo de licencia de compra única (freemium) en lugar de una suscripción.&lt;/p>
&lt;p>El LTV se calcula como la suma del valor presente de la probabilidad de compras de versiones actualizadas en el futuro, descontada por la tasa de descuento $d$. Cuando el período es $T$, se expresa mediante la siguiente fórmula.&lt;/p>
$$
LTV = \sum_{t=1}^{T} \frac{ARPU_t \times Margin}{(1+d)^t}
$$&lt;p>Por otro lado, el CAC es el gasto total en marketing de los anuncios en Twitter (actualmente X) y el flujo de los artículos del blog, dividido por el número de nuevos usuarios.&lt;/p>
$$
CAC = \frac{Total\ Marketing\ Spend}{Number\ of\ New\ Users}
$$&lt;p>La fortaleza del desarrollo personal es que puedes considerar los costos laborales de desarrollo como &amp;ldquo;costos hundidos&amp;rdquo; como tiempo de pasatiempo, de modo que el CAC se puede calcular usando puramente costos de marketing. Actualmente, a través del crecimiento orgánico centrado en el boca a boca en comunidades tecnológicas de nicho, estamos logrando un modelo económico unitario saludable de $LTV > CAC$ en un estado cercano a $CAC \approx 0$.&lt;/p>
&lt;h2 id="8-conclusión-el-mundo-complicado-pero-hermoso-del-desarrollo-de-aplicaciones-nativas">8. Conclusión: El mundo complicado pero hermoso del desarrollo de aplicaciones nativas
&lt;/h2>&lt;p>Mirando hacia atrás en el viaje desde el desarrollo de LogicPad hasta su lanzamiento en Microsoft Store, de ninguna manera fue un camino fácil, lidiando con incomprensibles errores de compilación de C++/WinRT, rastreando fugas de memoria debido a errores de recuento de referencias COM, e investigando las especificaciones del XML del manifiesto MSIX.&lt;/p>
&lt;p>Precisamente porque estamos en una era en la que &amp;ldquo;puedes construir cualquier cosa en un navegador&amp;rdquo; debido a la evolución de las tecnologías web, la experiencia del desarrollo nativo, donde llamas directamente a las API del sistema operativo y prestas atención a cada byte de memoria y cada reloj de la CPU, eleva abrumadoramente tu fuerza fundamental como ingeniero.&lt;/p>
&lt;p>WinUI 3 y Windows App SDK aún se están desarrollando activamente, y son las mejores herramientas para crear aplicaciones hermosas que aprovechen al máximo el paradigma de UI de Windows 11. Espero sinceramente que este artículo de blog ayude a los desarrolladores que están a punto de asumir el reto de desarrollar aplicaciones nativas de Windows, y que se alineen aplicaciones maravillosas en la tienda.&lt;/p>
&lt;p>El desarrollo aún no ha terminado. Para la próxima versión de LogicPad, planeamos integrar nuestro propio motor de renderizado de formas de onda utilizando Direct2D. En el próximo artículo, planeamos profundizar en la interoperabilidad entre DirectX y WinUI 3 (utilizando SwapChainPanel). Por favor, espérenlo.&lt;/p></description></item></channel></rss>