You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用safer-ffi生成Rust DLL,ffi-napi调用结构体参数异常排查

问题:Rust safer-ffi生成的DLL中结构体参数在Node.js ffi-napi调用时乱码

我用Rust的safer-ffi库编译生成DLL,其中process_waave_info函数接收自定义WavesInfo结构体作为参数,但通过Node.js的ffi-napi调用该函数时,结构体字段值出现乱码,无法正确传递参数。同一DLL中的get_sens_to_px函数(仅接收基础类型参数)调用完全正常。

代码片段

Rust 代码(lib.rs)

use safer_ffi::prelude::*;

#[ffi_export]
#[derive_ReprC]
#[repr(C)]
pub struct WavesInfo {
    pub wave_count: u32,
    pub wave_heights: *const f32,
    pub wave_periods: *const f32,
}

#[ffi_export]
pub fn process_waave_info(info: WavesInfo) -> u32 {
    println!("Received wave_count: {}", info.wave_count);
    println!("First wave height: {}", unsafe { *info.wave_heights });
    println!("First wave period: {}", unsafe { *info.wave_periods });
    info.wave_count
}

#[ffi_export]
pub fn get_sens_to_px(sens: f32) -> f32 {
    sens * 100.0
}

safer_ffi::cfg_aliases! {
    #[cfg(target_os = "windows")]
    OS_WINDOWS = true;
}

#[cfg(OS_WINDOWS)]
#[ffi_export]
pub extern "C" fn DllMain(_: HINSTANCE, call_reason: DWORD, _: LPVOID) -> BOOL {
    match call_reason {
        DLL_PROCESS_ATTACH => println!("DLL loaded"),
        DLL_PROCESS_DETACH => println!("DLL unloaded"),
        _ => (),
    }
    TRUE
}

Node.js 代码(test.mjs)

import ffi from 'ffi-napi';
import ref from 'ref-napi';
import refArray from 'ref-array-napi';

const f32Array = refArray(ref.types.float);

const WavesInfo = ref.types.struct({
    wave_count: ref.types.uint32,
    wave_heights: f32Array,
    wave_periods: f32Array,
});

const lib = ffi.Library('./target/release/wave_processor.dll', {
    process_waave_info: ['uint32', [WavesInfo]],
    get_sens_to_px: ['float', ['float']],
});

// 测试get_sens_to_px(正常工作)
const sensResult = lib.get_sens_to_px(0.5);
console.log('get_sens_to_px result:', sensResult); // 输出: get_sens_to_px result: 50

// 测试process_waave_info(结构体参数乱码)
const waveHeights = new f32Array([1.2, 3.4, 5.6]);
const wavePeriods = new f32Array([6.0, 7.0, 8.0]);
const wavesInfo = new WavesInfo({
    wave_count: 3,
    wave_heights: waveHeights,
    wave_periods: wavePeriods,
});

const result = lib.process_waave_info(wavesInfo);
console.log('process_waave_info result:', result);

错误输出与期望输出

当前错误输出

DLL loaded
get_sens_to_px result: 50
Received wave_count: 1869576192
First wave height: 0.0
First wave period: 0.0
process_waave_info result: 1869576192

期望输出

DLL loaded
get_sens_to_px result: 50
Received wave_count: 3
First wave height: 1.2
First wave period: 6.0
process_waave_info result: 3

排查与解决方案

核心原因:结构体类型定义不匹配

问题出在Node.js中WavesInfo结构体的类型定义与Rust侧的C兼容结构体不匹配:

  1. 指针类型处理错误:Rust侧的wave_heights和wave_periods是*const f32(对应C的const float*),但Node.js中直接用f32Array类型会传递数组对象的包装结构,而非原始指针地址,导致Rust读取到错误内存值。
  2. 内存布局不兼容:ref-array-napi生成的数组类型包含元数据(如长度),而非单纯的指针,破坏了结构体的C语言内存布局。

修正后的Node.js代码

import ffi from 'ffi-napi';
import ref from 'ref-napi';

// 正确对应Rust的*const f32:C语言的const float*
const floatPtr = ref.types.pointer(ref.types.float);

const WavesInfo = ref.types.struct({
    wave_count: ref.types.uint32,
    wave_heights: floatPtr,
    wave_periods: floatPtr,
});

const lib = ffi.Library('./target/release/wave_processor.dll', {
    process_waave_info: ['uint32', [WavesInfo]],
    get_sens_to_px: ['float', ['float']],
});

// 测试正常函数
const sensResult = lib.get_sens_to_px(0.5);
console.log('get_sens_to_px result:', sensResult);

// 修正结构体参数传递
const waveHeights = ref.alloc('float', 3);
ref.writeFloat(waveHeights, 0, 1.2);
ref.writeFloat(waveHeights, 4, 3.4); // float占4字节,偏移量4
ref.writeFloat(waveHeights, 8, 5.6);

const wavePeriods = ref.alloc('float', 3);
ref.writeFloat(wavePeriods, 0, 6.0);
ref.writeFloat(wavePeriods, 4, 7.0);
ref.writeFloat(wavePeriods, 8, 8.0);

const wavesInfo = new WavesInfo({
    wave_count: 3,
    wave_heights: waveHeights,
    wave_periods: wavePeriods,
});

const result = lib.process_waave_info(wavesInfo);
console.log('process_waave_info result:', result);

额外注意事项

  • 内存生命周期:确保Node.js侧分配的数组内存在Rust函数执行期间有效,若Rust需要长期持有指针,需在Rust侧复制数据。
  • ReprC验证:Rust结构体必须添加#[derive_ReprC]和#[repr(C)],保证内存布局与C语言完全一致。
  • 调用约定:safer-ffi默认使用extern "C",ffi-napi默认也是C调用约定,无需额外配置,若有改动需保持一致。

内容的提问来源于stack exchange,提问作者tomato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:23:14