演练:用 C++ 创建一个基本的 Windows 运行时组件,然后从 JavaScript 或 C# 中调用该组件

本演练演示如何创建可从 JavaScript、C# 或 Visual Basic 中调用的基本 Windows 运行时组件 DLL。 在开始本演练之前,请确保你已了解抽象二进制接口 (ABI)、ref 类等概念以及更便于使用 ref 类的 Visual C++ 组件扩展。 有关更多信息,请参见用 C++ 创建 Windows 运行时组件Visual C++ 语言参考 (C++/CX)

创建 C++ 组件 DLL

在此示例中,我们首先创建组件项目,但也可以先创建 JavaScript 项目。 顺序并不重要。

请注意,组件的主类包含属性和方法定义的示例以及事件声明。 提供这些仅为演示具体操作方法。 这些内容并非必需内容,在此示例中,我们会将所有生成的代码都替换为自己的代码。

创建 C++ 组件项目

  1. 在 Visual Studio 菜单栏上,选择**“文件”“新建”“项目”**。

  2. 在**“新建项目”对话框的左窗格中,展开“Visual C++”**,然后选择 Windows 应用商店应用程序的节点。

  3. 在中间窗格中,选择**“Windows 运行时组件”**,然后将项目命名为 WinRT_CPP。

  4. 选择**“确定”**按钮。

向组件中添加可激活的类

  • “可激活的类”是指客户端代码可使用 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 线程。 这些方法将返回 IAsyncOperationWithProgressIAsyncActionWithProgress。 前者定义一个返回结果的异步方法,后者定义一个返回 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;
    

添加标头和命名空间指令

  1. 在 Class1.cpp 中,添加以下 #include 指令:

    #include <ppltasks.h>
    #include <concurrent_vector.h>
    
  2. 现在添加以下 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 的实现

  1. 创建 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);
        });
    }
    
    
  2. 按 Ctrl+Shift+B 生成组件。

创建 JavaScript 客户端应用程序

创建 JavaScript 项目

  1. 备注

    如果你只希望创建 C# 客户端,则可以跳过此部分。

    在**“解决方案资源管理器”中,打开“解决方案”节点的快捷菜单,然后选择“添加”“新建项目”**。

  2. 展开**“JavaScript”(可能嵌套在“其他语言”下),然后选择“空白应用程序”**。

  3. 选择**“确定”**按钮接受默认名称 App1。

  4. 打开“App1”项目节点的快捷菜单,然后选择**“设为启动项目”**。

  5. 向 WinRT_CPP 中添加项目引用:

    1. 打开**“引用”节点的快捷菜单,然后选择“添加引用”**。

    2. 在**“引用管理器”对话框的左窗格中选择“解决方案”,然后选择“项目”**。

    3. 在中间窗格中选择**“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 事件处理程序

  1. 在 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;
    }
    
    
  2. 按 F5 运行应用程序。

创建 C# 客户端应用程序

与 JavaScript 客户端一样,从 C# 客户端中也可以轻松调用 C++ Windows 运行时组件。 下面的步骤演示如何创建一个与上一部分中的 JavaScript 客户端大体等效的 C# 客户端。

创建 C# 项目

  1. 在**“解决方案资源管理器”中,打开“解决方案”节点的快捷菜单,然后选择“添加”“新建项目”**。

  2. 展开**“Visual C#”(可能嵌套在“其他语言”下),选择左窗格中的“Windows 应用商店”,然后选择中间窗格中的“空白应用程序”**。

  3. 将此应用程序命名为 CS_Client,然后选择**“确定”**按钮。

  4. 打开“CS_Client”项目节点的快捷菜单,然后选择**“设为启动项目”**。

  5. 向 WinRT_CPP 中添加项目引用:

    1. 打开**“引用”节点的快捷菜单,然后选择“添加引用”**。

    2. 在**“引用管理器”对话框的左窗格中选择“解决方案”,然后选择“项目”**。

    3. 在中间窗格中选择**“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>
    

为按钮添加事件处理程序

  1. 在**“解决方案资源浏览器”**中,打开 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();
    }
    
  2. 为有序结果添加事件处理程序:

    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();
    }
    
  3. 为无序结果及清除结果的按钮添加事件处理程序,以便再次运行代码。

    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 文件中定义,因此它们不会显示在“对象浏览器”**中。

检查组件

  1. 在菜单栏中,依次选择**“视图”“其他窗口”“对象浏览器”**。

  2. 在**“对象浏览器”的左窗格中,展开“WinRT_CPP”**节点,以显示在组件中定义的类型和方法。

调试提示

为了获得更好的调试体验,请从公共的 Microsoft 符号服务器下载调试符号:

  1. 在菜单栏上,依次选择**“工具”“选项”**。

  2. 在**“选项”对话框中,展开“调试”,然后选择“符号”**。

  3. 选择**“Microsoft 符号服务器”,然后选择“确定”**按钮。

初次下载符号可能需要一些时间。 为了获得更快的性能,下次按 F5 时,请指定要在其中缓存符号的本地目录。

在调试包含组件 DLL 的 JavaScript 解决方案时,可以对调试器进行设置,以启用逐句调试脚本或逐句调试组件中的本机代码,但不能同时启用这两项功能。 若要更改设置,请在**“解决方案资源管理器”中打开 JavaScript 项目节点的快捷菜单中,然后依次选择“属性”“调试”“调试器类型”**。

确保在包设计器中选择适当功能。 例如,如果尝试通过编程方式访问“Pictures”文件夹中的文件,请确保在包设计器的**“功能”窗格中选中“图片库”**复选框。

如果 JavaScript 代码无法识别组件中的公共属性或方法,请确保在 JavaScript 中使用 camel 大小写形式。 例如,ComputeResult C++ 方法在 JavaScript 中必须作为 computeResult 来引用。

如果从解决方案中删除 C++ Windows 运行时组件项目,还必须从 JavaScript 项目中手动删除项目引用。 否则,将无法执行后续的调试或生成操作。 如果需要,随后可向 DLL 中添加程序集引用。

请参见

参考

Roadmap for Windows Store apps using C++

其他资源

在 JavaScript 和 C++ 中开发 Windows 应用商店应用程序 Bing 地图行程优化器