Join MDN and developers like you at Mozilla's View Source conference, 12-14 September in Berlin, Germany. Learn more at https://viewsourceconf.org

使用 js-ctypes

草案
本页尚未完工.

在使用 js-ctypes 前,你需要导入 ctypes.jsm 代码模块(Javascript Code Module)。这非常简单,只需要在所需的 Javascript scope 内包含下面这行代码 :

Components.utils.import("resource://gre/modules/ctypes.jsm")

加载本地库

在引入上述的代码模块后,你可以调用 ctypes.open() 来加载你想使用的本地库。例如:在Windows下,你可以这样引入系统的user32库:

var lib = ctypes.open("user32.dll");

On Mac OS X, you can load the Core Foundation library from the Core Foundation framework like this:

var coreFoundation = ctypes.open("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");

返回值是一个库对象,你可以用这个库对象去声明方法和数据类型,以供使用。

Note: js-ctypes only works with C libraries; you can't use C++ methods directly. Instead, you'll need to create a shim library that uses C functions that then call into the C++ library for you.

库搜索路径

如果你指定了库的完整路径,那么就可以直接加载该库. 否则,系统会以一套标准的流程来搜索该库。

Windows

在Window下,搜索库的顺序如下:

  1. The application's directory.
  2. The system directory.
  3. The 16-bit system directory.
  4. The Windows directory.
  5. The current working directory
  6. The directories listed in the PATH environment variable.
Note: 该内容来至于 this article on MSDN.

用完了

当你使用完库之后,你应该通过调用库对象的close()方法来关闭它。

lib.close();

如果关闭库失败,库会在垃圾回收时,自动关闭。

使用库

你可能需要声明一些新类型。这些类型可能是简单的,也可能是像结构体这样较复杂的类型,欲知详情请查看 Declaring types。大多数情况下,你会需要 声明一个或多个函数(declare one or more functions),以供调用。 

只要你声明了类型和方法,你就可以使用他们。 实例化C 类型的数据对象和引用他们可以参考 Working with data

内存管理

If JS code creates a structure or array, that memory will be valid as long as the JS object stays alive. Pointers to that memory must be carefully managed to make sure the underlying memory is still referenced.

When binary code hands back a pointer/handle to allocated memory, the JS code must make sure to free that memory with the correct allocator. It is usually best to expose a freeing function from the binary.

保持活着的对象

The following js-ctypes objects will hold references to objects, keeping them alive. This is not an exhaustive list, but will help you understand memory management and how it affects your use of js-ctypes:

  • A function or static data declared using the declare() method will hold that library alive.
  • A CType will hold referent CType objects alive.
  • A CData will hold referent CData objects alive, in specific circumstances. For example, a CData object produced by accessing a field of a structure or the internals of an array will hold the referent objects alive.

究竟会不会保持活着的对象

It's important to note that getting direct access to the contents of a CData object using address(), addressOfElement(), or contents, will result in a CData object that does not hold its referent alive. Be sure to hold an explicit reference to be sure the referent object doesn't get garbage collected before you're done using it.

闭包

You also need to be sure to retain references to any JavaScript code that native code may call back into. This should be obvious, but is important enough to be worth stating explicitly.

当有疑问时, malloc()

When you absolutely, positively need to keep data around, you can use malloc() to allocate it directly. This bypasses JavaScript's memory management and lets you handle memory management yourself.

示例

这些例子提供对js-ctypes使用的快速浏览。 要看更复杂的例子,请翻阅js-ctypes examples

调用 Windows 例程

这个例子演示了怎么使用ctypes来调用Win32 API。

Components.utils.import("resource://gre/modules/ctypes.jsm");

var lib = ctypes.open("C:\\WINDOWS\\system32\\user32.dll");

/* Declare the signature of the function we are going to call */
var msgBox = lib.declare("MessageBoxW"/* 函数名称*/,
                         ctypes.winapi_abi,/*ABI类型,有3种,这里指调用系统的接口*/
                         ctypes.int32_t,/*返回值*/
                         ctypes.int32_t,/*告警类型*/
                         ctypes.jschar.ptr,/*告警内容*/
                         ctypes.jschar.ptr,/*标题*/
                         ctypes.int32_t);/*按钮类型*/
