Tutorial: Crear en C++ un componente básico de Windows en tiempo de ejecución y llamarlo desde JavaScript o C#
Este tutorial muestra cómo crear un componente básico DLL de Windows en tiempo de ejecución que se pueda llamar desde JavaScript, C# o Visual Basic. Antes de iniciar este tutorial, asegúrate de familiarizarte con conceptos tales como la interfaz binaria abstracta (ABI), las clases ref y Extensiones de componentes de Visual C++ que facilitan el trabajo con las clases ref. Para obtener más información, consulta Crear componentes de Windows en tiempo de ejecución en C++ y Referencia del lenguaje Visual C++ (C++/CX).
Crear la DLL del componente de C++
En este ejemplo, se crea en primer lugar el proyecto de componente, pero se podría crear el proyecto de JavaScript primero. El orden no importa.
Ten en cuenta que la clase principal del componente contiene ejemplos de definiciones de propiedades y métodos, así como una declaración de evento. Estos se proporcionan solo para mostrar cómo se hizo. No son necesarios y, en este ejemplo, vamos a reemplazar todo el código generado con nuestro propio código.
Para crear el proyecto de componente de C++
En la barra de menús de Visual Studio, elige Archivo, Nuevo, Proyecto.
En el cuadro de diálogo Nuevo proyecto, en el panel de la izquierda, expande Visual C++ y selecciona el nodo correspondiente a aplicaciones de la Tienda Windows.
En el panel central, selecciona Componente de Windows en tiempo de ejecución y, a continuación, asigna al proyecto el nombre WinRT_CPP.
Elige el botón Aceptar.
Para agregar una clase activable al componente
- Una clase activable es aquella que el código de cliente puede crear utilizando una expresión new (New en Visual Basic o ref new en C++). En el componente, se declara como public ref class sealed. De hecho, los archivos Class1.h y .cpp ya tienen una clase ref. Puedes cambiar el nombre, pero en este ejemplo usaremos el nombre predeterminado: Class1. Puedes definir clases ref adicionales o clases regulares en el componente si es necesario. Para obtener más información sobre las clases ref, consulta Sistema de tipos (C++/CX).
Para agregar las directivas #include necesarias
Agrega estas directivas #include a Class1.h:
#include <collection.h> #include <amp.h> #include <amp_math.h>
collection.h es el archivo de encabezado para las clases concretas de C++ como Platform::Collections::Vector (Clase) y Platform::Collections::Map (Clase), que implementan las interfaces independientes del lenguaje definidas por Windows en tiempo de ejecución. Los encabezados amp se utilizan para ejecutar cálculos en la GPU. No tienen un equivalente en Windows en tiempo de ejecución, lo cual no es un problema dado su carácter privado. Normalmente, por motivos de rendimiento se debe utilizar código C++ conforme a las normas ISO y bibliotecas estándar internamente dentro del componente; únicamente es necesario expresar la interfaz de Windows en tiempo de ejecución mediante tipos de Windows en tiempo de ejecución.
Para agregar un delegado en el ámbito de espacio de nombres
Un delegado es una construcción que define los parámetros y el tipo de valor devuelto para los métodos. Un evento es una instancia de un tipo determinado de delegado, y cualquier método de controlador de eventos que se suscriba al evento debe tener la signatura especificada en el delegado. El código siguiente define un tipo de delegado que toma un valor int y devuelve void. A continuación, el código declara un event público de este tipo; esto permite al código de cliente proporcionar métodos que se invocan cuando se desencadena el evento.
Agrega la siguiente declaración de delegado en el ámbito de espacio de nombres en Class1.h, justo antes de la declaración de Class1.
public delegate void PrimeFoundHandler(int result);
Sugerencia Si el código no se alinea correctamente al pegarlo en Visual Studio, simplemente presiona Ctrl+K+D para corregir la sangría de todo el archivo.
Para agregar los miembros públicos
La clase expone tres métodos públicos y un evento público. El primer método es sincrónico porque siempre se ejecuta muy rápidamente. Dado que los otros dos métodos pueden tardar algún tiempo, son asincrónicos para que no bloqueen el subproceso de la interfaz de usuario. Estos métodos devuelven IAsyncOperationWithProgress e IAsyncActionWithProgress. El primero define un método async que devuelve un resultado, y el segundo define un método async que devuelve void. Estas interfaces también permiten al código de cliente recibir las notificaciones sobre el progreso de la operación.
public: // Synchronous method. Windows::Foundation::Collections::IVector<double>^ ComputeResult(double input); // Asynchronous methods Windows::Foundation::IAsyncOperationWithProgress<Windows::Foundation::Collections::IVector<int>^, double>^ GetPrimesOrdered(int first, int last); Windows::Foundation::IAsyncActionWithProgress<double>^ GetPrimesUnordered(int first, int last); // Event whose type is a delegate "class" event PrimeFoundHandler^ primeFoundEvent;
Para agregar los miembros privados
La clase contiene tres miembros privados: dos métodos auxiliares para los cálculos numéricos y un objeto CoreDispatcher que se utiliza para calcular las referencias a las invocaciones de eventos desde los subprocesos de trabajo al subproceso de la interfaz de usuario.
private: bool is_prime(int n); Windows::UI::Core::CoreDispatcher^ m_dispatcher;
Para agregar el encabezado y las directivas de espacio de nombres
En Class1.cpp, agrega estas directivas #include:
#include <ppltasks.h> #include <concurrent_vector.h>
A continuación, agrega estas instrucciones using para extraer los espacios de nombres necesarios:
using namespace concurrency; using namespace Platform::Collections; using namespace Windows::Foundation::Collections; using namespace Windows::Foundation; using namespace Windows::UI::Core;
Para agregar la implementación para ComputeResult
En Class1.cpp, agrega la implementación de método siguiente. Este método se ejecuta sincrónicamente en el subproceso de llamada, pero es muy rápido porque utiliza C++ AMP para paralelizar el cálculo en la GPU. Para obtener más información, consulta Información general sobre C++ AMP. Los resultados se anexan a un tipo concreto Platform::Collections::Vector<T> que se convierte implícitamente en una interfaz Windows::Foundation::Collections::IVector<T> cuando se devuelve.
//Public API IVector<double>^ Class1::ComputeResult(double input) { // Implement your function in ISO C++ or // call into your C++ lib or DLL here. This example uses AMP. float numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 10000.0 }; array_view<float, 1> logs(6, numbers); // See https://msdn.microsoft.com/en-us/library/hh305254.aspx parallel_for_each( logs.extent, [=] (index<1> idx) restrict(amp) { logs[idx] = concurrency::fast_math::log10(logs[idx]); } ); // Return a Windows Runtime-compatible type across the ABI auto res = ref new Vector<double>(); int len = safe_cast<int>(logs.extent.size()); for(int i = 0; i < len; i++) { res->Append(logs[i]); } // res is implicitly cast to IVector<double> return res; }
Para agregar la implementación para GetPrimesOrdered y el método auxiliar
En Class1.cpp, agrega las implementaciones para GetPrimesOrdered y el método auxiliar is_prime. GetPrimesOrdered usa Clase concurrent_vector y un bucle parallel_for (Función) para repartir el trabajo y utilizar los máximos recursos del equipo en el que se ejecuta el programa para generar los resultados. Una vez calculados, almacenados y ordenados, los resultados se agregan a un objeto Platform::Collections::Vector<T> y se devuelven en la interfaz Windows::Foundation::Collections::IVector<T> al código de cliente.
Observa el código del indicador de progreso, que permite al cliente enlazar una barra de progreso u otro tipo de interfaz de usuario para mostrar al usuario cuánto tiempo queda para finalizar la operación. La notificación sobre el progreso tiene un costo. Debe desencadenarse un evento en el lado del componente y controlarse en el subproceso de la interfaz de usuario, y el valor de progreso se debe almacenar en cada iteración. Una manera de minimizar el costo consiste en limitar la frecuencia con la que se desencadena un evento de progreso. Si el costo sigue siendo prohibitivo, o si no puedes hacer un cálculo aproximado de la duración de la operación, considera la posibilidad de utilizar un anillo de progreso, que muestra que una operación está en curso pero no muestra el tiempo restante hasta la finalización.
// Determines whether the input value is prime. bool Class1::is_prime(int n) { if (n < 2) return false; for (int i = 2; i < n; ++i) { if ((n % i) == 0) return false; } return true; } // This method computes all primes, orders them, then returns the ordered results. IAsyncOperationWithProgress<IVector<int>^, double>^ Class1::GetPrimesOrdered(int first, int last) { return create_async([this, first, last] (progress_reporter<double> reporter) -> IVector<int>^ { // Ensure that the input values are in range. if (first < 0 || last < 0) { throw ref new InvalidArgumentException(); } // Perform the computation in parallel. concurrent_vector<int> primes; long operation = 0; long range = last - first + 1; double lastPercent = 0.0; parallel_for(first, last + 1, [this, &primes, &operation, range, &lastPercent, reporter](int n) { // Increment and store the number of times the parallel // loop has been called on all threads combined. There // is a performance cost to maintaining a count, and // passing the delegate back to the UI thread, but it's // necessary if we want to display a determinate progress // bar that goes from 0 to 100%. We can avoid the cost by // setting the ProgressBar IsDeterminate property to false // or by using a ProgressRing. if(InterlockedIncrement(&operation) % 100 == 0) { reporter.report(100.0 * operation / range); } // If the value is prime, add it to the local vector. if (is_prime(n)) { primes.push_back(n); } }); // Sort the results. std::sort(begin(primes), end(primes), std::less<int>()); reporter.report(100.0); // Copy the results to a Vector object, which is // implicitly converted to the IVector return type. IVector // makes collections of data available to other // Windows Runtime components. return ref new Vector<int>(primes.begin(), primes.end()); }); }
Para agregar la implementación para GetPrimesUnordered
El último paso para crear el componente de C++ consiste en agregar la implementación para el método GetPrimesUnordered en Class1.cpp. Este método devuelve cada resultado a medida que lo encuentra, sin esperar a que se encuentren todos los resultados. Cada resultado se devuelve en el controlador de eventos y se muestra en la interfaz de usuario en tiempo real. Una vez más, observa que se utiliza un indicador de progreso. Este método también utiliza el método auxiliar is_prime.
// This method returns no value. Instead, it fires an event each time a // prime is found, and passes the prime through the event. // It also passes progress info. IAsyncActionWithProgress<double>^ Class1::GetPrimesUnordered(int first, int last) { auto window = Windows::UI::Core::CoreWindow::GetForCurrentThread(); m_dispatcher = window->Dispatcher; return create_async([this, first, last](progress_reporter<double> reporter) { // Ensure that the input values are in range. if (first < 0 || last < 0) { throw ref new InvalidArgumentException(); } // In this particular example, we don't actually use this to store // results since we pass results one at a time directly back to // UI as they are found. However, we have to provide this variable // as a parameter to parallel_for. concurrent_vector<int> primes; long operation = 0; long range = last - first + 1; double lastPercent = 0.0; // Perform the computation in parallel. parallel_for(first, last + 1, [this, &primes, &operation, range, &lastPercent, reporter](int n) { // Store the number of times the parallel loop has been called // on all threads combined. See comment in previous method. if(InterlockedIncrement(&operation) % 100 == 0) { reporter.report(100.0 * operation / range); } // If the value is prime, pass it immediately to the UI thread. if (is_prime(n)) { // Since this code is probably running on a worker // thread, and we are passing the data back to the // UI thread, we have to use a CoreDispatcher object. m_dispatcher->RunAsync( CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, n, operation, range]() { this->primeFoundEvent(n); }, Platform::CallbackContext::Any)); } }); reporter.report(100.0); }); }
Presiona Ctrl+Mayús+B para compilar el componente.
Crear una aplicación cliente de JavaScript
Para crear un proyecto de JavaScript
-
Nota Si solo deseas crear un cliente de C#, puedes omitir esta sección.
En el Explorador de soluciones, abre el menú contextual del nodo Solución y elige Agregar, Nuevo proyecto.
Expande JavaScript (puede estar anidado en Otros lenguajes) y elige Aplicación vacía.
Elige el botón Aceptar para aceptar el nombre predeterminado: App1.
Abre el menú contextual del nodo del proyecto App1 y elige Establecer como proyecto de inicio.
Agrega una referencia de proyecto a WinRT_CPP:
Abre el menú contextual del nodo Referencias y elige Agregar referencia.
En el panel izquierdo del cuadro de diálogo Administrador de referencias, selecciona Solución y, después, Proyectos.
En el panel central, selecciona WinRT_CPP y elige el botón Aceptar.
Para agregar el código HTML que invoca los controladores de eventos de JavaScript
Pega este código HTML en el nodo <body> de la página default.html:
<div id="LogButtonDiv"> <button id="logButton" onclick="LogButton_Click()">Logarithms using AMP</button> </div> <div id="LogResultDiv"> <p id="logResult"></p> </div> <div id="OrderedPrimeButtonDiv"> <button id="orderedPrimeButton" onclick="ButtonOrdered_Click()">Primes using parallel_for with sort</button> </div> <div id="OrderedPrimeProgress"> <progress id="OrderedPrimesProgressBar" style="-ms-grid-column-span:2" value="0" max="100"></progress> </div> <div id="OrderedPrimeResultDiv"> <p id="orderedPrimes"> Primes found (ordered): </p> </div> <div id="UnorderedPrimeButtonDiv"> <button id="ButtonUnordered" onclick="ButtonUnordered_Click()">Primes returned as they are produced.</button> </div> <div id="UnorderedPrimeDiv"> <progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress> </div> <div id="UnorderedPrime"> <p id="unorderedPrimes"> Primes found (unordered): </p> </div> <div id="ClearDiv"> <button id="Button_Clear" onclick="ButtonClear_Click()">Clear</button> </div>
<div id="LogButtonDiv"> <button id="logButton" onclick="LogButton_Click()">Logarithms using AMP</button> </div> <div id="LogResultDiv"> <p id="logResult"></p> </div> <div id="OrderedPrimeButtonDiv"> <button id="orderedPrimeButton" onclick="ButtonOrdered_Click()">Primes using parallel_for with sort</button> </div> <div id="OrderedPrimeProgress"> <progress id="OrderedPrimesProgressBar" value="0" max="100"></progress> </div> <div id="OrderedPrimeResultDiv"> <p id="orderedPrimes"> Primes found (ordered): </p> </div> <div id="UnorderedPrimeButtonDiv"> <button id="ButtonUnordered" onclick="ButtonUnordered_Click()">Primes returned as they are produced.</button> </div> <div id="UnorderedPrimeDiv"> <progress id="UnorderedPrimesProgressBar" value="0" max="100"></progress> </div> <div id="UnorderedPrime"> <p id="unorderedPrimes"> Primes found (unordered): </p> </div> <div id="ClearDiv"> <button id="Button_Clear" onclick="ButtonClear_Click()">Clear</button> </div>
Para agregar estilos
En default.css, quita el estilo body y agrega estos estilos:
#LogButtonDiv { border: orange solid 1px; -ms-grid-row: 1; /* default is 1 */ -ms-grid-column: 1; /* default is 1 */ } #LogResultDiv { background: black; border: red solid 1px; -ms-grid-row: 1; -ms-grid-column: 2; } #UnorderedPrimeButtonDiv, #OrderedPrimeButtonDiv { border: orange solid 1px; -ms-grid-row: 2; -ms-grid-column:1; } #UnorderedPrimeProgress, #OrderedPrimeProgress { border: red solid 1px; -ms-grid-column-span: 2; height: 40px; } #UnorderedPrimeResult, #OrderedPrimeResult { border: red solid 1px; font-size:smaller; -ms-grid-row: 2; -ms-grid-column: 3; -ms-overflow-style:scrollbar; }
Para agregar controladores de eventos de JavaScript que llaman a la DLL del componente
Agrega las siguientes funciones al final del archivo default.js: Se llama a estas funciones cuando se eligen los botones de la página principal. Observa cómo JavaScript genera clases de C++ y después llama a sus métodos y usa los valores devueltos para rellenar las etiquetas HTML.
var nativeObject = new WinRT_CPP.Class1(); function LogButton_Click() { var val = nativeObject.computeResult(0); var result = ""; for (i = 0; i < val.length; i++) { result += val[i] + "<br/>"; } document.getElementById('logResult').innerHTML = result; } function ButtonOrdered_Click() { document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): "; nativeObject.getPrimesOrdered(2, 10000).then( function (v) { for (var i = 0; i < v.length; i++) document.getElementById('orderedPrimes').innerHTML += v[i] + " "; }, function (error) { document.getElementById('orderedPrimes').innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById("OrderedPrimesProgressBar"); progressBar.value = p; }); } function ButtonUnordered_Click() { document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): "; nativeObject.onprimefoundevent = handler_unordered; nativeObject.getPrimesUnordered(2, 10000).then( function () { }, function (error) { document.getElementById("unorderedPrimes").innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById("UnorderedPrimesProgressBar"); progressBar.value = p; }); } var handler_unordered = function (n) { document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " "; }; function ButtonClear_Click() { document.getElementById('logResult').innerHTML = ""; document.getElementById("unorderedPrimes").innerHTML = ""; document.getElementById('orderedPrimes').innerHTML = ""; document.getElementById("UnorderedPrimesProgressBar").value = 0; document.getElementById("OrderedPrimesProgressBar").value = 0; }
var nativeObject = new WinRT_CPP.Class1(); function LogButton_Click() { var val = nativeObject.computeResult(0); var result = ""; for (i = 0; i < val.length; i++) { result += val[i] + "<br/>"; } document.getElementById('logResult').innerHTML = result; } function ButtonOrdered_Click() { document.getElementById('orderedPrimes').innerHTML = "Primes found (ordered): "; nativeObject.getPrimesOrdered(2, 10000).then( function (v) { for (var i = 0; i < v.length; i++) document.getElementById('orderedPrimes').innerHTML += v[i] + " "; }, function (error) { document.getElementById('orderedPrimes').innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById("OrderedPrimesProgressBar"); progressBar.value = p; }); } function ButtonUnordered_Click() { document.getElementById('unorderedPrimes').innerHTML = "Primes found (unordered): "; nativeObject.onprimefoundevent = handler_unordered; nativeObject.getPrimesUnordered(2, 10000).then( function () { }, function (error) { document.getElementById("unorderedPrimes").innerHTML += " " + error.description; }, function (p) { var progressBar = document.getElementById("UnorderedPrimesProgressBar"); progressBar.value = p; }); } var handler_unordered = function (n) { document.getElementById('unorderedPrimes').innerHTML += n.target.toString() + " "; }; function ButtonClear_Click() { document.getElementById('logResult').innerHTML = ""; document.getElementById("unorderedPrimes").innerHTML = ""; document.getElementById('orderedPrimes').innerHTML = ""; document.getElementById("UnorderedPrimesProgressBar").value = 0; document.getElementById("OrderedPrimesProgressBar").value = 0; }
Presiona F5 para ejecutar la aplicación.
Crear una aplicación cliente de C#
Resulta igual de sencillo llamar a la DLL del componente de Windows en tiempo de ejecución de C++ desde un cliente de C# que hacerlo desde un cliente de JavaScript. Los pasos siguientes muestran cómo crear un cliente de C# que es aproximadamente equivalente al cliente de JavaScript de la sección anterior.
Para crear un proyecto de C#
En el Explorador de soluciones, abre el menú contextual del nodo Solución y, a continuación, elige Agregar, Nuevo proyecto.
Expande Visual C# (puede estar anidado en Otros lenguajes), selecciona Tienda Windows en el panel izquierdo y, a continuación, Aplicación vacía en el panel central.
Asigna a esta aplicación el nombre CS_Client y elige el botón Aceptar.
Abre el menú contextual del nodo del proyecto CS_Client y elige Establecer como proyecto de inicio.
Agrega una referencia de proyecto a WinRT_CPP:
Abre el menú contextual del nodo Referencias y elige Agregar referencia.
En el panel izquierdo del cuadro de diálogo Administrador de referencias, selecciona Solución y, después, Proyectos.
En el panel central, selecciona WinRT_CPP y elige el botón Aceptar.
Para agregar el código XAML que define la interfaz de usuario
Agrega el control ScrollViewer siguiente y su contenido a la cuadrícula en mainpage.xaml:
<ScrollViewer> <StackPanel Width="1400"> <Button x:Name="Button1" Width="340" Height="50" Margin="0,20,20,20" Content="Synchronous Logarithm Calculation" FontSize="16" Click="Button1_Click_1"/> <TextBlock x:Name="Result1" Height="100" FontSize="14"></TextBlock> <Button x:Name="PrimesOrderedButton" Content="Prime Numbers Ordered" FontSize="16" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesOrderedButton_Click_1"></Button> <ProgressBar x:Name="PrimesOrderedProgress" IsIndeterminate="false" Height="40"></ProgressBar> <TextBlock x:Name="PrimesOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock> <Button x:Name="PrimesUnOrderedButton" Width="340" Height="50" Margin="0,20,20,20" Click="PrimesUnOrderedButton_Click_1" Content="Prime Numbers Unordered" FontSize="16"></Button> <ProgressBar x:Name="PrimesUnOrderedProgress" IsIndeterminate="false" Height="40" ></ProgressBar> <TextBlock x:Name="PrimesUnOrderedResult" MinHeight="100" FontSize="10" TextWrapping="Wrap"></TextBlock> <Button x:Name="Clear_Button" Content="Clear" HorizontalAlignment="Left" Margin="0,20,20,20" VerticalAlignment="Top" Width="341" Click="Clear_Button_Click" FontSize="16"/> </StackPanel> </ScrollViewer>
Para agregar controladores de eventos para los botones
En el Explorador de soluciones, abre mainpage.xaml.cs. (El archivo puede estar anidado en mainpage.xaml). Agrega una directiva using para System.Text y, a continuación, agrega el controlador de eventos para el cálculo del logaritmo en la clase MainPage justo después de OnNavigateTo.
private void Button1_Click_1(object sender, RoutedEventArgs e) { // Create the object var nativeObject = new WinRT_CPP.Class1(); // Call the synchronous method. val is an IList that // contains the results. var val = nativeObject.ComputeResult(0); StringBuilder result = new StringBuilder(); foreach (var v in val) { result.Append(v).Append(System.Environment.NewLine); } this.Result1.Text = result.ToString(); }
Agrega el controlador de eventos para el resultado ordenado:
async private void PrimesOrderedButton_Click_1(object sender, RoutedEventArgs e) { var nativeObject = new WinRT_CPP.Class1(); StringBuilder sb = new StringBuilder(); sb.Append("Primes found (ordered): "); PrimesOrderedResult.Text = sb.ToString(); // Call the asynchronous method var asyncOp = nativeObject.GetPrimesOrdered(2, 100000); // Before awaiting, provide a lambda or named method // to handle the Progress event that is fired at regular // intervals by the asyncOp object. This handler updates // the progress bar in the UI. asyncOp.Progress = (asyncInfo, progress) => { PrimesOrderedProgress.Value = progress; }; // Wait for the operation to complete var asyncResult = await asyncOp; // Convert the results to strings foreach (var result in asyncResult) { sb.Append(result).Append(" "); } // Display the results PrimesOrderedResult.Text = sb.ToString(); }
Agrega el controlador de eventos para el resultado sin ordenar y para el botón que borra los resultados, lo que te permitirá volver a ejecutar el código.
private void PrimesUnOrderedButton_Click_1(object sender, RoutedEventArgs e) { var nativeObject = new WinRT_CPP.Class1(); StringBuilder sb = new StringBuilder(); sb.Append("Primes found (unordered): "); PrimesUnOrderedResult.Text = sb.ToString(); // primeFoundEvent is a user-defined event in nativeObject // It passes the results back to this thread as they are produced // and the event handler that we define here immediately displays them. nativeObject.primeFoundEvent += (n) => { sb.Append(n.ToString()).Append(" "); PrimesUnOrderedResult.Text = sb.ToString(); }; // Call the async method. var asyncResult = nativeObject.GetPrimesUnordered(2, 100000); // Provide a handler for the Progress event that the asyncResult // object fires at regular intervals. This handler updates the progress bar. asyncResult.Progress += (asyncInfo, progress) => { PrimesUnOrderedProgress.Value = progress; }; } private void Clear_Button_Click(object sender, RoutedEventArgs e) { PrimesOrderedProgress.Value = 0; PrimesUnOrderedProgress.Value = 0; PrimesUnOrderedResult.Text = ""; PrimesOrderedResult.Text = ""; Result1.Text = ""; }
Ejecutar la aplicación
Selecciona el proyecto de C# o el proyecto de JavaScript como proyecto de inicio; para ello, abre el menú contextual del nodo del proyecto en el Explorador de soluciones y elige Establecer como proyecto de inicio. Presiona F5 para ejecutarlo con depuración o Ctrl+F5 para ejecutarlo sin depuración.
Inspeccionar el componente en el Examinador de objetos (opcional)
En el Examinador de objetos, puedes inspeccionar todos los tipos de Windows en tiempo de ejecución definidos en los archivos .winmd. Esto incluye los tipos en el espacio de nombres Platform y en el espacio de nombres predeterminado. Sin embargo, debido a que los tipos del espacio de nombres Platform::Collections están definidos en el archivo de encabezado collections.h, no en un archivo winmd, no aparecen en el Examinador de objetos.
Para inspeccionar el componente
En la barra de menús, elige Ver, Otras ventanas, Examinador de objetos.
En el panel izquierdo del Examinador de objetos, expande el nodo WinRT_CPP para mostrar los tipos y métodos definidos en el componente.
Sugerencias de depuración
Para mejorar la experiencia al usar la depuración, descarga los símbolos de depuración de los servidores de símbolos públicos de Microsoft:
En la barra de menús, elige Herramientas, Opciones.
En el cuadro de diálogo Opciones, expande el nodo Depuración y selecciona Símbolos.
Selecciona Servidores de símbolos de Microsoft y, a continuación, elige el botón Aceptar.
Es posible que la primera vez se tarde algo de tiempo en descargar los símbolos. Para acelerar el rendimiento la próxima vez que presiones F5, especifica un directorio local donde almacenar los símbolos en memoria caché.
Al depurar una solución de JavaScript que tenga un objeto DLL de componente, puedes establecer el depurador para habilitar el recorrido paso a paso a través del script o el recorrido paso a paso a través del código nativo del componente, pero no ambos al mismo tiempo. Para cambiar el valor, abre el menú contextual del nodo del proyecto de JavaScript en el Explorador de soluciones y elige Propiedades, Depuración, Tipo de depurador.
Asegúrese de seleccionar las capacidades adecuadas en el diseñador de paquetes. Por ejemplo, si estás intentando tener acceso mediante programación a los archivos de la carpeta Imágenes, asegúrate de activar la casilla Biblioteca de imágenes en el panel Capacidades del diseñador de paquetes.
Si el código de JavaScript no reconoce los métodos o propiedades públicos del componente, asegúrate de que en JavaScript se esté usando el uso combinado de mayúsculas y minúsculas tipo Camel. Por ejemplo, en JavaScript debe hacerse referencia al método ComputeResult de C++ como computeResult.
Si quitas de una solución un proyecto de componente de Windows en tiempo de ejecución de C++, también debes quitar manualmente la referencia al proyecto desde el proyecto de JavaScript. De lo contrario, se impiden las operaciones de depuración o compilación subsiguientes. Si es necesario, puedes agregar una referencia de ensamblado al archivo DLL.
Vea también
Referencia
Roadmap for Windows Store apps using C++