Delen via


Walkthrough: Creating a basic Windows Runtime component in C++ and calling it from JavaScript or C#

 

This walkthrough shows how to create a basic Windows Runtime Component DLL that's callable from JavaScript, C#, or Visual Basic. Before you begin this walkthrough, make sure that you understand concepts such as the Abstract Binary Interface (ABI), ref classes, and the Visual C++ Component Extensions that make working with ref classes easier. For more information, see Creating Windows Runtime Components in C++ and Visual C++ Language Reference (C++/CX).

Creating the C++ component DLL

In this example, we create the component project first, but you could create the JavaScript project first. The order doesn’t matter.

Notice that the main class of the component contains examples of property and method definitions, and an event declaration. These are provided just to show you how it's done. They are not required, and in this example, we'll replace all of the generated code with our own code.

To create the C++ component project

  1. On the Visual Studio menu bar, choose File, New, Project.

  2. In the New Project dialog box, in the left pane, expand Visual C++ and then select the node for Windows Store apps.

  3. In the center pane, select Windows Runtime Component and then name the project WinRT_CPP.

  4. Choose the OK button.

To add an activatable class to the component

  1. An activatable class is one that client code can create by using a new expression (New in Visual Basic, or ref new in C++). In your component, you declare it as public ref class sealed. In fact, the Class1.h and .cpp files already have a ref class. You can change the name, but in this example we’ll use the default name—Class1. You can define additional ref classes or regular classes in your component if they are required. For more information about ref classes, see Type System (C++/CX).

To add the required #include directives

  • Add these #include directives to Class1.h:

    collection.h is the header file for C++ concrete classes such as the Platform::Collections::Vector Class and the Platform::Collections::Map Class, which implement language-neutral interfaces that are defined by the Windows Runtime. The amp headers are used to run computations on the GPU. They have no Windows Runtime equivalents, and that’s fine because they are private. In general, for performance reasons you should use ISO C++ code and standard libraries internally within the component; it’s just the Windows Runtime interface that must be expressed in Windows Runtime types.

To add a delegate at namespace scope

  1. A delegate is a construct that defines the parameters and return type for methods. An event is an instance of a particular delegate type, and any event handler method that subscribes to the event must have the signature that's specified in the delegate. The following code defines a delegate type that takes an int and returns void. Next the code declares a public event of this type; this enables client code to provide methods that are invoked when the event is fired.

    Add the following delegate declaration at namespace scope in Class1.h, just before the Class1 declaration.

Tip

If the code isn’t lining up correctly when you paste it into Visual Studio, just press Ctrl+K+D to fix the indentation for the entire file.

To add the public members

  1. The class exposes three public methods and one public event. The first method is synchronous because it always executes very fast. Because the other two methods might take some time, they are asynchronous so that they don’t block the UI thread. These methods return IAsyncOperationWithProgress and IAsyncActionWithProgress. The former defines an async method that returns a result, and the latter defines an async method that returns void. These interfaces also enable client code to receive updates on the progress of the operation.

To add the private members

  1. The class contains three private members: two helper methods for the numeric computations and a CoreDispatcher object that’s used to marshal the event invocations from worker threads back to the UI thread.

To add the header and namespace directives

  1. In Class1.cpp, add these #include directives:

  2. Now add these using statements to pull in the required namespaces:

To add the implementation for ComputeResult

  1. In Class1.cpp, add the following method implementation. This method executes synchronously on the calling thread, but it is very fast because it uses C++ AMP to parallelize the computation on the GPU. For more information, see C++ AMP Overview. The results are appended to a Platform::Collections::Vector<T> concrete type, which is implicitly converted to a Windows::Foundation::Collections::IVector<T> when it is returned.

To add the implementation for GetPrimesOrdered and its helper method

  1. In Class1.cpp, add the implementations for GetPrimesOrdered and the is_prime helper method. GetPrimesOrdered uses a concurrent_vector Class and a parallel_for Function loop to divide up the work and use the maximum resources of the computer on which the program is running to produce results. After the results are computed, stored, and sorted, they are added to a Platform::Collections::Vector<T> and returned as Windows::Foundation::Collections::IVector<T> to client code.

    Notice the code for the progress reporter, which enables the client to hook up a progress bar or other UI to show the user how much longer the operation is going to take. Progress reporting has a cost. An event must be fired on the component side and handled on the UI thread, and the progress value must be stored on each iteration. One way to minimize the cost is by limiting the frequency at which a progress event is fired. If the cost is still prohibitive, or if you can't estimate the length of the operation, then consider using a progress ring, which shows that an operation is in progress but doesn't show time remaining until completion.

To add the implementation for GetPrimesUnordered

  1. The last step to create the C++ component is to add the implementation for the GetPrimesUnordered in Class1.cpp. This method returns each result as it is found, without waiting until all results are found. Each result is returned in the event handler and displayed on the UI in real time. Again, notice that a progress reporter is used. This method also uses the is_prime helper method.

  2. Press Ctrl+Shift+B to build the component.

