最近一直在帮人处理Thunderbird不同软件版本下XPCOM注册不成功的问题,就是在一个版本下开发的扩展在其他版本上都不能使用。这里纠结了老久,后来想到了可能是xulrunner-sdk版本不同引起的,所以就有了XPCOM组件开发,发现这样开发扩展太麻烦,毕竟组件完成的工作也就是中间模块,只是用来连接Thunderbird和底层模块的,由于之前也写过COM组件,根据对COM组件的开发经历,突然发现这里完全没必要使用XPCOM组件,一个简单DLL模块就行了,因为对Thunderbird事件的捕获、响应都是用Javascript实现的,我们只需要使用JS调用我们的模块,现在就来介绍一下用JS调用dll的方法:js-ctypes。
MDN上关于js-ctypes解释:
js-ctypes allows application and extension code to call back and forth to native code written in C. C++ support is limited, see bug 505907 for full support. Unlike binary XPCOM components, It allows developers to ship a single binary for use with multiple versions of Firefox.
注意两点:1、js-ctypes只和C libraries交互,不能和C++ libraries交互,如果需要和C++ libraries交互,可以建立中间C libraries。
2、这个是重要的一点,它可以解决XPCOM的版本兼容问题。
要使用js-ctypes,我们必须导入ctypes.jsm模块:
Components.utils.import("resource://gre/modules/ctypes.jsm")
ctypes对象提供一些方法和数据类型。
CType ArrayType(type[,length]); | Returns a new CType representing an array data type. |
CData cast(data, type); | Casts the specified CData object to a new type, returning a new CData object representing the value in the new type. |
CType FunctionType(abi, returnType[, argType1, ...]); | Returns a new CType object describing a C function. |
CData Int64(n); | |
String libraryName(name); | Returns the correct platform-specific filename for a given library name |
Library open(libSpec); | Opens a library, specified by a pathname |
CType PointerType(typeSpec); | Returns a CType object describing a new pointer data type. |
CType StructType(name[, fields]); | Returns a CType object describing a new structure data type. |
CData UInt64(n); |
var lib = ctypes.open("user32.dll");
在MAC OS X下:
var coreFoundation = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
当你停止使用时,使用close(),如果失败,垃圾回收机制会自动关闭对库的引用。
lib.close();
dll导出函数需要使用Library对象的declare()方法进行声明,一旦声明了,函数可以使用标准语法进行函数调用。例如,我们创建DLL,导出函数long Add(long a ,long b);
在js-ctypes中,我们需要对其进行声明:
var add = lib.declare("Add", ctypes.default_abi, ctypes.long, ctypes.long, ctypes.long);
第一个参数是函数名,第二个参数有三种选择:
default_abi | Corresponds to cdecl ; standard libraries use this ABI. You also use this for all system calls on Mac OS X and Linux. |
stdcall_abi | Used for calling functions declared with stdcall on Windows. These functions' names are automatically mangled for you by js-ctypes. |
winapi_abi | Used for calling Windows system functions. These are declared as stdcall on Windows, but do not have mangled names like those used by stdcall_abi above. |
后面的参数,分别是函数的返回参数和输入参数,如何返回是void或输入是void,在声明中不用表示。
另外还有两个属性用来查询错误信息:
errno | Number | The value of the latest system error. Similar to errno in libc, available on all platforms. Cannot be set. |
winLastError | Number|undefined | The value of the latest Windows error. Similar to GetLastError under Windows, available only for Windows. Cannot be set. |
内存管理:如果JS代码段中创建了结构体或者数组成员,只要JS对象存在它们所占有的内存将不会被释放。下面的js-ctypes对象会持有对象引用,保持对象存在:
1、用declare()声明的函数或静态数据,它们会保持library对象存在
2、CType变量使CType对象持续存在。
3、在特定情况下,CData变量使CData对象持续存在。例如通过方位结构体成员或者数组成员产生的CData对象会使CData对象持续存在。
注意当使用CData.address()或addressOfElement或contents直接访问CData的内容时,将会隐式产生一个CData对象,但它不能持续存在,可能会被GC自动回收。在使用它之前应该显式的引用,保证所指对象不会被GC回收。