JavaScript Application Calling WinRT Component DLL
In this part, we will create a C++ WinRT Component DLL and access it from a JavaScript application.
We have developed a C++ WinRT Component DLL & C#.NET application in the following post:
We have seen the compiler generated components for making the C# application access the C++ WinRT component in the following post:
We have seen the packaging and installation process that happens in the background during building and deploying of the applications in the below post:
We have seen the C++ XAML application calling the C++ WinRT Component DLL at:
Generally, in a LOB application, we might have to build a C++ Component DLL to take advantage of the performance of C++ in complex or computationally-intensive operations. The C++ component can access Windows operating system services that are not accessible through the Windows Runtime in the current version. Mostly, to reuse existing code that is already written and tested.
Step 1
Create a Windows Runtime C++ Component DLL
Open Visual Studio 2011 –> File –> New Project –> Go to Installed Templates section –> Visual C++ –>Select WinRT Component DLL and name it as CPPWinRTComponentDll as shown in the following figure.
Fig 1: CPPWinRTComponentDll project
Open WinRTComponent.h file and create an Employee
class as shown in the following code snippet. The Platform
namespace is where C++ defines its classes that are specific Windows Runtime types.
Create a private
variables address_
and employeeid_
. These are used as backing store to hold the values. We will use get()
& set()
properties to set or get these values.
private:
Platform::String^ address_;
int employeeid_;
In the public
section, add the following code which are properties for the above backing store.
property Platform::String^ Address
{
Platform::String^ get()
{
return (address_);
}
}
property int EmployeeId
{
int get()
{
return employeeid_;
}
void set(int value)
{
if(value <= 0)
{
throw ref new Platform::InvalidArgumentException();
employeeid_ = value;
}
}
}
We can also add some trivial get
/set
property with the compiler generating the backing store.
public:
property Platform::String^ Name;
Following is the complete code of the Employee
class.
public ref class Employee sealed
{
private:
Platform::String^ address_;
int employeeid_;
public:
property Platform::String^ Name;
property Platform::String^ Address
{
Platform::String^ get()
{
return (address_);
}
}
property int EmployeeId
{
int get()
{
return employeeid_;
}
void set(int value)
{
if(value <= 0)
{
throw ref new Platform::InvalidArgumentException();
employeeid_ = value;
}
}
}
public:
Platform::String^ SayHello()
{
return "Hello World";
}
public:
int Add(int x, int y)
{
return x+y;
}
};
When you code your C++ component, if needed, you can use the regular C++ library and built-in types inside the class code 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.
You have to make the Employee
class as activatable class so that it 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. If needed, 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 just as for any class.
The Employee
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.
In the Platform::String^ address_
, the ^
operator signifies a handle to a Windows Runtime string
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. 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.
Step 2
Creating a JavaScript Windows Metro Style Application Project
To create a project in this solution, right-click the solution node in Solution Explorer. and select Add Project > Blank Application. Name it as WinWebApp1
.
Fig 2: Creating the JavaScript project
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 Add References Manager dialog box appears, click “Solution” to display the available references in the solution. Select “CPPWinRTComponentDll
” in this solution and click on Add button as shown in figure 3; 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.
Fig 3: Adding reference to the C++ WinRT Component DLL.
Add the HTML markup that invokes JavaScript.
Add the following HTML into the <body>
node of the default.html page
<body>
<button id="callwinrt"
onclick="CallWinRT()">Call WinRT</button>
<p></p>
<label id ="Label1"
style="background-color: grey;">Activation Object Result ->
</label><label id ="loaded"
style="background-color: #51B65A;"></label>
<p></p>
<label id ="Label2"
style="background-color: grey;">Calling Employee::SayHello() method
</label><label id ="callmethod"
style="background-color: #51B65A;"></label>
<p></p>
<label id ="Label3"
style="background-color: grey;" >Getting Employee.Name Property value
</label><label id ="retrievedproperty"
style="background-color: #51B65A;"></label>
<p></p>
</body>
The default.html page appears as follows:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>WinWebApp1</title>
<!– WinJS references –>
<link rel="stylesheet" href="/winjs/css/ui-dark.css" />
<script src="/winjs/js/base.js"></script>
<script src="/winjs/js/wwaapp.js"></script>
<!– WinWebApp1 references –>
<link rel="stylesheet" href="/css/default.css" />
<script src="/js/default.js"></script>
</head>
<body>
<button id="callwinrt"
onclick="CallWinRT()">Call WinRT</button>
<p></p>
<label id ="Label1"
style="background-color: grey;">Activation Object Result ->
</label><label id ="loaded"
style="background-color: #51B65A;"></label>
<p></p>
<label id ="Label2"
style="background-color: grey;">Calling Employee::SayHello() method
</label><label id ="callmethod"
style="background-color: #51B65A;"></label>
<p></p>
<label id ="Label3"
style="background-color: grey;" >Getting Employee.Name Property value
</label><label id ="retrievedproperty"
style="background-color: #51B65A;"></label>
<p></p>
</body>
</html>
Add the JavaScript event handlers that call into the component DLL.
Add the following function CallWinRT()
at the end of the default.js file. This function is called when you click the button “Call WinRT” 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() {
var nativeObject = new CppWinRTComponentDll.Employee();
document.getElementById(‘loaded’).innerHTML = nativeObject;
var num = nativeObject.sayHello();
document.getElementById(‘callmethod’).innerHTML = num;
nativeObject.name = "Kishore Babu";
var propValue = nativeObject.name;
document.getElementById(‘retrievedproperty’).innerHTML = propValue;
}
The complete code of default.js file is as follows:
(function () {
‘use strict’;
WinJS.Application.onmainwindowactivated = function (e) {
if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
}
}
WinJS.Application.start();
})();
function CallWinRT() {
var nativeObject = new CppWinRTComponentDll.Employee();
document.getElementById(‘loaded’).innerHTML = nativeObject;
var num = nativeObject.sayHello();
document.getElementById(‘callmethod’).innerHTML = num;
nativeObject.name = "Kishore Babu";
var propValue = nativeObject.name;
document.getElementById(‘retrievedproperty’).innerHTML = propValue;
}
Build the solution & deploy the application. If you go to start window, you will find an application WinWebApp1
. Click on it.
Click on call WinRT button. The values from the C++ DLL are returned and set in the JavaScript application as shown in the following figure:
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.
Debugging: 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.
Download the source code here.
As a C++ developer, I find this useful. If you are a C++ developer and new to JavaScript, you sometimes make the mistake of not using the camel-casing in JS. 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 Platform::String^ SayHello() C++ method must be referenced as sayHello() in JavaScript.
"The task of the leader is to get his people from where they are to where they have not been." — Henry Kissinger
Kishore Babu Gaddam is a Senior Technology Consultant, Technology Evangelist turned Technology Entrepreneur and a regular speaker at national conferences, regional code camps and local user groups with over 14 years of experience in software product development. His experience includes building & managing award-winning software development teams, managing customer relationships, marketing and launching new software products & services. Kishore launched his technology career almost 15 years ago with a Robotics software development startup and has served in multiple roles since including developer, innovation leader, consultant, technology executive and business owner.
A technology specialist in C++, C#, XAML and Azure, he successfully published two applications to Windows store http://bit.ly/WinStoreApp and http://bit.ly/FlagsApp.
Kishore is the author of the popular Microsoft Technologies blog at http://www.kishore1021.wordpress.com/ and his work on Portable Class Library project in Visual Studio 2012– .NET 4.5 was featured on Channel 9 at http://bit.ly/msdnchannel9. Kishore enjoys helping people understand technical concepts that may initially seem complex and perform lot of Research & Development on emerging technologies to help solve some of the toughest customer issues. Kishore spends a lot of time teaching and mentoring developers to learn new technologies and to be better developers. He is a speaker at various code camps around Washington DC area, mainly at Microsoft Technology Center for NOVA code camp (http://bit.ly/novacc12), CMAP Code Camp Fall 2012 (http://bit.ly/novacc12), etc. The majority of his software development experience has centered on Microsoft technologies including MFC, COM, COM+, WCF, WPF, winRT, HTML5, RestAPI and SQL Server. You can follow Kishore on Twitter at www.twitter.com/kishore1021. He can be reached on email at researcherkishore@outlook.com