Creating Windows Runtime Components in C++

Expand
19 out of 24 rated this helpful Rate this topic

Creating Windows Runtime Components in C++

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

This topic shows how to create a Windows Runtime Component DLL in C++ that is callable from a Metro style app built for Windows using JavaScript. There are several reasons for building such a component, for example:

  • To take advantage of the performance of C++ in complex or computationally-intensive operations.

  • To access Windows operating system services that are not accessible through the Windows Runtime in the current version.

  • To reuse existing code that is already written and tested.

When you build a solution that contains a JavaScript project and a Windows Runtime Component DLL project, the JavaScript project files and the compiled DLL are merged into one package, which you can then deploy locally or remotely for testing or submit to the Windows Store. You can also distribute just the component project as an Extension SDK. For more information, see Tutorial: Creating and using extension SDKs.

You can also use a C++ Windows Runtime Component DLL in a Metro style app written in C++, C#, or Visual Basic.

In general, when you code your C++ component, use the regular C++ library and built-in types except at the abstract binary interface (ABI) boundary where you are passing data to and from JavaScript. There, use Windows Runtime types and the special syntax that Visual C++ supports for creating and manipulating those types. In addition, your Visual C++ code will use types such as delegate and event to implement events that can be fired from your component and handled in JavaScript. For more information about the new Visual C++ syntax, see Visual C++ Windows Runtime Programming Guide.

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

Create a Windows Runtime Component DLL project

Choose Visual C++ > C++ WinRT Component DLL project. Type “mycomp” for the project name.

Notice that the main class of the component contains examples of a property and method definition, and an event declaration. These are provided as examples just to show you how it is done. They are not needed, and in this example we will replace all of the generated code with our own code.

For the sake of simplicity, this example has only one class, and all its functionality is implemented in the header file. The functions are not very useful, except to demonstrate how to pass various types of data across the ABI from C++ to JavaScript.

First, remove the .cpp file by right-clicking it and selecting “Exclude from Project.” We won’t use it in this example.

Add an “include WinRTComponent.h” directive to pch.h.

Add types and methods to the component

Replace all of the existing code in WinRTComponent.h with this code:


#pragma once

using namespace Platform;
namespace WFC = Windows::Foundation::Collections;

#include <collection.h>
#include <algorithm>
#include <cmath>

namespace mycomp
{
        // delegates
    public delegate void PropertyChangedHandler(Object^ source, int i);
    public delegate void SomeHandler(Platform::String^ str);

    // Activatable class.
    public ref class LangSample sealed
    {
        // Backing store for propertyA.
        int _propertyAValue;

    public:
        LangSample()
        {
            _propertyAValue = 21;

        }

        // IVector across the ABI.
        WFC::IVector<int>^ SortVector(WFC::IVector<int>^ vec)
        {
            std::sort(begin(vec), end(vec));
            return vec;
        }

        // IMap
        WFC::IMap<int, Platform::String^> ^GetMap(void)
        {
            WFC::IMap<int, Platform::String^> ^ret = ref new Platform::Map<int, Platform::String^>;            
            ret->Insert(1, "One ");
            ret->Insert(2, "Two ");
            ret->Insert(3, "Three ");
            ret->Insert(4, "Four ");
            ret->Insert(5, "Five ");

            return ret;
        }

        // Property with custom setter/getter
        property int PropertyA
        {
            int get() { return _propertyAValue; }
            void set(int propertyAValue) 
            {
                if (propertyAValue != _propertyAValue)
                {
                    _propertyAValue = propertyAValue;
                    propertyChangedEvent(this, propertyAValue);
                }
            }
        }

        // Trivial get/set property with compiler-generated backing store.
        property Platform::String^ PropertyB;

        // Public method that returns a value of built-in type.
        // Implicit conversion of return value to equivalent WinRT type.
        double LogCalc(double input)
        {       
            
            // Use C++ standard library as usual.
            return std::log(input); 
        }

        // Method that fires an event
        void FireEvent(Platform::String^ str)
        {
            someEvent(Platform::String::Concat(str, PropertyA.ToString()));
        }

        // Method that throws an exception.
        void ExceptionMethod()
        {    
            throw ref new Platform::DisconnectedException();
        }

        // Events. See delegate types above.
        event PropertyChangedHandler^ propertyChangedEvent;
        event SomeHandler^ someEvent;

    };
}
// WinRTComponent.h


