[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]
This article shows how to use C++ to create a Windows Runtime Component, which is a DLL that's callable from a Windows Metro style app that's built by using JavaScrip (or C#, Visual Basic or C++). Here are several reasons for building such a component:
-
To get the performance advantage of C++ in complex or computationally-intensive operations.
-
To reuse existing code that's already written and tested.
When you build a solution that contains a JavaScript project and a Windows Runtime Component project, the JavaScript project files and the compiled DLL are merged into one package, which you can debug locally, in the simulator, or remotely on a tethered device. You can also distribute just the component project as an Extension SDK.
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++ Language Reference (C++/CX).
JavaScript is case sensitive. Therefore, you must follow these casing conventions:
-
When you reference C++ namespaces and classes, use the same casing that's used on the C++ side.
-
When you call methods, use camel casing even if the method name is capitalized on the C++ side. For example, a C++ method GetDate() must be called from JavaScript as getDate().
-
An activatable class name and namespace name can't contain UNICODE characters.
Only Windows Runtime types can be passed across the ABI boundary. The compiler will raise an error if the component has a type such as std::wstring as a return type or parameter in a public method. If you use the C++ built-in types such as int, double, and so on, the compiler automatically converts them to the appropriate Windows Runtime type—int32, float64, and so on—in parameters and return types of public methods. No such conversion occurs unless you are passing the type across the ABI.For more information, see Visual C++ Language Reference (C++/CX).
C++
namespace mycomp { //ref class definition in C++ namespace mycomp { public ref class LangSample sealed { //…class members } } }
JavaScript
//Instantiation in JavaScript (requires "add reference > Project reference") var nativeObject = new mycomp.LangSample();
An activatable class (also known as a ref 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.
A Windows Runtime component can contain multiple activatable classes as well as additional classes that are known only internally to the component. Apply the WebHostHidden[WebHostHidden] attribute to C++ types that are not intended to be visible to JavaScript.
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.
All the numeric primitives are defined in the default namespace. The Platform Namespace is where C++ defines classes that are specific Windows Runtime types. These include Platform::String Class and Platform::Object Class. The concrete collection types such as Platform::Collections::Map Class and Platform::Collections::Vector Class are defined in the Platform::Collections Namespace. The public interfaces that these types implement are defined in Windows::Foundation::Collections Namespace (C++/CX). For more information, see Type System (C++/CX).
Method that returns a value of built-in type
C++
// Implicit conversion of return value to equivalent Windows Runtime type. double LogCalc(double input) { // Use C++ standard library as usual. return std::log(input); }
JavaScript
//Call a method var num = nativeObject.logCalc(21.5); document.getElementById('callmethod').innerHTML = num;
Method that returns a custom value struct
To pass user-defined structs across the ABI, define a JavaScript object that has the same members as the value struct that's defined in C++. You can then pass that object as an argument to a C++ method so that the object is implicitly converted to the C++ type. Another approach is to define a class that implements IPropertySet (not shown).
C++
// Custom struct public value struct Batter { String^ Name; int Number; double BattingAverage; }; Public ref class LangSample { private: MyData batter_; public: property Batter { Batter get(){ return batter_; } } }
JavaScript
var myData = nativeObject.batter; document.getElementById('myDataResult').innerHTML = myData.name + " , " + myData.number + " , " + myData.battingAverage.toPrecision(3);
Overloaded methods
JavaScript has limited ability to differentiate overloaded methods. For example, it can tell the difference between these signatures:
int GetNumber(int i); int GetNumber(int i, string str); double GetNumber(int i, MyData^ d);
But it can’t tell the difference between these:
int GetNumber(int i); double GetNumber(double d);
or these:
MyData^ GetData(MyData^ d1, MyData^ d2); MyData^ GetData(MyData^ d1, TestData^ d2);
In cases of ambiguity, you can ensure that JavaScript always calls a specific overload by applying the Windows::Metadata::DefaultOverload attribute to the method signature in the header file.
//C++ header file: [Windows::Foundation::Metadata::DefaultOverloadAttribute] int WinRTComponent::GetNumber(int i); double WinRTComponent::GetNumber(double d);
This Javascript always calls the attributed overload:
var num = nativeObject.getNumber(9);
To enable JavaScript to access multiple overloads that it would otherwise not be able to tell apart, you can apply the Windows::Metadata::Overload attribute to each overloaded method signature in the C++ header file. The Overload attribute takes a string parameter that specifies an alternate unique name for an overload. This enables JavaScript to call a specific overload by that name (after converting to camelCase of course):
var nativeObject = new CppLib.WinRTComponent(); var num = nativeObject.getNumber.getInt(9); var num2 = nativeObject.getFloat(9.0);
When you pass a DateTime value from C++ to JavaScript, JavaScript accepts it as a Date object and displays it by default as a long for date string.
C++
property Windows::Foundation::DateTime TimeStamp; … auto cal = ref new Windows::Globalization::Calendar(); cal->Now(); TimeStamp = cal->ToDateTime();
JavaScript
var myDate = nativeObject.timeStamp; //prints long form date string document.getElementById('dateString').innerHTML = myDate;
Collections are always passed across the ABI boundary as handles to Windows Runtime types, such as Windows::Foundation::Collections::IVector^ and Windows::Foundation::Collections::IMap^. For example, if you return a handle to a Platform::Collections::Map, it will implicitly convert to a Windows::Foundation::Collections::IMap^. The collection interfaces are defined in a separate namespace from the C++ classes that provide the concrete implementations.
Passing IVector to a Javascript array
C++
C++ // Windows::Foundation::Collections::IVector across the ABI. IVector<int>^ SortVector(IVector<int>^ vec) { std::sort(begin(vec), end(vec)); return vec; }
JavaScript
// Call the method to sort an integer array var inVector = [14, 12, 45, 89, 23]; var outVector = nativeObject.sortVector(inVector);
Passing an IMap object
C++
C++ // Windows::Foundation::Collections::IMap IMap<int, Platform::String^> ^GetMap(void) { IMap<int, String^> ^ret = ref new Map<int, String^>; ret->Insert(1, "One "); ret->Insert(2, "Two "); ret->Insert(3, "Three "); ret->Insert(4, "Four "); ret->Insert(5, "Five "); return ret; }
JavaScript
// 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;
Expose public data members as properties, by using the property keyword. A trivial property resembles 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's 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.
C++
Public ref class LangSample { //… // Backing store for propertyA. int _propertyAValue; // Property that has custom setter/getter property int PropertyA { int get() { return _propertyAValue; } void set(int propertyAValue) { if (propertyAValue != _propertyAValue) { _propertyAValue = propertyAValue; //fire event. (See event example below.) propertyChangedEvent(this, propertyAValue); } } } // Trivial get/set property that has a compiler-generated backing store. property Platform::String^ PropertyB; }
JavaScript
// 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;
A delegate is a Windows Runtime type that represents a function object. You can use delegates in connection with events, callbacks, and asynchronous method calls to specify an action to be performed later. Like a function object, the delegate provides type-safety by enabling the compiler to verify the return type and parameter types of the function. The declaration of a delegate resembles a function signature, the implementation resembles a class definition, and the invocation resembles a function invocation. A delegate instance can also be created "inline" by using a lambda expression.
Adding an event listener
You can use the event keyword to declare a public member of a specified delegate type. Client code subscribes to the event by using the standard mechanisms that are provided in the particular language.
From JavaScript, the C++ component has an addEventListener method that enables JavaScript to hook up an event handler for an event that the component publishes.
C++
C++ namespace mycomp { public delegate void PropertyChangedHandler(Object^ source, int i); public ref class sealed LangSample { event PropertyChangedHandler^ propertyChangedEvent; // Property that has custom setter/getter property int PropertyA { int get() { return _propertyAValue; } void set(int propertyAValue) { if (propertyAValue != _propertyAValue) { _propertyAValue = propertyAValue; propertyChangedEvent(this, propertyAValue); } } } }; }
JavaScript
// Define an event handler method var singlecasthandler = function (ev) { document.getElementById('singlecastresult').innerHTML = ev; }; // Subscribe to the event nativeObject.onpropertychangedevent = singlecasthandler; // Set the value of the property and fire the event nativeObject.propertyA = 2 * propValue;
Adding multiple event listeners for one event
C++
namespace mycomp { public delegate void SomeHandler(Platform::String^ str); public ref class sealed LangSample { event SomeHandler^ someEvent; // Method that fires an event void FireEvent(Platform::String^ str) { someEvent(Platform::String::Concat(str, PropertyA.ToString())); } }; }
JavaScript
// Add two event handlers var multicast1 = function (ev) { document.getElementById('multicastresult1').innerHTML = ev.target; }; var multicast2 = function (ev) { document.getElementById('multicastresult2').innerHTML = ev.target; }; //Subscribe to the same event nativeObject.addEventListener("someevent", multicast1); nativeObject.addEventListener("someevent", multicast2); // This method should fire an event nativeObject.fireEvent("The answer is ");
Enum values are passed between C++ and JavaScript as integers. You can optionally declare a JavaScript object that contains the same named values as the C++ enum and use it as follows.
C++
public enum class Direction {North, South, East, West}; …. Direction m_direction; … property Direction CurrentDirection { Direction get(){return m_direction; } }
JavaScript
var Direction = { 0: "North", 1: "South", 2: "East", 3: "West" }; … var curDirection = nativeObject.currentDirection; document.getElementById('curDirection').innerHTML = Direction[curDirection];
To consume asynchronous methods exposed by other Windows Runtime objects, use the task Class (Concurrency Runtime) For more information, see Asychronous Programming in C++ and Task Parallelism (Concurrency Runtime).
To implement asynchronous methods in C++ is to use the create_async Function that's defined in ppltasks.h. For more information, see Creating Asynchronous Operations in C++ for Metro style Apps. For an example, see Walkthrough: Creating a basic Windows Runtime component in C++ and calling it from JavaScript
You can throw any exception type that's 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. There's no way to specify a custom Message in a COMException.
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, select the JavaScript project node in Solution Explorer and then choose Properties, Debugging, Debugger Type.
Be sure to select appropriate capabilities in the package designer. For example, if you are attempting to open a file by 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 doesn't 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.
