转载

Chakra实战:UWP与js交互(C#)

几个月前在翻MSDN时发现Microsoft已经允许在Windows Store Apps(即UWP)里使用Chakra的API了。这意味着大家终于可以光明正大地在app中调用Javascript。//另外UWP允许JIT了所以你自己移植个V8上去其实也行在8.x时代,Chakra是被标记为Desktop only的API,想要在Store apps里使用js,要么整个App使用HTML/js编写,要么使用WebView调用。前者显然不符合主要使用C#/XAML编写UI的前提,后者

麻烦的要死,

不好用。

UWP写起来真舒服

(棒读)

使用Chakra之前需要较为深入地了解Chakra API,COM和JavaScript。

其实不了解直接照抄代码拿着用也没什么不好,就是出了错之后不好排除。

使用C#调用Chakra API

UWP是可以直接使用chakra.dll大部分函数的,除去 JsStartProfiling JsStopProfiling JsEnumerateHeapJsIsEnumeratingHeap 四个。

然而Microsoft并没有在SDK里提供C#/WinRT API,所以需要用P/Invoke进行基本的封装。这里以 Microsoft官方示例 为准。

将上述的Native.cs以及该目录下所有文件都加入项目。

使用C#调用Javascript

主要步骤:

1.使用 JsCreateRuntime 创建一个Javascript运行时(runtime)

JavaScriptRuntime runtime;   Native.ThrowIfError(Native.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null, out runtime));   

2.使用 JsCreateContext 在这个运行时内创建一个上下文(context)

JavaScriptContext context;   Native.ThrowIfError(Native.JsCreateContext(runtime, out context));   

3.使用 JsSetCurrentContext 将该上下文设置到当前线程

Native.ThrowIfError(Native.JsSetCurrentContext(context));   

4.(可选)使用 JsStartDebugging 开启调试

Native.ThrowIfError(Native.JsStartDebugging());   

5.使用 JsRunScript 运行Javascript

JavaScriptValue result;   JavaScriptSourceContext currentSourceContext = JavaScriptSourceContext.FromIntPtr(IntPtr.Zero);   if (Native.JsRunScript(script, currentSourceContext, ""/*如果需要调试,需要在此处指定源码绝对路径*/, out result) != JavaScriptErrorCode.NoError)   {     JavaScriptException exception;     Native.ThrowIfError(Native.JsGetAndClearException(out exception));     //在此处理异常 } JavaScriptValue stringResult;   UIntPtr stringLength;   Native.ThrowIfError(Native.JsConvertValueToString(result, out stringResult));   Native.ThrowIfError(Native.JsStringToPointer(stringResult, out returnValue, out stringLength));   var ret = Marshal.PtrToStringUni(returnValue);//处理返回值   

6.其它用途(如 JsCallFunction 等)

需要注意的是,如果当前使用的上下文已经被设定到一个线程(第三步),那么该上下文仅能用于这个线程。当这个线程不再需要这个上下文时,需要将其设置为NULL(第七步)。

7.将当前线程的jsrt上下文设置为NULL

Native.ThrowIfError(Native.JsSetCurrentContext(new JavaScriptContext()));   

8.(可选)在其它线程使用这个jsrt上下文(从第三步开始重复)9.使用完成后销毁这个runtime

Native.ThrowIfError(Native.JsDisposeRuntime(runtime));   

类型转换

JavaScript的类型与C#是不同的,而在Chakra API中使用JsValueRef(即C#中封装的JavaScriptValue)来表示一个值。

常用的类型主要有Undefined, Null, Number, String, Boolean, Object, Function, Array等。C#与Javascript交互时,需要将JavaScriptValue与 .NET 的类型互相转换。

JavaScriptValue本身封装了集中简单类型的转换,例如Number与System.Double: JavaScriptValue.FromDouble()JavaScriptValue.ToDouble()

判断JavaScriptValue的类型使用 JsGetValueType 函数

JavaScriptValueType type;   Native.ThrowIfError(Native.JsGetValueType(val, out type));   switch (type)   {     //对特定类型进行处理 } 

WinRT类型的转换

任何 WinRT类型 (写C#时可粗略理解为放在winmd里的类型)均继承自IInspectable,可以直接将其对象使用 JsInspectableToObject 转换成JavaScriptValue使用。

同样如果 确定一个JavaScriptValue代表的对象继承自IInspectable ,也可以使用 JsObjectToInspectable 将其转换为System.Object。

数组类型的转换

JavaScript的数组并没有实现IInspectable接口,因此它不能直接使用COM交互,需要手动读取其值并且进行转换。

举例:转换为.NET的List

List<T> JsArrayToList<T>(JavaScriptValue arrayval) {     var _retList = new List<T>();      JavaScriptValueType type;     Native.ThrowIfError(Native.JsGetValueType(arrayval, out type));     if (type != JavaScriptValueType.Array)         return null;      JavaScriptValue lengthvalue;     Native.ThrowIfError(Native.JsGetProperty(             arrayval,             JavaScriptPropertyId.FromString("length"),             out lengthvalue));      int length;     Native.ThrowIfError(Native.JsNumberToInt(lengthvalue, out length));      for (int i = 0; i < length; i++)     {         JavaScriptValue elem;         Native.ThrowIfError(Native.JsGetIndexedProperty(             arrayval,             JavaScriptValue.FromInt32(i),             out elem));          JavaScriptValueType elemtype;         Native.ThrowIfError(Native.JsGetValueType(elem, out elemtype));          if (elemtype == JavaScriptValueType.Object)         {             object insp;             var err = Native.JsObjectToInspectable(elem, out insp);             if (err == JavaScriptErrorCode.NoError &&                     insp.GetType() == typeof(T))                 _retList.Add((T)insp);         }      }     return _retList; } 

函数调用

首先调用 JsGetGlobalObject 获取当前上下文的全局对象,使用 JsGetProperty 获得函数的对象,再使用 JsCallFunction 调用函数。函数的参数需要全部转换成JavaScriptValue,同时将返回值从JavaScriptValue转换成所需要的类型。

JavaScriptValue CallFunction(string functionName, params JavaScriptValue[] parameters) {     JavaScriptValue _globalObject;     Native.ThrowIfError(Native.JsGetGlobalObject(out _globalObject));     var functionId = JavaScriptPropertyId.FromString(functionName);     var function = _globalObject.GetProperty(functionId);     return function.CallFunction(parameters); } 

使用Javascript调用WinRT

在UWP中使用Javascript而不是Python等别的脚本语言做扩展,原因之一就是JavaScript调用Windows Runtime Component(WinRT组件)非常方便。无论是对于系统API还是自己创建的WinRT组件,都可以用 JsProjectWinRTNamespace 轻松地映射到Javascript中。映射后的使用方式,与直接使用HTML/js编写UWP时调用WinRT API相同。需要注意的是,有WebHostHiddenAttribute的WinRT类仍然无法被js使用。

如果想将WinRT对象映射为js的全局对象,也可以先使用 JsInspectableToObject 将其转换为JavaScriptValue,使用 JsGetGlobalObject 获得全局对象, JsSetProperty 将WinRT类型的对象设置为全局对象的属性。

调试

集成VS调试方便是Chakra的另一大优点。将VS的C#项目内的调试器类型设置为"Script"(如图),并在代码中调用 JsStartDebugging ,并且指定js源代码文件位置(见上文),在VS中打开相应js文件,附加调试器,即可开始调试脚本。

Chakra实战:UWP与js交互(C#)
2016年的第一篇文章,如此长篇大论,药丸
原文  http://hjc.im/uwp-chakra-js-1/
正文到此结束
Loading...