使用rust调用c++静态库并编译nodejs包

本文介绍了如何使用Rust调用C++ SDK,并将其封装为Node.js包。通过创建Rust库项目,利用Cargo配置,以及处理不同架构的编译链接,实现跨平台的接口调用。详细步骤包括项目创建、Cargo.toml配置、package.json的处理,以及代码分析。
摘要由CSDN通过智能技术生成

🚀 优质资源分享 🚀

学习路线指引(点击解锁) 知识定位 人群定位
🧡 Python实战微信订餐小程序 🧡 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

在项目上经常要用到身份证阅读器、护照阅读仪、指纹仪等各种品牌硬件,假如每套系统的都做集成开发那代码的维护成本将变得很高,为此采用rust来调用厂家提供的sdk c++开发包并封装成nodejs包,用fastify来开发成web api独立的服务形式。这样我们开发系统时只需调用web接口即可,跨平台又可共用,方便快捷,话不多说来看代码如何实现。

一、创建项目
安装rust后,打开vs新建一个工程目录,我们通过cargo new创建的一个package项目,加上–lib参数后创建的项目就是库项目(library package)。
cargo new --lib reader
package 就是一个项目,因此它包含有独立的 Cargo.toml 文件,用于项目配置。库项目只能作为三方库被其它项目引用,而不能独立运行,即src/lib.rs。
典型的package
如果一个 package 同时拥有 src/main.rs 和 src/lib.rs,那就意味着它包含两个包:库包和二进制包,这两个包名也都是 test_math —— 都与 package 同名。
一个真实项目中典型的 package,会包含多个二进制包,这些包文件被放在 src/bin 目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个 src/lib.rs:
.
├── Cargo.toml
├── Cargo.lock
├── src
│ ├── main.rs
│ ├── lib.rs
│ └── bin
│ └── main1.rs
│ └── main2.rs
├── tests
│ └── some_integration_tests.rs
├── benches
│ └── simple_bench.rs
└── examples
└── simple_example.rs

唯一库包:src/lib.rs
默认二进制包:src/main.rs,编译后生成的可执行文件与package同名
其余二进制包:src/bin/main1.rs 和 src/bin/main2.rs,它们会分别生成一个文件同名的二进制可执行文件
集成测试文件:tests 目录下
性能测试benchmark文件:benches 目录下
项目示例:examples 目录下
这种目录结构基本上是 Rust 的标准目录结构,在 github 的大多数项目上,你都将看到它的身影。
运行Cargo build命令,我们在target\debug目录下可以看到编译后的结果。
二、Cargo.toml

[package]
name = "reader"
version = "0.1.0"
edition = "2018"
exclude = ["reader.node"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libc = "0.2.9"
libloading = "0.7"
once\_cell = "1.8"
serde = { version = "1.0", features = ["derive"] }
widestring = "0.5.1"
serde\_json = "1.0"
base64 = "0.13"
hex="0.4.2"
encoding = "0.2"
tokio={version="1.18.0",features = ["full"]}

[dependencies.neon]
version = "0.9"
default-features = false
features = ["napi-5", "channel-api"]

[lib]
crate-type = ["cdylib"]

三、package.json

{
  "name": "reader",
  "version": "0.1.0",
  "description": "",
  "main": "index.node",
  "scripts": {
    "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
    "build-debug": "npm run build --",
    "build-release": "npm run build -- --release",
    "build\_win32": "npm run build -- --release --target=i686-pc-windows-msvc",
    "test": "cargo test",
    "run": "cargo run"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "cargo-cp-artifact": "^0.1"
  },
  "dependencies": {
    "express": "^4.17.3"
  }
}

我们可以打印rust看看编译输出支持哪些架构
rustc --print target-list
//添加 x86编译链接器
rustup target add i686-pc-windows-msvc

四、代码分析

use std::collections::HashMap;
use std::str;
use std::fmt::Write;
use std::io::{Error};

extern crate encoding;
use encoding::all::GB18030;
use encoding::{DecoderTrap,EncoderTrap,Encoding};

use tokio::time::{sleep, Duration,Instant};
use libc::{c_int, c_void};
use libloading::{Library, Symbol};
use neon::prelude::*;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};

use widestring::{WideCStr, WideCString, WideChar};
// 编码转换 utf8 -> utf16le
fn encode(source: &str) -> WideCString {
    let string\_source = source.to\_string() + "\0";
    WideCString::from\_str(&string_source).unwrap()
}
// 解码转换 utf16le -> utf8
fn decode(source: &[WideChar]) -> String {
    WideCStr::from\_slice\_truncate(source)
        .unwrap()
        .to\_string()
        .unwrap()
}
// 加载 dll
static LIBRARY: OnceCell = OnceCell::new();

//指定编译架构
static MACHINE\_KIND: &str = if cfg!(target\_os = "windows") {
 if cfg!(target\_arch = "x86") {
 "win32"
 } else if cfg!(target\_arch = "x86\_x64") {
 "win64"
 } else {
 "other"
 }
} else if cfg!(target\_os = "linux") {
 if cfg!(target\_arch = "x86") {
 "linux32"
 } else if cfg!(target\_arch = "x86\_64") {
 "linux64"
 } else if cfg!(target\_arch = "aarch64") {
 "aarch64"
 } else if cfg!(target\_arch = "arm") {
 "arm"
 } else {
 "other"
 }
} else {
 "other"
};

折叠 
//定义函数方法名,这里要根据c++库的函数名和参数来定义,函数名和参数类型务必要一致。
type LPCTSTR = *const WideChar;
type BOOL = c_int;
type INITPTR = *const i8;
type CANRST = *mut WideChar;

// 打开设备
type S2V7\_open = unsafe extern "system" fn() -> c_int;
// 关闭设备
type S2V7\_close = unsafe extern "system" fn() -> c_int;

 //【set mode 设置读证功能】
type S2V7\_set\_mode =
    unsafe extern "system" fn(flg_takeColor: c_int, flg_takeUV: c_int, flg_readChipInfo: c_int, flg_readChipFace: c_int) -> c_int; // Type = 0 即可

//【wait Doc. in 等待放卡】
type S2V7\_wait\_DocIn =
unsafe extern "system" fn(timeout: f64, flg_in: INITPTR) -> c_int; // Type = 0 即可


//【wait Doc. out 等待拿卡】
type S2V7\_wait\_DocOut =
unsafe extern "system" fn(timeout: f64, flg_out: INITPTR) -> c_int; // Type = 0 即可

 //【process 执行读卡过程】
type S2V7\_process = unsafe extern "system" fn() -> c_int;

 //读取卡类型
type S2V7\_get\_cardType = unsafe extern "system" fn() -> c_int;

//保存彩照
type S2V7\_VIS\_saveColor = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
//保存红外照
type S2V7\_VIS\_saveIR = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;

//【get MRZ text 获取OCR文字信息】
type S2V7\_VIS\_getMRZtext = unsafe extern "system" fn(text: LPCTSTR) -> c_int;

//show text information 文字信息
type S2V7\_RDO\_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
type S2V7\_VIS\_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;

type S2V7\_RF\_active = unsafe extern "system" fn(ante
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

[虚幻私塾】

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值