Creating a JavaScript client app

To create a JavaScript project

Note

If you just want to create a C# client, you can skip this section.

In **Solution Explorer**, open the shortcut menu for the **Solution** node and choose **Add**, **New Project**.
  1. Expand JavaScript (it might be nested under Other Languages) and choose Blank App.

  2. Accept the default name—App1—by choosing the OK button.

  3. Open the shortcut menu for the App1 project node and choose Set as Startup Project.

  4. Add a project reference to WinRT_CPP:

    1. Open the shortcut menu for the References node and choose Add Reference.

    2. In the left pane of the References Manager dialog box, select Solution and then select Projects.

    3. In the center pane, select WinRT_CPP and then choose the OK button.

To add the HTML that invokes the JavaScript event handlers

  1. Paste this HTML into the <body> node of the default.html page:

To add styles

  1. In default.css, remove the body style and then add these styles:

    #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;
    }
    

To add the JavaScript event handlers that call into the component DLL

  1. Add the following functions at the end of the default.js file. These functions are called when the buttons on the main page are chosen. Notice how JavaScript activates the C++ class, and then calls its methods and uses the return values to populate the HTML labels.

  2. Press F5 to run the app.

Creating a C# client app

The C++ Windows Runtime Component DLL can just as easily be called from a C# client as from a JavaScript client. The following steps show how to make a C# client that is roughly equivalent to the JavaScript client in the previous section.

To create a C# project

  1. In Solution Explorer, open the shortcut menu for the Solution node and then choose Add, New Project.

  2. Expand Visual C# (it might be nested under Other Languages), select Windows Store in the left pane, and then select Blank App in the middle pane.

  3. Name this app CS_Client and then choose the OK button.

  4. Open the shortcut menu for the CS_Client project node and choose Set as Startup Project.

  5. Add a project reference to WinRT_CPP:

    1. Open the shortcut menu for the References node and choose Add Reference.

    2. In the left pane of the References Manager dialog box, select Solution and then select Projects.

    3. In the center pane, select WinRT_CPP and then choose the OK button.

To add the XAML that defines the user interface

  1. Add the following ScrollViewer and its contents to the Grid in 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>
    

To add the event handlers for the buttons

  1. In Solution Explorer, open mainpage.xaml.cs. (The file might be nested under mainpage.xaml.) Add a using directive for System.Text, and then add the event handler for the Logarithm calculation in the MainPage class just after OnNavigateTo.

  2. Add the event handler for the ordered result:

  3. Add the event handler for the unordered result, and for the button that clears the results so that you can run the code again.

Running the app

Select either the C# project or JavaScript project as the startup project by opening the shortcut menu for the project node in Solution Explorer and choosing Set As Startup Project. Then press F5 to run with debugging, or Ctrl+F5 to run without debugging.

Inspecting your component in Object Browser (optional)

In Object Browser, you can inspect all Windows Runtime types that are defined in .winmd files. This includes the types in the Platform namespace and the default namespace. However, because the types in the Platform::Collections namespace are defined in the header file collections.h, not in a winmd file, they don’t appear in Object Browser.

To inspect the component

  1. On the menu bar, choose View, Other Windows, Object Browser.

  2. In the left pane of the Object Browser, expand the WinRT_CPP node to show the types and methods that are defined on your component.

Debugging tips

For a better debugging experience, download the debugging symbols from the public Microsoft symbol servers:

  1. On the menu bar, choose Tools, Options.

  2. In the Options dialog box, expand Debugging and select Symbols.

  3. Select Microsoft Symbol Servers and the choose the OK button.

It might take some time to download the symbols the first time. For faster performance the next time you press F5, specify a local directory in which to cache the symbols.

When you debug a JavaScript solution that has a component DLL, you can set the debugger to enable either stepping through script or stepping through native code in the component, but not both at the same time. To change the setting, open the shortcut menu for the JavaScript project node in Solution Explorer and choose Properties, Debugging, Debugger Type.

Be sure to select appropriate capabilities in the package designer. For example, if you are attempting to programmatically access files in the Pictures folder, be sure to select the Pictures Library check box in the Capabilities pane of the package designer.

If your JavaScript code doesn't recognize the public properties or methods in the component, make sure that in JavaScript you are using camel casing. For example, the ComputeResult C++ method must be referenced as computeResult in JavaScript.

If you remove a C++ Windows Runtime Component project from a solution, you must also manually remove the project reference from the JavaScript project. Failure to do so prevents subsequent debug or build operations. If necessary, you can then add an assembly reference to the DLL.

See Also

Developing Bing Maps Trip Optimizer, a Windows Store app in JavaScript and C++

)B71E4A4-5D8A-4A20-B2EC-E40062675EC1