用C++创建windows 运行时组件

翻译自MSDN文档:

This article shows how to use C++ to create a Windows Runtime Component DLL that's callable from a Windows Metro style app that's built by using JavaScript. Here are several reasons for building such a component:

这文章展示了如何使用C++创建一个使用JavaScript 创建的 Windows Metro风格程序可调用的windows Runtime Component Dll,下面是建立一个这样组件的原因:

1.To get the performance advantage of C++ in complex or computationally-intensive operations.

   为了利用C++中复杂或集中计算的高效能优势。

2.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 DLL 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.

当你建立一个包含一个javascript 工程和一个windows运行时组件Dll工程的解决方案,javascript的项目文件和编译的dll合并成一个可以在本地调试,模拟器调试或者远程设备上的包。你还可以把组件项目当作扩展SDK发布。

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

你还可以在用C++,C#,VB写的Metro风格的应用程序中使用C++编写的Windows 运行时组件DLL。

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++组件时,你可以使用常规C++库和内置类型,除了你从JavaScript传递数据或者接收数据的ABI边界外。在那里,使用Windows的运行时类型和特殊的语法,VISUAL C + +用于创建和操纵这些类型的支持。另外,你的VC++代码使用类似于delegate和event去实现从您的组件发出event和在JavaScript处理事件。更多关于新的Visual C++语法,请参考Visual C++ language reference

Casing and naming rules

JavaScript is case sensitive. Therefore, you must follow these casing conventions:

  1. When you reference C++ namespaces and classes, use the same casing that's used on the C++ side.

  2. When you call methods, use camel casing even if the method name is capitalized on the C++ side. For example, a C++ methodGetDate() must be called from JavaScript as getDate().

  3. An activatable class name and namespace name can't contain UNICODE characters.

JavaScript是大小写敏感的。因此,你必须遵循这些大小写规范:

  1.当你引用C++的命名空间和类时,使用与c++部分相同的大小写规范。

  2.当你调用方法时,使用camel 大小写转化即使方法名在c++部分使用。如C++方法名GetDate()在JavaScript中必须调用getDate().

  3.激活的类名和命名空间不能包含UNICODE字符

Only Windows Runtime types can be passed across the ABI boundary. The compiler will raise an error if the component has a type such asstd::wstring as a return type or parameter in a public method. If you use the C++ built-in types such as intdouble, and so on, the compiler automatically converts them to the appropriate Windows Runtime type—int32float64, 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.

只有windows运行时类型才能被传递通过ABI边界。如果组件中有类似于std::wstring 作为返回值或者在公共方法中有这样的参数,编译器将会报错。如果你在公共方法的返回值或者参数中使用C++内置类型,如int,double等,编译器将会自动将他们转化成响应的windows运行时类型int32,float64等。没有发生类似的转换发生除非你传递类型i通过ABI。更多信息,查看Visual C++ language reference.

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();

C++ built-in types, library types, and Windows Runtime types(C++内置类型,库类型,windows运行时类型)

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. An activatable class must inherit directly from Platform::Object. For more information, see Type System (C++/CX).

一个激活类(又称为ref类)是能被另外的类似于JavaScript的语言初始化的类。为了能被另外的如JavaScript语言消费(调用?),一个组件必须至少包含一个激活类。一个激活的类必须继承直接从平台::对象有关详细信息Type System (C++/CX).

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.

在Windows运行时组件可以包含多个激活类,和被称为内部组建的附件类一样。应用WebHostHidden [ WebHostHidden ]属性的C + +的类型在JavaScript中是不可见的。

Client code creates an instance of the component by using the new keyword—New in Visual Basic—just as for any class.

客户端代码通过使用关键字new来创建组件的一个实例----像任意其他类一样。

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.

激活累必须被声明为public ref class sealed。ref关键字告诉编译器把这个类作为windows 运行时兼容类型,sealed关键字指定类不能呗继承。由Javasript类调用的类必须被声明为sealed。

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 Map Class (C++/Cx) and Vector Class (C++/Cx) are defined in the Platform::Collections Namespace. The public interfaces that these types implement are defined in Windows::Foundation::Collections Namespace (C++/CX).

