Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

25 Dec 2019

WinRT for JavaScript学习笔记

Windows Runtime (WinRT) for JavaScript学习笔记。

从wwahost.exe加载的WebApp可以访问WinRT的API。从Edge Webview不仅可以加载远程的Web,也可以加载本地的内容,并且从MSApp访问流(stream)块(blob)接口以及用于JS对象和WinRT对象之间转化的接口。

文档中列举了一些JS可以访问的WinRT的API接口的根级命名空间。

Using the Windows Runtime in JavaScript

JS不仅可以访问Windows中自带的WinRT接口,也可以访问自定义WinRT组件的接口。有一些命名上的差比需要识别:

  • 命名空间在JS中以大写开头,比如:Windows.Deployment.PackageInfo;
  • 类成员,包括方法与部属,或者结构以及枚举的成员,都是驼峰(camel case)格式命名:Deployment.PackageInfo.createPackage();
  • 事件采用全小写命名:dataTransferManager.ontargetapplicationchosen;

Considerations when Using the Windows Runtime API

WinRT类型在JS中的表示需要注意的地方:

  • 字符串:未初始化的字符串传给WinRT的时候,其值是“undefined”;null字符串传递给WinRT的时候,其值为“null”。为了避免意外,将字符串传递给WinRT的时候,最好将其初始化为”"。
  • 接口:无法用JS实现一个WinRT的接口类型
  • 数组:WinRT的数组无法改变大小,所以JS的数组大小相关的操作不能用于WinRT数组
  • 数组:如果传递一个JS的数组给WinRT,这个数组会被拷贝。WinRT无法修改数组内容之后返还此数组。但是你可以使用类型数组,比如Int32Array Object这个是非拷贝的。
  • 结构:WinRT的结构类型,在JS也是以对象的形式表现的。如果要传递要给WinRT的结构给WinRT的方法,不要在JS中使用new来初始化这个结构。反之,直接在对象上添加所需要的属性(例如:SomeStruct.firstMember
  • 对象:JS中的对象不是.NET中的托管对象,不能把一个JS对象传递给一个接受System.Object类型的WinRT方法
  • 对象标识:大部分情况下,在JS和WinRT之间传递的对象是不变的。JS引擎会保持一个已知对象的映射。当JS从WinRT获取到一个对象时,会在这个映射表中做匹配,如果不存在的话会插入。这个对于WinRT方法返回的对象也是一样的操作。但是有下面几点例外:
    • 从WinRT中返回的对象,如果添加了新(expando)的部属。在对象传回WinRT的时候新添加的部属不会跟随传入。但是因为有映射表在,从WinRT返回从前的对象的时候,从前对象的JS新增部属依然存在。
    • 结构和委派类型在WinRT中以值的形式存在,前面创建的值和新创建的值不会相等,虽然都是以引用类型表示的。
  • 名字冲突:不同的命名空间,可能有相同的成员,如果这些成员合并到同一个JS对象,那么需要以全前缀的方式引用:Class["MemberName"](parameter)
namespace CollisionExample {  
    interface InterfaceA  
    {  
        HRESULT Draw([in] Int32 a);  
    }  
    interface InterfaceB  
    {  
        HRESULT Draw([in] HString a);  
    }  
    runtimeclass ExampleObject {  
      interface InterfaceA  
      interface InterfaceB  
    }  
}  
var example = new ExampleObject();  
example["CollisionExample.InterfaceA.draw"](12);  
example["CollisionExample.InterfaceB.draw"]("hello");  
  • 输出参数:如果一个WinRT方法有多个输出参数,那么这些参数会合成到要给JS对象返回:
void ExampleMethod(  
  [OutAttribute] char^ first,   
  [OutAttribute] char^ second  
)  
var returnValue = exampleMethod();   // returnValue包含first和second两个部属
  • 静态成员:WinRT中有静态成员和实例成员。在JS中,静态成员被添加为WinRT运行类或者接口的对象部属:
// Static method.   
var accel = Windows.Devices.Sensors.Accelerometer.getDefault();   
// Instance method.   
var reading = accel.getCurrentReading();  

Using Windows Runtime Asynchronous Methods

WinRT中的异步操作在JS中采用Promise表示。在Promise的then或者done回调中,指定对异步操作返回结果的处理。下面是一个例子:

client.createResourceAsync(uri, description, item)  
    // Success.  
      .then(function(newItem) {   
               console.log("New item is: " + newItem.id);  
            },  
    // Error.  可选
            function(error) {   
               alert("Failed to create a resource.");  
            },  
    // Progress.  可选
            function(progress, resultSoFar) {   
               setProgressBar(progress);  
            });  

或者

client.createResourceAsync(uri, description, item)  
    // Success.  
      .done(function(newItem) {   
               console.log("New item is: " + newItem.id);  
            }); 

then和done的区别是done会将异常抛出。

在JS中调试异步操作可能会比较麻烦,没有足够多的信息。为了改善这一点,调试状态下当WinRT异步操作返回错误的时候,错误对象上会新增一些额外的部属:

  • asyncOpSource 储存错误发生时的调用信息,asyncOpSource.originatingCall是一个字符串,用于显示错误发生时的代码所在。
  • asyncOpType是一个字符串,可以用来获取发生错误的异步操作的类型。

Handling Windows Runtime Events in JavaScript

WinRT事件在JS中以小写字符串形式表示,可以被用来传入WinRT类的addEventListener和removeEventListener方法,比如Geolocator.PositionChanged事件:

var locator = new Windows.Devices.Geolocation.Geolocator();  
locator.addEventListener(  
    "positionchanged",   
     function (ev) {  
        console.log("Got event");  
    });  

另外也可以通过onpositionchanged来侦听事件变化:

locator.onpositionchanged =    
    function (ev) {  
        console.log("Got event");  
    }; 

另外一个差异是 WinRT的事件委托一般接收的是两个参数,而JS中这些都是作为事件的部属传递的:

function (ev) {  
    console.log("Sender: " + ev.target);  
    console.log("Position: " +  
        ev.position.latitude + "," +  
        ev.position.longitude);  
};  

JavaScript Representation of Windows Runtime Types

WinRT类型在JS中的表示:

  • Uint8,在JS中表示为Number,JS转WinRT的时候,先将JS的值转为Number,再使用ToUnit32方法,结果取2的8次方的模。
  • Int32,在JS中表示为Number,JS转WinRT的时候,先将JS的值转为Number,再使用ToInit32方法,结果取2的16次方的模。
  • Int64,在JS中表示为Number,这个处理起来比较复杂,因为JS的Number是double类型的,其浮点位为52个,指数位为11,符号为1。如果Int64超过了 [-2^53, 2^53]的范围,那么就可能会被视为计算错误。
  • Uint64,在JS中表示为Number,跟Int64有点类似,取值范围最好在[0, 2^53]。
  • Single(32位浮点),在JS中表示为Number,JS转WinRT的时候,如果结果不能用32位表示,会出错
  • Double,在JS中表示为Number,算是和JS的Number精度匹配了,如果转化失败,则会出错
  • Boolean,在JS中表示为Number,JS转WinRT的时候会调用ToBoolean,这意味着JS的字符串比如”test“可以被转为true
  • Char16,在JS中表示为String,JS转WinRT的时候会调用ToString,结果必须是和Char16同长度,否则会出错
  • String,在JS中表示为String,这两个算是比较匹配,不过JS的null和undefined会被转为"null"和"undefined”,如果WinRT需要一个null字符串,则应该传一个JS的”“。
  • Enumeration,在JS中表示为Object,各种枚举项目以Object的只读部属表示。由于Enumeration是无符号或者有符号的32位整型,所以上面描述的32位整型的转化也适用于枚举。但是从JS转WinRT的时候,不会检查值是否是枚举中有效的值。
  • Structure,在JS中表示为Object,Structure是复合值,按照其所复合的项目,组成一个JS的Object。注意,Structure不能通过new关键字构建
  • Array,在JS中表示为Array,注意的是,WinRT的Array是非可改的;其次,Array的[[Class]]也不能设置为Array,所以Array.isArray(v)返回false。注意的是,传一个JS的Array给需要WinRT Array的时候,会拷贝整个Array。
  • Delegate (function callback),在JS中表示为Function。在WinRT中一个Delegate(委调)是一个函数的引用。在调用这个委调的时候,如果传的参数比实际的少,那么就会失败;如果比实际的多,那么多余的就会被忽略。输出的参数会打包作为一个JS对象的部属返回。如果一个原生的JS对象被转化为一个WinRT委调,那么它会被外套打包成一个原生的WinRT委调。
  • Interface,在JS中表示为Object,接口或者接口族群并不直接映射为一个JS对象。如果一个接口有对应的runtimeclass,那么这个runtimeclass对应的JS对象会被用来当作接口的JS对象。如果没有,那么一个临时的JS对象就会被制作出来,用来表示这个接口。
  • Runtime classes,在JS中表示为Object。这个容易对应。值得注意的是runtimeclass对应的JS对象,其原型对象包含了所有该runtimeclass的成员。

Windows Runtime DateTime and TimeSpan Representations

WinRT的DateTime比JS的Date精度要高。当WinRT的DateTime转为位JS对象是,也会转为Date对象,不过具有WInRT的精度。如果子啊JS中修改了从WinRT转化而来的Date对象,那么这个Date类对象就会变成一个普通的JS的Date对象。

WinRT的TimeSpan会被转为一个JS的Number,单位是毫秒。

Error codes for Windows Runtime apps using JavaScript

此章节列举了一些在Visual Studio console 会出现的错误代码。

MSApp

MSApp仅支持使用JS作为WinRT编程语言的项目(包括PWA)。MSApp对象只有在从ms-appx加载的HTML文档中可见。MSApp提供了对Blob和MSStream的支持。

其他

(完)

comments powered by Disqus