函数调用

加载库

要声明函数,首先使用 加载共享库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()。卸载该库后,任何从该库查找或调用函数的尝试都会崩溃。

在某些平台上(例如Linux 上的 musl C 库),共享库无法卸载,因此在调用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 约定

约定(conventions)
经典形式(Classic form)
原型形式(Prototype form)
描述(Description)

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 平台上安全地使用它们,它们只是被忽略。

Koffi 2.7 中引入了对将约定指定为经典形式的第一个参数的支持。

在早期版本中,您必须使用koffi.stdcall()和 类似的功能。这些函数仍然受支持,但已弃用,并将在 Koffi 3.0 中删除。

下面您可以找到一个小示例,展示如何使用非默认调用约定,并使用两种语法:

调用类型

同步调用

一旦声明了本机函数,您就可以像调用任何其他 JS 函数一样简单地调用它。

对于可变参数函数,您必须指定每个附加参数的类型和值。

异步调用

您可以通过其 async 成员调用该函数来发出异步调用。在这种情况下,您需要提供一个带有参数(err, res)的回调函数作为最后一个参数。

这些调用由工作线程执行。您有责任处理本机代码中可能由多线程引起的数据共享问题。

util.promisify()您可以使用 Node.js 标准库轻松地将这种回调式异步函数转换为基于 Promise 的版本。

可变参数函数不能异步调用。

函数指针

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