所有的数字单元在default命名空间中定义。Platform 命名空间C++指定的windows 运行时类型。这些包含Platform::StringPlatform::Object 。具体的集合类如Map Class (C++/Cx) and Vector Class (C++/Cx)定义在Platform::Collections命名空间。这些类型的公共接口的实现定义在Windows::Foundation::Collections命名空间。

方法返回一个内置类型的值

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;

方法返回一个自定义值结构

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);

重载方法

JavaScript has limited ability to differentiate overloaded methods. For example, it can tell the difference between these signatures:

JavaScript有有限的能力去区分重载的方法。例如,它能区分下面的区别

C++

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:

但是不能区分下面两者的区别:

C++

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.

在模棱两可的情况下,可以通过在头文件的函数签名中应用 Windows::Metadata::DefaultOverload属性,来保证的JavaScript调用特定重载。

//C++ header file:
[Windows::Foundation::Metadata::DefaultOverloadAttribute]
int WinRTComponent::GetNumber(int i);

double WinRTComponent::GetNumber(double d);

This Javascript always calls the attributed overload:

这个Javascript一直调用了属性重载:

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):

为确保JavaScript访问多个重载,你可以在C++头文件中使用Windows::Metadata::Overload属性到每个重载方法标记,否则javascript不能区分他们。重载属性需要一个字符串参数指定备用重载的唯一名称。这可以使JavaScript通过指定的名字调用重载方法。

[Windows::Foundation::Metadata::Overload("GetInt")]
int WinRTComponent::GetNumber(int i);

[Windows::Foundation::Metadata::Overload("GetFloat")]
double WinRTComponent::GetNumber(double d);

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.

当你将一个DataTime从C++传递到JavaScript中时,JavaScript把它作为一个Data Object对象接受并且把他作为一个默认的long的data字符串。

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 asWindows::Foundation::Collections::IVector^ and Windows::Foundation::Collections::IMap^. For example, if you return a handle to aPlatform::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.

集合总是作为句柄跨ABI的边界传递给Windows运行时类型,如Windows::Foundation::Collections::IVector^ 和 Windows::Foundation::Collections::IMap^。例如,如果你返回一个Platform::Collections::Map的句柄,它将会呗隱式的转化为Windows::Foundation::Collections::IMap^。集合接口被定义在一个从C + +的类提供了具体的实现的单独的命名空间。

 

Passing IVector to Javascript array 传递IVector 到 JavaScript数组

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 传递IMap对象

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;

Properties(属性)

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.

通过使用property 关键字来暴露公共数据成员作为属性。一平凡的属性类似于一个数据成员,因为它的功能是隐含的。一个不平凡的属性有明确的get和set访问器和一个名为私有变量,这是“后备存储”的价值。在这个例子中,私有成员变量_propertyAValue就是一个PropertyA的后备存储。当一个属性的值改变的时候,它能fire一个event,并且JavaScript客户端能注册并且接受这个事件。

 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;

Delegates and events(委托和事件)

 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.

委托代表了一个函数对象的windows运行时类型。你可以在连接event,回调,异步方法。像一个函数对象,委托提供类型安全,以使编译器来验证函数的返回类型和参数类型。委托声明类似于一个函数的签名,实现类似与类的定义,调用类似于函数的调用。委托实例也能用lambda表达式创建内联。

添加一个事件监听器

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.

你能使用event关键字声明一个指定委托类型的公共成员。客户端代码使用在特定的语言提供的标准机制来订阅事件。

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.

从JavaScript中,C++组件有addEventListener可以使JavaScript能够hook一个组件发布的event句柄。

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;

一个事件添加多个事件监听器

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 ");

Enums枚举

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++和JavaScript间传递。你可以选择声明一个JavaScript对象,它包含的C + +枚举和使用如下命名为相同的值。

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, seeCreating 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 inSolution Explorer and then choose PropertiesDebuggingDebugger 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.

 

 

 

转载于:https://www.cnblogs.com/deagle/articles/2555296.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值