var MB_OK = 0;

var ret = msgBox(0, "Hello world", "title", MB_OK);

lib.close();

第三行, 就是 user32.dll 系统库被加载的地方。第六行声明了 msgBox() 函数是一个调用系统函数MessageBoxW的方法. 第十五行调用了msgBox() ,会显示出一个告警框哦~。

最后当我们完成使用之后,千万记得用close来关闭它。

相较于给定完整的路径,通常只是给一个文件名。

关于声明函数

我们已经知道可以通过MSDN来获取MessageBox (MessageBoxW is just a unicode version of same function) 的相关信息,用以声明函数。也学习了关于lib.declare怎么使用:lib.declare. 学习了数据类型的使用: Data Types. 我们知道了它可以这样来声明:

int WINAPI MessageBox(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType
);

所以我们阅读这个文章关于类型定义和使用: Declaring Types

var lib = ctypes.open("user32.dll");

甚至不用后缀:

var lib = ctypes.open("user32");

在 Mac OS X 上调用 Carbon 例程

This example demonstrates how to use ctypes to call a Carbon function on Mac OS X.

Note:This example will not work on 64bit OS X, you will likely need to change to the Cocoa API.
/* build a Str255 ("Pascal style") string from the passed-in string */

function makeStr(str) {
  return String.fromCharCode(str.length) + str;
}

Components.utils.import("resource://gre/modules/ctypes.jsm");

var carbon = ctypes.open("/System/Library/Frameworks/Carbon.framework/Carbon");

stdAlert = carbon.declare("StandardAlert",       /* function name */
                          ctypes.default_abi,    /* ABI type */
                          ctypes.int16_t,        /* return type */
                          ctypes.int16_t,        /* alert type */
                          ctypes.char.ptr,       /* primary text */
                          ctypes.char.ptr,       /* secondary text */
                          ctypes.uint32_t,       /* alert param */
                          ctypes.int16_t);       /* item hit */

var hit = 0;
var msgErr = makeStr("Carbon Says...");
var msgExp = makeStr("We just called the StandardAlert Carbon function from JavaScript!");

var err = stdAlert(1, msgErr, msgExp, 0, hit);

carbon.close();

The makeStr() function is a utility routine that takes as input a standard JavaScript string and returns a Carbon-style "Pascal" string, which is a length byte followed by the characters of the string itself. Note that this only works correctly if the string is in fact under 256 characters; if it's longer, this will fail spectacularly.

In line 9, the Carbon library is loaded from the system's Carbon framework.

Line 11 declares the stdAlert() function, which will call the Carbon StandardAlert routine. It uses the default ABI, returns a 16-bit integer (which is a Carbon OSErr value), and accepts an integer (the alert type), two strings, a pointer to a parameter block, which we aren't using, and another integer, which is used to return the hit item. See Apple's documentation for StandardAlert for details.

After that, we simply set up our parameters by using makeStr() to generate the two Str255 strings we need, then call stdAlert(), which produces the following alert window:

ctype-mac-dialog.png

The last thing we do is call carbon.close() to close the library when we're done using it.

在 Linux/POSIX 上调用 LibC 例程

This example demonstrates how to use ctypes to call a libc function on Linux.

/* import js-ctypes */
Components.utils.import("resource://gre/modules/ctypes.jsm");

/* Open the library */
try {
    /* Linux */
    var libc = ctypes.open("libc.so.6");
} catch (e) {
    /* Most other Unixes */
    libc = ctypes.open("libc.so");
}

/* Import a function */
var puts = libc.declare("puts",             /* function name */
                        ctypes.default_abi, /* call ABI */
                        ctypes.int,       /* return type */
                        ctypes.char.ptr);   /* argument type */

var ret = puts("Hello World from js-ctypes!");
wge

文档标签和贡献者

 此页面的贡献者: wZi, zbinlin
 最后编辑者: wZi,