Namespaces

Notice that in this example the component namespace is the same as the Visual Studio project name: “mycomp”. This is a limitation in the current version of Windows Developer Preview.

The Platform namespace is where C++ defines its classes that are specific Windows Runtime types, including all the numeric primitives and also types such as Platform::String and Platform::Object.

#include files

  1. windows.h is not included. It is only needed when your component references Win32 types directly.

  2. collection.h defines a set of Windows Runtime helper types that simplify conversion between STL collections and Windows Runtime collections. For more information, see Collections (Visual C++ for Windows Runtime)

  3. <algorithm> and <cmath> are included. A C++ Windows Runtime Component DLL uses the available C++0x features, the STL, and other C++ libraries just as with classic Windows programming.

C++ built-in types, library types, and Windows Runtime types

An activatable class is one that can be instantiated from another language such as JavaScript. To be consumable from another language such as JavaScript, a component must contain at least one activatable class. For more information, see Visual C++ for Windows Runtime Programming Guide and Ref classes (Visual C++ for Windows Runtime).

In this example, LangSample is the activatable class. A Windows Runtime component can contain multiple activatable classes as well as additional classes known only internally to the component. Client code creates an instance of the component by using the new keyword (New in Visual Basic) just as for any class.

An activatable class must be declared as public ref class sealed. The ref class keywords tell the compiler to create the class as a Windows Runtime compatible type, and the sealed keyword specifies that the class cannot be inherited. A class must be sealed to be consumed by JavaScript.

The ^ operator signifies a handle to a Windows Runtime type that under the covers is reference-counted and deleted when the count reaches zero. Instances of these types are created by using the ref new keywords. Members are accessed by using the -> operator, as shown in the GetMap() method in the example. Do not explicitly call delete on these instances.

Types must be passed to and from the public methods as Windows Runtime types. If you use the C++ built-in types such as int, double and so on, the compiler will automatically convert them to the appropriate Windows Runtime type. No such conversion occurs unless you are passing the type across the ABI.

Complex types such as Platform::String^ must be specified explicitly.

Collections and Arrays

Collections are always passed across the ABI boundary as handles to Windows Runtime types, such as IVector^ and IMap^. If you return a handle to a Platform::Map, for example, it will implicitly convert to an IMap^.

Properties

Expose public data members as properties, by using the property keyword. A trivial property looks basically like a data member because all of its functionality is implicit. A non-trivial property has explicit get and set accessors and a named private variable that is the “backing store” for the value. In this example, the private member variable _propertyAValue is the backing store for PropertyA. A property can fire an event when its value changes, and a JavaScript client can register to receive that event.

Delegates and events

A delegate is a Windows Runtime type that represents a function object. The declaration of a delegate resembles a function signature, the implementation resembles a class definition, and the invocation resembles a method invocation. A delegate instance can also be created implicitly or “inline” by using a lambda expression.

The event keyword is used to declare a public member of a specified delegate type. Client code subscribes to the event by using the standard mechanisms provided in the particular language.

Throwing exceptions from the component

You can throw any exception type defined by the Windows Runtime. You cannot derive custom types from any Windows Runtime exception type. However, you can throw COMException and provide a custom HRESULT that can be accessed by the code that catches the exception.

This example uses the basic empty JavaScript Application template, although any template works just as well.

To create a project in this solution, right-click the solution node in Solution Explorer. and select Add Project > Empty JavaScript Application.

After you create the JavaScript project, the three basic steps are:

  1. Add a reference to the component project.

  2. Add the HTML markup that invokes JavaScript.

  3. Add the JavaScript code that activates and invokes your component.

Add a reference to the component project

After you have compiled the C++ project for the first time, you can add it as a project reference in the JavaScript project. Right-click the References node in the JavaScript project, and select Add. When the References Manager dialog box appears, click “Solution” to display the available references in the solution. “mycomp” is the only one available in this solution; select it. The namespace and all public types and methods are now available to your JavaScript code. You can verify this by experimenting with the Member List or Statement Completion feature in the JavaScript file.

Add the HTML that invokes the JavaScript event handlers

