本演练演示如何创建可从 JavaScript、C# 或 Visual Basic 中调用的基本 Windows 运行时组件 DLL。 在开始本演练之前,请确保你已了解抽象二进制接口 (ABI)、ref 类等概念以及更便于使用 ref 类的 Visual C++ 组件扩展。 有关更多信息,请参见用 C++ 创建 Windows 运行时组件和 Visual C++ 语言参考 (C++/CX)。
创建 C++ 组件 DLL
在此示例中,我们首先创建组件项目,但也可以先创建 JavaScript 项目。 顺序并不重要。
请注意,组件的主类包含属性和方法定义的示例以及事件声明。 提供这些仅为演示具体操作方法。 这些内容并非必需内容,在此示例中,我们会将所有生成的代码都替换为自己的代码。
创建 C++ 组件项目
在 Visual Studio 菜单栏上,选择**“文件”、“新建”、“项目”**。
在**“新建项目”对话框的左窗格中,展开“Visual C++”**,然后选择 Windows 应用商店应用程序的节点。
在中间窗格中,选择**“Windows 运行时组件”**,然后将项目命名为 WinRT_CPP。
选择**“确定”**按钮。
向组件中添加可激活的类
- “可激活的类”是指客户端代码可使用 new 表达式(Visual Basic 中的 New 或 C++ 中的 ref new)创建的类。 在组件中,将其声明为 public ref class sealed。 实际上,Class1.h 和 .cpp 文件已经有了一个 ref 类。 可以更改名称,但在此示例中我们将使用默认名称 Class1。 如果需要,可在组件中定义其他 ref 类或常规类。 有关 ref 类的更多信息,请参见类型系统 (C++/CX)。
添加所需的 #include 指令
将这些 #include 指令添加到 Class1.h 中:
#include <collection.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 的委托类型。 接下来,此代码声明了一个该类型的公共 event;这使客户端代码可以提供激发事件时调用的方法。
在 Class1.h 中的命名空间范围处,将以下委托声明添加到 Class1 声明之前。
public delegate void PrimeFoundHandler(int result);
提示
将代码粘贴到 Visual Studio 中后,如果代码的排列格式不正确,只需按 Ctrl+K+D 即可修复整个文件的缩进问题。
添加公共成员
该类会公开三个公共方法和一个公共事件。 第一个方法是同步方法,因为它的执行速度始终都非常快。 由于另外两个方法可能需要一些时间才能执行完毕,因此它们是异步方法,这样可以避免阻塞 UI 线程。 这些方法将返回 IAsyncOperationWithProgress 和 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;
添加私有成员
该类包含三个隐私成员:两个用于数值计算的 Helper 方法,以及一个用于将事件调用从工作线程封送回 UI 线程的 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 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; }
添加 GetPrimesOrdered 及其 Helper 方法的实现
在 Class1.cpp 中,添加 GetPrimesOrdered 和 is_prime 帮助器方法的实现。 GetPrimesOrdered 使用 concurrent_vector 类和 parallel_for 函数循环拆分工作,并最大限度地利用运行程序的计算机上的资源来生成结果。 在对结果进行计算、存储和排序后,它们将被添加到 Platform::Collections::Vector<T> 中,并作为 Windows::Foundation::Collections::IVector<T> 返回给客户端代码。
请注意进度报告程序的代码,此代码可使客户端挂接一个进度栏或其他 UI,以向用户显示操作还需要多长时间才能完成。 进度报告会产生开销。 必须在组件一端激发事件并在 UI 线程上处理该事件,而且每次迭代时都必须存储进度值。 若要最大限度地降低开销,一种方式是限制激发进度事件的频率。 如果开销依然很高,或者无法估计操作时长,可考虑采用进度环,它只显示正在执行操作,但不显示完成操作的剩余时间。
// 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++ 组件的最后一步是在 Class1.cpp 中添加 GetPrimesUnordered 的实现。 此方法会在找到每个结果时立即予以返回,而不是等找到所有结果后再一起返回。 每个结果都会返回到事件处理程序中,并实时显示在 UI 上。 请注意,这里也使用了进度报告程序。 此方法还使用 is_prime Helper 方法。
// 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); }); }
按 Ctrl+Shift+B 生成组件。
创建 JavaScript 客户端应用程序
创建 JavaScript 项目
-
备注
如果你只希望创建 C# 客户端,则可以跳过此部分。
在**“解决方案资源管理器”中,打开“解决方案”节点的快捷菜单,然后选择“添加”、“新建项目”**。
展开**“JavaScript”(可能嵌套在“其他语言”下),然后选择“空白应用程序”**。
选择**“确定”**按钮接受默认名称 App1。
打开“App1”项目节点的快捷菜单,然后选择**“设为启动项目”**。
向 WinRT_CPP 中添加项目引用:
打开**“引用”节点的快捷菜单,然后选择“添加引用”**。
在**“引用管理器”对话框的左窗格中选择“解决方案”,然后选择“项目”**。
在中间窗格中选择**“WinRT_CPP”,然后选择“确定”**按钮。
添加调用 JavaScript 事件处理程序的 HTML
将以下 HTML 粘贴到 default.html 页的 <body> 节点中:
<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>
添加样式
在 default.css 中,移除 body 样式,然后添加下列样式:
#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; }
添加调入组件 DLL 的 JavaScript 事件处理程序
在 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; }
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; }
按 F5 运行应用程序。
创建 C# 客户端应用程序
与 JavaScript 客户端一样,从 C# 客户端中也可以轻松调用 C++ Windows 运行时组件。 下面的步骤演示如何创建一个与上一部分中的 JavaScript 客户端大体等效的 C# 客户端。
创建 C# 项目
在**“解决方案资源管理器”中,打开“解决方案”节点的快捷菜单,然后选择“添加”、“新建项目”**。
展开**“Visual C#”(可能嵌套在“其他语言”下),选择左窗格中的“Windows 应用商店”,然后选择中间窗格中的“空白应用程序”**。
将此应用程序命名为 CS_Client,然后选择**“确定”**按钮。
打开“CS_Client”项目节点的快捷菜单,然后选择**“设为启动项目”**。
向 WinRT_CPP 中添加项目引用:
打开**“引用”节点的快捷菜单,然后选择“添加引用”**。
在**“引用管理器”对话框的左窗格中选择“解决方案”,然后选择“项目”**。
在中间窗格中选择**“WinRT_CPP”,然后选择“确定”**按钮。
添加定义用户界面的 XAML
将以下 ScrollViewer 及其内容添加到 mainpage.xaml 中的 Grid:
<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 下。)为 System.Text 添加 using 指令,然后将用于 MainPage 类中 Logarithm 计算的事件处理程序添加到 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(); }
为有序结果添加事件处理程序:
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 只运行而不调试。
在对象浏览器中检查组件(可选)
在**“对象浏览器”中,可以检查在 .winmd 文件中定义的所有 Windows 运行时类型。 这包括 Platform 命名空间和默认命名空间中的类型。 但是,由于 Platform::Collections 命名空间中的类型都在头文件 collections.h 而不是 winmd 文件中定义,因此它们不会显示在“对象浏览器”**中。
检查组件
在菜单栏中,依次选择**“视图”、“其他窗口”、“对象浏览器”**。
在**“对象浏览器”的左窗格中,展开“WinRT_CPP”**节点,以显示在组件中定义的类型和方法。
调试提示
为了获得更好的调试体验,请从公共的 Microsoft 符号服务器下载调试符号:
在菜单栏上,依次选择**“工具”、“选项”**。
在**“选项”对话框中,展开“调试”,然后选择“符号”**。
选择**“Microsoft 符号服务器”,然后选择“确定”**按钮。
初次下载符号可能需要一些时间。 为了获得更快的性能,下次按 F5 时,请指定要在其中缓存符号的本地目录。
在调试包含组件 DLL 的 JavaScript 解决方案时,可以对调试器进行设置,以启用逐句调试脚本或逐句调试组件中的本机代码,但不能同时启用这两项功能。 若要更改设置,请在**“解决方案资源管理器”中打开 JavaScript 项目节点的快捷菜单中,然后依次选择“属性”、“调试”、“调试器类型”**。
确保在包设计器中选择适当功能。 例如,如果尝试通过编程方式访问“Pictures”文件夹中的文件,请确保在包设计器的**“功能”窗格中选中“图片库”**复选框。
如果 JavaScript 代码无法识别组件中的公共属性或方法,请确保在 JavaScript 中使用 camel 大小写形式。 例如,ComputeResult C++ 方法在 JavaScript 中必须作为 computeResult 来引用。
如果从解决方案中删除 C++ Windows 运行时组件项目,还必须从 JavaScript 项目中手动删除项目引用。 否则,将无法执行后续的调试或生成操作。 如果需要,随后可向 DLL 中添加程序集引用。
请参见
参考
Roadmap for Windows Store apps using C++