Tutorial para crear un componente de Windows Runtime en C++/CX y llamarlo desde JavaScript o C#
Nota:
Este tema existe para ayudarte a mantener la aplicación C++/CX. Pero te recomendamos que uses C++/WinRT para las nuevas aplicaciones. C++/WinRT es una moderna proyección de lenguaje C++17 totalmente estándar para las API de Windows Runtime (WinRT), implementada como una biblioteca basada en archivos de encabezado y diseñada para darte acceso de primera clase a la API moderna de Windows. Para obtener información sobre cómo crear un componente de Windows Runtime con C++/WinRT, consulte Componentes de Windows Runtime con C++/WinRT.
En este tutorial se muestra cómo crear un archivo DLL básico del componente de Windows Runtime que se pueda llamar desde JavaScript, C# o Visual Basic. Antes de comenzar este tutorial, asegúrate de que conoces conceptos como la interfaz binaria abstracta (ABI), las clases de referencia y las extensiones del componente de Visual C++, que facilitan el trabajo con clases de referencia. Para obtener más información, vea Componentes de Windows Runtime con C++/CX y Referencia del lenguaje Visual C++ (C++/CX).
Creación del archivo DLL del componente de C++
En este ejemplo, primero creamos el proyecto de componente, pero podría crear primero el proyecto de JavaScript. No importa el orden.
Observe que la clase principal del componente contiene ejemplos de definiciones de propiedad y método, y una declaración de evento. Estos se proporcionan solo para mostrar cómo se hace. No son necesarios y, en este ejemplo, reemplazaremos todo el código generado por nuestro propio código.
Para crear el proyecto de componente de C++
En la barra de menús de Visual Studio, elija Archivo, Nuevo, Proyecto.
En el cuadro de diálogo Nuevo proyecto , en el panel izquierdo, expanda Visual C++ y, a continuación, seleccione el nodo para aplicaciones universales de Windows.
En el panel central, seleccione Componente de Windows Runtime y, a continuación, asigne al proyecto el nombre WinRT_CPP.
Elija el botón Aceptar .
Para agregar una clase activable al componente
Una clase activable es aquella que el código de cliente puede crear mediante una nueva expresión (New in Visual Basic o ref new in C++). En el componente, se declara como clase ref pública sellada. De hecho, los archivos Class1.h y .cpp ya tienen una clase ref. Puede cambiar el nombre, pero en este ejemplo usaremos el nombre predeterminado: Class1. Puede definir clases ref adicionales o clases regulares en el componente si son necesarias. Para obtener más información sobre las clases ref, vea Type System (C++/CX).
Agregue estas directivas #include a Class1.h:
#include <collection.h>
#include <ppl.h>
#include <amp.h>
#include <amp_math.h>
collection.h es el archivo de encabezado de las clases concretas de C++, como la clase Platform::Collections::Vector y la clase Platform::Collections::Map, que implementa interfaces neutras de lenguaje definidas por Windows Runtime. Los encabezados de amp se usan para ejecutar cálculos en la GPU. No tienen equivalentes de Windows Runtime y eso está bien porque son privados. En general, por motivos de rendimiento, debe usar código ISO C++ y bibliotecas estándar internamente dentro del componente; es solo la interfaz de Windows Runtime que debe expresarse en los tipos de Windows Runtime.
Para agregar un delegado en el ámbito del 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 delegado determinado y cualquier método de controlador de eventos que se suscribe al evento debe tener la firma 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 evento público de este tipo; esto permite al código de cliente proporcionar métodos que se invocan cuando se desencadena el evento.
Agregue la siguiente declaración de delegado en el ámbito del espacio de nombres en Class1.h, justo antes de la declaración Class1.
public delegate void PrimeFoundHandler(int result);
Si el código no se alinea correctamente al pegarlo en Visual Studio, simplemente presione 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ápido. 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 asincrónico que devuelve un resultado y el último define un método asincrónico que devuelve void. Estas interfaces también permiten que el código de cliente reciba actualizaciones en 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 usa para serializar las invocaciones de eventos de subprocesos de trabajo a la interfaz de usuario.
private:
bool is_prime(int n);
Windows::UI::Core::CoreDispatcher^ m_dispatcher;
Para agregar las directivas de encabezado y espacio de nombres
- En Class1.cpp, agregue estas directivas de #include:
#include <ppltasks.h>
#include <concurrent_vector.h>
- Ahora agregue 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 de ComputeResult
En Class1.cpp, agregue la siguiente implementación del método. Este método se ejecuta de forma sincrónica en el subproceso que realiza la llamada, pero es muy rápido porque usa C++ AMP para paralelizar el cálculo en la GPU. Para obtener más información, consulte Información general sobre C++ AMP. Los resultados se anexan a un tipo concreto Platform::Collections::Vector<T> , que se convierte implícitamente en un objeto 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 http://msdn.microsoft.com/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 de GetPrimesOrdered y su método auxiliar
En Class1.cpp, agregue las implementaciones para GetPrimesOrdered y el método auxiliar is_prime. GetPrimesOrdered usa una clase concurrent_vector y un bucle de función parallel_for para dividir el trabajo y usar los recursos máximos del equipo en el que se ejecuta el programa para generar resultados. Una vez calculados, almacenados y ordenados los resultados, se agregan a platform::Collections::Vector<T> y se devuelven como Windows::Foundation::Collections::IVector<T> al código de cliente.
Observe el código del informador de progreso, que permite al cliente enlazar una barra de progreso u otra interfaz de usuario para mostrar al usuario cuánto más tiempo tardará la operación. Los informes de progreso tienen un costo. Un evento debe activarse en el lado del componente y controlarse en el subproceso de la interfaz de usuario, y el valor de progreso debe almacenarse en cada iteración. Una manera de minimizar el costo es limitando la frecuencia con la que se desencadena un evento de progreso. Si el costo sigue siendo prohibitivo o si no puede calcular la longitud de la operación, considere la posibilidad de usar un anillo de progreso, lo 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 de GetPrimesUnordered
El último paso para crear el componente de C++ es agregar la implementación de GetPrimesUnordered en Class1.cpp. Este método devuelve cada resultado a medida que se encuentra, sin esperar hasta 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. De nuevo, observe que se usa un periodista de progreso. Este método también usa 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);
});
}
Creación de una aplicación cliente de JavaScript (Visual Studio 2017)
Si desea crear un cliente de C#, puede omitir esta sección.
Nota:
los proyectos de Plataforma universal de Windows (UWP) que usan JavaScript no se admiten en Visual Studio 2019. Consulte JavaScript y TypeScript en Visual Studio 2019. Para seguir esta sección, se recomienda usar Visual Studio 2017. Consulte JavaScript en Visual Studio 2017.
Para crear un proyecto de JavaScript
En Explorador de soluciones (en Visual Studio 2017; vea la nota anterior), abra el menú contextual del nodo Solución y elija Agregar, Nuevo proyecto.
Expanda JavaScript (podría estar anidado en Otros lenguajes) y elija Aplicación vacía (Windows universal) .
Acepte el nombre predeterminado(App1) eligiendo el botón Aceptar .
Abra el menú contextual del nodo del proyecto App1 y elija Establecer como proyecto de inicio.
Agregue una referencia de proyecto a WinRT_CPP:
Abra el menú contextual del nodo Referencias y elija Agregar referencia.
En el panel izquierdo del cuadro de diálogo Administrador de referencias, seleccione Proyectos y, a continuación, seleccione Solución.
En el panel central, seleccione WinRT_CPP y elija el botón Aceptar .
Para agregar el código HTML que invoca los controladores de eventos de JavaScript
Pegue este código HTML en el <nodo body> de la página default.html:
<div id="LogButtonDiv">
<button id="logButton">Logarithms using AMP</button>
</div>
<div id="LogResultDiv">
<p id="logResult"></p>
</div>
<div id="OrderedPrimeButtonDiv">
<button id="orderedPrimeButton">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">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">Clear</button>
</div>
Para agregar estilos
En default.css, quite el estilo del cuerpo y agregue 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 los controladores de eventos de JavaScript que llaman al archivo DLL del componente
Agregue las siguientes funciones al final del archivo default.js. Se llama a estas funciones cuando se eligen los botones de la página principal. Observe cómo JavaScript activa la clase C++ y, a continuación, 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;
}
Agregue código para agregar los agentes de escucha de eventos reemplazando la llamada existente a WinJS.UI.processAll en app.onactivated en default.js por el código siguiente que implementa el registro de eventos en un bloque then. Para obtener una explicación detallada de esto, consulte Creación de una aplicación "Hola mundo" (JS).
args.setPromise(WinJS.UI.processAll().then( function completed() {
var logButton = document.getElementById("logButton");
logButton.addEventListener("click", LogButton_Click, false);
var orderedPrimeButton = document.getElementById("orderedPrimeButton");
orderedPrimeButton.addEventListener("click", ButtonOrdered_Click, false);
var buttonUnordered = document.getElementById("ButtonUnordered");
buttonUnordered.addEventListener("click", ButtonUnordered_Click, false);
var buttonClear = document.getElementById("Button_Clear");
buttonClear.addEventListener("click", ButtonClear_Click, false);
}));
Presione F5 para ejecutar la aplicación.
Creación de una aplicación cliente de C#
Para crear un proyecto de C#
En Explorador de soluciones, abra el menú contextual del nodo Solución y, a continuación, elija Agregar, Nuevo proyecto.
Expanda Visual C# (podría anidarse en Otros lenguajes), seleccione Windows y, después , Universal en el panel izquierdo y, a continuación, seleccione Aplicación en blanco en el panel central.
Asigne un nombre a esta aplicación CS_Client y elija el botón Aceptar .
Abra el menú contextual del nodo del proyecto CS_Client y elija Establecer como proyecto de inicio.
Agregue una referencia de proyecto a WinRT_CPP:
Abra el menú contextual del nodo Referencias y elija Agregar referencia.
En el panel izquierdo del cuadro de diálogo Administrador de referencias, seleccione Proyectos y, a continuación, seleccione Solución.
En el panel central, seleccione WinRT_CPP y elija el botón Aceptar .
Para agregar el XAML que define la interfaz de usuario
Copie el código siguiente en el elemento Grid de 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 los controladores de eventos para los botones
En Explorador de soluciones, abra MainPage.xaml.cs. (Es posible que el archivo esté anidado en MainPage.xaml). Agregue una directiva using para System.Text y agregue el controlador de eventos para el cálculo logaritmo en la clase MainPage.
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();
}
Agregue 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();
}
Agregue el controlador de eventos para el resultado no ordenado y para el botón que borra los resultados para que pueda 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 = "";
}
Ejecución de la aplicación
Seleccione el proyecto de C# o el proyecto de JavaScript como proyecto de inicio; para ello, abra el menú contextual del nodo del proyecto en Explorador de soluciones y elija Establecer como proyecto de inicio. A continuación, presione F5 para ejecutar con depuración o Ctrl+F5 para ejecutarse sin depurar.
Inspección del componente en el Examinador de objetos (opcional)
En el Examinador de objetos, puedes inspeccionar todos los tipos de Windows Runtime definidos en archivos .winmd. Esto incluye los tipos del espacio de nombres platform y el espacio de nombres predeterminado. Sin embargo, dado que los tipos del espacio de nombres Platform::Collections se definen en las colecciones de archivos de encabezado.h, no en un archivo winmd, no aparecen en el Examinador de objetos.
Para inspeccionar un componente
En la barra de menús, elija Ver, Examinador de objetos (Ctrl+Alt+J).
En el panel izquierdo del Examinador de objetos, expanda el nodo WinRT_CPP para mostrar los tipos y métodos definidos en el componente.
Sugerencias de depuración
Para obtener una mejor experiencia de depuración, descargue los símbolos de depuración de los servidores de símbolos públicos de Microsoft:
Para descargar símbolos de depuración
En la barra de menús, elija Herramientas, Opciones.
En el cuadro de diálogo Opciones , expanda Depuración y seleccione Símbolos.
Seleccione Servidores de símbolos de Microsoft y elija el botón Aceptar .
Es posible que tarde algún tiempo en descargar los símbolos la primera vez. Para obtener un rendimiento más rápido la próxima vez que presione F5, especifique un directorio local en el que almacenar en caché los símbolos.
Al depurar una solución de JavaScript que tenga un archivo DLL de componente, puede establecer el depurador para habilitar el script paso a paso o recorrer el código nativo en el componente, pero no ambos al mismo tiempo. Para cambiar la configuración, abra el menú contextual del nodo del proyecto de JavaScript en Explorador de soluciones y elija Propiedades, Depuración, Tipo de depurador.
Asegúrese de seleccionar las funcionalidades adecuadas en el diseñador de paquetes. Para abrir el diseñador de paquetes, abra el archivo Package.appxmanifest. Por ejemplo, si está intentando acceder mediante programación a archivos en la carpeta Imágenes, asegúrese 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 las propiedades o métodos públicos del componente, asegúrese de que en JavaScript usa mayúsculas y minúsculas camel. Por ejemplo, se debe hacer referencia al ComputeResult
método C++ como computeResult
en JavaScript.
Si quitas un proyecto de componente de Windows Runtime de C++ de una solución, también debes quitar manualmente la referencia del proyecto de JavaScript. Si no lo hace, se evitan las operaciones posteriores de depuración o compilación. Si es necesario, puede agregar una referencia de ensamblado al archivo DLL.