Paste the following HTML into the <body> node of the default.html page.

<button id="callwinrt" onclick="CallWinRT()">Call WinRT</button>
<p></p>
    <label id ="Label1">Activation Result::  </label><label id ="loaded"></label>
    <p></p>
    <label id ="Label2">Input Vector::  </label><label id ="inputvector"></label>
    <p></p>
    <label id ="Label3">Sorted Vector::  </label><label id ="outputvector"></label>
    <p></p>
    <label id ="Label9">Map output::  </label><label id ="mapoutput"></label>
    <p></p>
    <label id ="Label4">Retrieved property::  </label><label id ="retrievedproperty"></label>
    <p></p>
     <label id ="Label10">Set property::  </label><label id ="setproperty"></label>
    <p></p>
     <label id ="Label11">Call method::  </label><label id ="callmethod"></label>
    <p></p>
    <label id ="Label5">Single Cast Handler called::  </label><label id ="singlecastresult"></label>
    <p></p>
    <label id ="Label6">Multi Cast Handler 1 called::  </label><label id ="multicastresult1"></label>
    <p></p>
    <label id ="Label7">Multi Cast Handler 2 called::  </label><label id ="multicastresult2"></label>
    <p></p>
    <label id ="Label8">Handled Exception::  </label><label id ="handledexception"></label>
    <p></p>

Add the JavaScript event handlers that call into the component DLL

Add the following function at the end of the default.js file. This function is called when you click the button on the main page. Notice how JavaScript activates the C++ class, and then calls its methods and populates the HTML labels with the return values.

function CallWinRT() {
    // activate the native Windows Runtime component
    var nativeObject = new mycomp.LangSample();
    document.getElementById('loaded').innerHTML = nativeObject;

    // call the method to sort an integer array
    var inVector = [14, 12, 45, 89, 23];
    document.getElementById('inputvector').innerHTML = inVector;

    var outVector = nativeObject.sortVector(inVector);
    document.getElementById('outputvector').innerHTML = outVector;

    // call the method to get the map
    var outputMap = nativeObject.getMap();
    var mStr = outputMap.lookup(1) + outputMap.lookup(2) + outputMap.lookup(3) + outputMap.lookup(4) + outputMap.lookup(5);
    document.getElementById('mapoutput').innerHTML = mStr;

    // get the value of the integer property
    var propValue = nativeObject.propertyA;
    document.getElementById('retrievedproperty').innerHTML = propValue;

    // set a string property
    nativeObject.propertyB = "What is the meaning of the universe?";
    document.getElementById('setproperty').innerHTML = nativeObject.propertyB;

    //call a method
    var num = nativeObject.logCalc(21.5);
    document.getElementById('callmethod').innerHTML = num;

    // add an event handler
    var singlecasthandler = function (ev) {
        document.getElementById('singlecastresult').innerHTML = ev;
    };

    nativeObject.onpropertychangedevent = singlecasthandler;

    // set the value of the integer property.  this should fire an event
    nativeObject.propertyA = 2 * propValue;

    // add multi-cast handlers
    var multicast1 = function (ev) {
        document.getElementById('multicastresult1').innerHTML = ev.target;
    };
    var multicast2 = function (ev) {
        document.getElementById('multicastresult2').innerHTML = ev.target;
    };

    nativeObject.addEventListener("someevent", multicast1);
    nativeObject.addEventListener("someevent", multicast2);

    // this method should fire an event
    nativeObject.fireEvent("The answer is ");

    // call the method that throws an exception
    try {
        nativeObject.exceptionMethod();
    }
    catch (err) {
        document.getElementById('handledexception').innerHTML = err;
    }
}


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, right-click the JavaScript project node in Solution Explorer, then select Properties > Debugging > Debugger Type.

Be sure to select appropriate capabilities in the package designer. For example, if you are attempting to open a file using the Windows Runtime APIs, be sure to select the Document Library Access checkbox in the Capabilities pane of the package designer.

If your JavaScript code does not seem to be recognizing the public properties or methods in the component, make sure that in JavaScript you are using camel-casing. For example, the LogCalc C++ method must be referenced as logCalc 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 will prevent subsequent debug or build operations. If necessary, you can then add an assembly reference to the DLL.

Did you find this helpful?
(1500 characters remaining)