Пошаговое руководство по созданию компонента среды выполнения Windows на C++/CX и его вызову с помощью JavaScript или C#
Примечание.
Этот раздел поможет вам поддерживать приложение C++/CX. Но мы рекомендуем использовать C++/WinRT для новых приложений. C++/WinRT — это полностью стандартная проекция языка C++17 для API среды выполнения Windows (WinRT), реализованная как библиотека на основе файлов заголовков и предназначенная для предоставления вам первоклассного доступа к современным интерфейсам API Windows. Сведения о создании компонента среды выполнения Windows с помощью C++/WinRT см. в статье Создание компонентов среды выполнения Windows с помощью C++/WinRT.
В этом пошаговом руководстве описано, как создать простой компонент среды выполнения Windows, являющийся библиотекой DLL, которую можно вызвать с помощью JavaScript, C# или Visual Basic. Прежде чем приступить к этому пошаговому руководству, убедитесь, что вы понимаете такие понятия, как абстрактный двоичный интерфейс (ABI), классы ссылок и расширения компонентов Visual C++, которые упрощают работу с классами ссылок. Дополнительные сведения см. в разделе среда выполнения Windows компонентов с справочником по языку C++/CX и Visual C++ (C++/CX).
Создание библиотеки DLL компонента C++
В этом примере сначала мы создадим проект компонента, но сначала можно создать проект JavaScript. Порядок не имеет значения.
Обратите внимание, что основной класс компонента содержит примеры определений свойств и методов и объявление события. Они предоставляются только для того, чтобы показать, как это сделано. Они не требуются, и в этом примере мы заменим весь созданный код собственным кодом.
Создание проекта компонента C++
В строке меню Visual Studio выберите "Файл", "Создать", "Проект".
В диалоговом окне "Новый проект" в левой области разверните Visual C++ и выберите узел для универсальных приложений Windows.
В центральной области выберите компонент среда выполнения Windows, а затем назовите проект WinRT_CPP.
Нажмите кнопку ОК.
Добавление активируемого класса в компонент
Активируемый класс — это тот, который клиентский код может создать с помощью нового выражения (Создать в Visual Basic или ref new in C++). В компоненте вы объявляете его как открытый класс ссылок, запечатанный. На самом деле файлы Class1.h и .cpp уже имеют класс ссылок. Вы можете изменить имя, но в этом примере мы будем использовать имя по умолчанию — Class1. При необходимости можно определить дополнительные классы ссылок или обычные классы в компоненте. Дополнительные сведения о классах ссылок см. в разделе Type System (C++/CX).
Добавьте эти директивы #include в Class1.h:
#include <collection.h>
#include <ppl.h>
#include <amp.h>
#include <amp_math.h>
collection.h — это файл заголовка для конкретных классов C++, таких как класс Platform::Collections::Vector и класс Platform::Collections::Map, реализующий интерфейсы, не зависящие от языка, определенные среда выполнения Windows. Заголовки amp используются для выполнения вычислений на GPU. У них нет среда выполнения Windows эквивалентов, и это нормально, потому что они частные. Как правило, по соображениям производительности следует использовать код ISO C++ и стандартные библиотеки внутри компонента; это просто интерфейс среда выполнения Windows, который должен быть выражен в среда выполнения Windows типах.
Добавление делегата в области пространства имен
Делегат — это конструкция, которая определяет параметры и тип возврата для методов. Событие — это экземпляр определенного типа делегата, и любой метод обработчика событий, который подписывается на событие, должен иметь подпись, указанную в делегате. Следующий код определяет тип делегата, который принимает int и возвращает void. Далее код объявляет общедоступное событие этого типа; Это позволяет клиентскому коду предоставлять методы, вызываемые при запуске события.
Добавьте следующее объявление делегата в области пространства имен в Class1.h непосредственно перед объявлением Class1.
public delegate void PrimeFoundHandler(int result);
Если код не правильно выстраивается при вставке в Visual Studio, просто нажмите клавиши CTRL+K+D, чтобы исправить отступ для всего файла.
Добавление общедоступных участников
Класс предоставляет три открытых метода и одно общедоступное событие. Первый метод синхронен, так как он всегда выполняется очень быстро. Так как другие два метода могут занять некоторое время, они асинхронны, чтобы они не блокируют поток пользовательского интерфейса. Эти методы возвращают IAsyncOperationWithProgresss и IAsyncActionWithProgress. Первый определяет асинхронный метод, возвращающий результат, и последний определяет асинхронный метод, возвращающий void. Эти интерфейсы также позволяют клиентскому коду получать обновления по ходу выполнения операции.
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;
Добавление частных членов
Класс содержит три частных члена: два вспомогательных метода для числовых вычислений и объект CoreDispatcher, используемый для маршалинга событий из рабочих потоков обратно в поток пользовательского интерфейса.
private:
bool is_prime(int n);
Windows::UI::Core::CoreDispatcher^ m_dispatcher;
Добавление директив заголовка и пространства имен
- В Class1.cpp добавьте следующие директивы #include:
#include <ppltasks.h>
#include <concurrent_vector.h>
- Теперь добавьте эти инструкции using для извлечения необходимых пространств имен:
using namespace concurrency;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation;
using namespace Windows::UI::Core;
Добавление реализации для ComputeResult
В Class1.cpp добавьте следующую реализацию метода. Этот метод выполняется синхронно в вызывающем потоке, но это очень быстро, так как он использует C++ AMP для параллелизации вычислений на GPU. Дополнительные сведения см. в обзоре C++ AMP. Результаты добавляются к конкретному типу Platform::Collections::Vector<T> , который неявно преобразуется в windows::Foundation::Collections::IVector<T> при возврате.
//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;
}
Добавление реализации для GetPrimesOrdered и вспомогательного метода
В Class1.cpp добавьте реализации для GetPrimesOrdered и вспомогательный метод is_prime. GetPrimesOrdered использует класс concurrent_vector и цикл функций parallel_for для разделения работы и использования максимальных ресурсов компьютера, на котором выполняется программа для получения результатов. После вычисления, хранения и сортировки результатов они добавляются в код клиента Platform::Collections::Vector<T> и возвращаются как Windows::Foundation::Collections::Collections::IVector<T> .
Обратите внимание на код для репортера хода выполнения, который позволяет клиенту подключить индикатор выполнения или другой пользовательский интерфейс, чтобы показать пользователю, сколько времени займет операция. Отчет о ходе выполнения имеет затраты. Событие должно быть запущено на стороне компонента и обработано в потоке пользовательского интерфейса, а значение хода выполнения должно храниться в каждой итерации. Одним из способов минимизации затрат является ограничение частоты запуска события хода выполнения. Если стоимость по-прежнему запрещена, или если вы не можете оценить длину операции, рассмотрите возможность использования кольца выполнения, который показывает, что операция выполняется, но не показывает время, оставшееся до завершения.
// 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());
});
}
Добавление реализации для GetPrimesUnordered
Последним шагом для создания компонента C++ является добавление реализации для GetPrimesUnordered в Class1.cpp. Этот метод возвращает каждый результат, как он найден, не ожидая, пока не будут найдены все результаты. Каждый результат возвращается в обработчике событий и отображается в пользовательском интерфейсе в режиме реального времени. Опять же, обратите внимание, что используется репортер хода выполнения. Этот метод также использует вспомогательный метод 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);
});
}
Создание клиентского приложения JavaScript (Visual Studio 2017)
Если вы хотите создать клиент C#, можно пропустить этот раздел.
Примечание.
проекты универсальная платформа Windows (UWP) с помощью JavaScript не поддерживаются в Visual Studio 2019. См . javaScript и TypeScript в Visual Studio 2019. Чтобы следовать этому разделу, рекомендуется использовать Visual Studio 2017. См . JavaScript в Visual Studio 2017.
Создание проекта JavaScript
В Обозреватель решений (в Visual Studio 2017; см. примечание выше), откройте контекстное меню для узла решения и нажмите кнопку "Добавить", "Создать проект".
Разверните JavaScript (он может быть вложен в другие языки) и выберите пустое приложение (универсальное приложение Для Windows).
Примите имя по умолчанию — App1, нажав кнопку "ОК ".
Откройте контекстное меню для узла проекта App1 и выберите "Задать в качестве запускаемого проекта".
Добавьте ссылку на проект в WinRT_CPP:
Откройте контекстное меню узла "Ссылки" и нажмите кнопку "Добавить ссылку".
В левой области диалогового окна "Диспетчер ссылок" выберите "Проекты " и выберите "Решение".
В центральной области выберите WinRT_CPP и нажмите кнопку "ОК "
Добавление HTML-кода, вызывающего обработчики событий JavaScript
Вставьте этот HTML-код в основной <> узел страницы 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>
Добавление стилей
В default.css удалите стиль текста и добавьте следующие стили:
#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;
}
Добавление обработчиков событий JavaScript, которые вызываются в библиотеку DLL компонента
Добавьте следующие функции в конце файла default.js. Эти функции вызываются при выборе кнопок на главной странице. Обратите внимание, как JavaScript активирует класс C++, а затем вызывает его методы и использует возвращаемые значения для заполнения меток 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;
}
Добавьте код для добавления прослушивателей событий, заменив существующий вызов WinJS.UI.processAll в app.onactivated в default.js следующим кодом, реализующим регистрацию событий в блоке. Подробное описание этого см. в статье "Создание приложения Hello, World" (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);
}));
Нажмите клавишу F5, чтобы запустить приложение.
Создание клиентского приложения C#
Создание проекта C#
В Обозреватель решений откройте контекстное меню для узла решения и нажмите кнопку "Добавить", "Создать проект".
Разверните Visual C# (оно может быть вложено в другие языки), выберите Windows , а затем универсальную в левой области и выберите пустое приложение на средней панели.
Присвойте этому приложению имя CS_Client и нажмите кнопку "ОК ".
Откройте контекстное меню для узла проекта CS_Client и выберите "Задать в качестве запускаемого проекта".
Добавьте ссылку на проект в WinRT_CPP:
Откройте контекстное меню узла "Ссылки" и нажмите кнопку "Добавить ссылку".
В левой области диалогового окна "Диспетчер ссылок" выберите "Проекты " и выберите "Решение".
В центральной области выберите WinRT_CPP и нажмите кнопку "ОК ".
Добавление XAML, определяющего пользовательский интерфейс
Скопируйте следующий код в элемент Grid в 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>
Добавление обработчиков событий для кнопок
В Обозреватель решений откройте MainPage.xaml.cs. (Файл может быть вложен в mainPage.xaml.) Добавьте директиву using для System.Text и добавьте обработчик событий для вычисления Logarithm в классе 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();
}
Добавьте обработчик событий для упорядоченного результата:
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();
}
Добавьте обработчик событий для неупорядоченного результата и для кнопки, которая очищает результаты, чтобы вы могли снова запустить код.
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 = "";
}
Запуск приложения
Выберите проект C# или проект JavaScript в качестве запускаемого проекта, открыв контекстное меню для узла проекта в Обозреватель решений и выбрав "Задать как запускаемый проект". Затем нажмите клавишу F5, чтобы запустить с отладкой или CTRL+F5, чтобы запустить без отладки.
Проверка компонента в браузере объектов (необязательно)
В обозревателе объектов можно проверить все среда выполнения Windows типы, определенные в winmd-файлах. Сюда входят типы в пространстве имен платформы и пространстве имен по умолчанию. Однако, поскольку типы в пространстве имен Platform::Collections определяются в коллекции файлов заголовков.h, а не в winmd-файле, они не отображаются в браузере объектов.
Проверка компонента
В строке меню выберите "Вид", "Обозреватель объектов" (CTRL+ALT+J).
В левой области браузера объектов разверните узел WinRT_CPP, чтобы отобразить типы и методы, определенные в компоненте.
Советы по отладке
Чтобы улучшить отладку, скачайте символы отладки с общедоступных серверов символов Майкрософт:
Скачивание символов отладки
В строке меню выберите "Сервис", "Параметры".
В диалоговом окне "Параметры" разверните узел отладки и выберите "Символы".
Выберите серверы символов Майкрософт и нажмите кнопку "ОК ".
Может потребоваться некоторое время, чтобы скачать символы в первый раз. Для повышения производительности при следующем нажатии клавиши F5 укажите локальный каталог, в котором кэшируются символы.
При отладке решения JavaScript с библиотекой DLL компонента можно настроить отладчик, чтобы включить пошаговое выполнение скрипта или пошаговое выполнение машинного кода в компоненте, но не одновременно. Чтобы изменить этот параметр, откройте контекстное меню для узла проекта JavaScript в Обозреватель решений и выберите "Свойства", "Отладка", "Тип отладчика".
Убедитесь, что в конструкторе пакетов выбраны соответствующие возможности. Конструктор пакетов можно открыть, открыв файл Package.appxmanifest. Например, если вы пытаетесь программно получить доступ к файлам в папке "Изображения", установите флажок "Библиотека рисунков" в области возможностей конструктора пакетов.
Если код JavaScript не распознает общедоступные свойства или методы в компоненте, убедитесь, что в JavaScript вы используете верблюдю регистр. Например, ComputeResult
на метод C++ необходимо ссылаться как computeResult
на JavaScript.
При удалении проекта компонента C++ среда выполнения Windows из решения необходимо также вручную удалить ссылку на проект JavaScript. Сбой этого предотвращает последующие операции отладки или сборки. При необходимости можно добавить ссылку на сборку в библиотеку DLL.