函数调用
加载库
要声明函数,首先使用 加载共享库koffi.load(filename)。
// ES6 syntax: import koffi from 'koffi';
const koffi = require('koffi');
const lib = koffi.load('/path/to/shared/library');
// File extension depends on platforms: .so, .dll, .dylib, etc.一旦对该库的所有引用都消失(包括使用它的所有函数,如下所述),该库将自动卸载。
从Koffi 2.3.20开始,您可以通过调用显式卸载库lib.unload()。卸载该库后,任何从该库查找或调用函数的尝试都会崩溃。
加载选项
Koffi 2.6 中的新功能
该load函数可以采用可选的对象参数,具有以下选项
const options = {
lazy: true // Use RTLD_LAZY (lazy-binding) on POSIX platforms (by default, use RTLD_NOW)
};
const lib = koffi.load('/path/to/shared/library.so', options);如果需要,可以添加更多选项。
函数定义
定义语法
使用 返回的对象koffi.load()从库中加载 C 函数。为此,您可以使用两种语法:
经典语法,灵感来自node-ffi
类 C 原型
经典语法
要声明函数,您需要指定其未损坏的名称、返回类型和参数。使用省略号作为可变参数函数的最后一个参数。
Koffi 自动尝试非标准 x86 调用约定的损坏名称。有关此主题的更多信息,请参阅有关调用约定的部分。
类似 C 的原型
如果您愿意,可以使用简单的类似 C 的原型字符串来声明函数,如下所示
您可以将()or(void)用于不带参数的函数。
可变参数函数
可变参数函数是用省略号作为最后一个参数来声明的。
为了调用可变参数函数,您必须为每个附加 C 参数提供两个 Javascript 参数,第一个是预期类型,第二个是值。
在 x86 平台上,只有 Cdecl 约定可用于可变参数函数。
调用约定(Calling conventions)
Koffi 2.7 中已更改
默认情况下,调用 C 函数是同步发生的。
大多数体系结构每个进程仅支持一种过程调用标准。32 位 x86 平台是一个例外,Koffi 支持多种 x86 约定
Cdecl
koffi.func(name, ret, params)
(default)
这是默认约定,也是其他平台上唯一的约定
Stdcall
koffi.func('__stdcall', name, ret, params)
__stdcall
此约定在 Win32 API 中广泛使用
Fastcall
koffi.func('__fastcall', name, ret, params)
__fastcall
很少使用,使用 ECX 和 EDX 作为前两个参数
Thiscall
koffi.func('__thiscall', name, ret, params)
__thiscall
很少使用,使用 ECX 作为第一个参数
您可以在非 x86 平台上安全地使用它们,它们只是被忽略。
下面您可以找到一个小示例,展示如何使用非默认调用约定,并使用两种语法:
调用类型
同步调用
一旦声明了本机函数,您就可以像调用任何其他 JS 函数一样简单地调用它。
对于可变参数函数,您必须指定每个附加参数的类型和值。
异步调用
您可以通过其 async 成员调用该函数来发出异步调用。在这种情况下,您需要提供一个带有参数(err, res)的回调函数作为最后一个参数。
这些调用由工作线程执行。您有责任处理本机代码中可能由多线程引起的数据共享问题。
util.promisify()您可以使用 Node.js 标准库轻松地将这种回调式异步函数转换为基于 Promise 的版本。
可变参数函数不能异步调用。
异步函数在工作线程上运行。如果在线程之间共享数据,则需要处理线程安全问题。
回调必须从主线程调用,或者更准确地说,从与 V8 解释器相同的线程调用。从另一个线程调用回调是未定义的行为,并且可能会导致崩溃或大混乱。
函数指针
Koffi 2.4 中的新功能
您可以通过两种方式调用函数指针:
直接调用函数指针
koffi.call(ptr, type, ...)将函数指针解码为实际函数
koffi.decode(ptr, type)
下面的示例展示了如何基于以下本机 C 库以两种方式调用 C 函数指针:int (*)(int, int)
直接调用指针
koffi.call(ptr, type, ...)用于调用函数指针。前两个参数是指针本身和您尝试调用的函数的类型(如下koffi.proto()所示声明),其余参数用于调用。
解码指向函数的指针
用于获取 JS 函数,然后您可以像任何其他 Koffi 函数一样使用该函数。koffi.decode(ptr, type)
此方法还允许您使用已解码函数的 async 成员执行异步调用。
参数转换
默认情况下,Koffi 只会将参数从 Javascript 转发并转换为 C。但是,许多 C 函数使用指针参数作为输出值或输入/输出值。
除此之外,在接下来的几页中,您将了解更多信息:
Last updated