关于使用rust调用c++静态库并编译nodejs包的问题

目录
  • 一、创建项目
  • 二、Cargo.toml
  • 三、package.json
  • 四、代码分析

在项目上经常要用到身份证阅读器、护照阅读仪、指纹仪等各种品牌硬件,假如每套系统的都做集成开发那代码的维护成本将变得很高,为此采用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<Library> = 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(antenna: c_int,atr: LPCTSTR, atr_len: c_int) -> c_int;
//构建函数实例
static V7_OPEN: OnceCell<Symbol<S2V7_open>> = OnceCell::new();
static V7_CLOSE: OnceCell<Symbol<S2V7_close>> = OnceCell::new();
static V7_SET_MODE: OnceCell<Symbol<S2V7_set_mode>> = OnceCell::new();
static V7_WAIT_DOCINT: OnceCell<Symbol<S2V7_wait_DocIn>> = OnceCell::new();
static V7_WAIT_DOCOUT: OnceCell<Symbol<S2V7_wait_DocOut>> = OnceCell::new();
static V7_PROCESS: OnceCell<Symbol<S2V7_process>> = OnceCell::new();
static V7_GET_CARDTYPE: OnceCell<Symbol<S2V7_get_cardType>> = OnceCell::new();
static V7_VIS_SAVECOLOR: OnceCell<Symbol<S2V7_VIS_saveColor>> = OnceCell::new();
static V7_VIS_SAVEIR: OnceCell<Symbol<S2V7_VIS_saveIR>> = OnceCell::new();
static V7_VIS_GETMRZTEXT: OnceCell<Symbol<S2V7_VIS_getMRZtext>> = OnceCell::new();
static V7_RDO_getBytesByIndex: OnceCell<Symbol<S2V7_RDO_getBytesByIndex>> = OnceCell::new();
static V7_VIS_getBytesByIndex: OnceCell<Symbol<S2V7_VIS_getBytesByIndex>> = OnceCell::new();
static V7_RF_active: OnceCell<Symbol<S2V7_RF_active>> = OnceCell::new();
// 对外导出函数方法
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
    cx.export_function("init", init_by_node)?;
    cx.export_function("start", start)?;
}
//加载dll并对函数进行初始化操作
pub fn init_by_node(mut cx: FunctionContext) -> JsResult<JsNumber> {
	//外部传进来的参数(根据自己的需要来定义)
    let directory = cx.argument::<JsString>(0)?.value(&mut cx);
    let userid = cx.argument::<JsString>(1)?.value(&mut cx);
    unsafe {
        DIRECTORY_PATH.take();
        DIRECTORY_PATH.set(directory).unwrap();
        USER_ID.take();
        USER_ID.set(userid).unwrap();
    };
    let result = init() as f64;
    Ok(cx.number(result))
}
//核心代码,加载dll函数并映射
fn init() -> c_int {
    let directory = unsafe { DIRECTORY_PATH.get().unwrap() };
    let userid = unsafe { USER_ID.get().unwrap() };
    let directory_path = std::path::Path::new(directory).join(MACHINE_KIND);
    if directory_path.exists() {
        let dll_path = directory_path.join(libloading::library_filename("STAR200_V7_DRV"));
        println!("dll_path: {:?}", dll_path);
        if dll_path.exists() {
            match init_dll(dll_path.to_str().unwrap()).is_ok() {
                true => {
                    // 打开设备
                    let init_result = unsafe {V7_OPEN.get_unchecked()()};
                    if init_result == 0 {
                        println!("设备打开成功");
                        return ResultType::Success as c_int;
                    } else {
                        println!("设备打开失败,代码:{:?}",init_result);
                        return ResultType::DeviceNotFound as c_int;
                    }
                }
                false => {
                    return ResultType::INITDLLFail as c_int;
                }
            }
        } else {
            return ResultType::DllPathNotExist as c_int;
        }
    } else {
        println!("{:?}", directory_path);
        return ResultType::DirectoryPathNotExist as c_int;
    }
}
// 加载dll
fn init_dll(dll_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
    unsafe {
        if INITDLL {
            return Ok(true);
        }
    }
    println!("加载dll");
    println!("dll_path");
    let library = LIBRARY.get_or_init(|| unsafe { Library::new(dll_path).unwrap() });
    println!("S2V7_open");
    V7_OPEN.get_or_init(|| unsafe { library.get::<S2V7_open>(b"S2V7_open").unwrap() });
    println!("S2V7_close");
    V7_CLOSE.get_or_init(|| unsafe { library.get::<S2V7_close>(b"S2V7_close").unwrap() });
    println!("S2V7_set_mode");
    V7_SET_MODE.get_or_init(|| unsafe {library.get::<S2V7_set_mode>(b"S2V7_set_mode").unwrap()});
    println!("S2V7_wait_DocIn");
    V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocIn>(b"S2V7_wait_DocIn").unwrap() });
    println!("S2V7_wait_DocOut");
    V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocOut>(b"S2V7_wait_DocOut").unwrap() });
    V7_PROCESS.get_or_init(|| unsafe { library.get::<S2V7_process>(b"S2V7_process").unwrap() });
    V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::<S2V7_get_cardType>(b"S2V7_get_cardType").unwrap() });
    V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveColor>(b"S2V7_VIS_saveColor").unwrap() });
    V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveIR>(b"S2V7_VIS_saveIR").unwrap() });
    V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::<S2V7_VIS_getMRZtext>(b"S2V7_VIS_getMRZtext").unwrap() });
    V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_RDO_getBytesByIndex>(b"S2V7_RDO_getBytesByIndex").unwrap() });
    V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_VIS_getBytesByIndex>(b"S2V7_VIS_getBytesByIndex").unwrap() });
    V7_RF_active.get_or_init(|| unsafe { library.get::<S2V7_RF_active>(b"S2V7_RF_active").unwrap() });
    unsafe {
        INITDLL = true;
    }
    Ok(true)
}
//创建新线程来监测设备读证操作
fn start(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
    let mut channel = cx.channel();
    channel.reference(&mut cx);
    println!("start {}", channel.has_ref());
    let index = unsafe {
        DEVICE_START_INDEX += 1;
        DEVICE_START_INDEX
    };
    std::thread::spawn(move || {
        // Do the heavy lifting inside the background thread.
        device_start(callback, channel, index);
    });
    Ok(cx.undefined())
}
use std::sync::{Arc, Mutex};
fn device_start(callback: Root<JsFunction>, channel: Channel, index: u64) {
    let index = index;
    let callback = Arc::new(Mutex::new(callback));
    //设置读证功能
    unsafe { V7_SET_MODE.get_unchecked()(1,1,1,1) };
    loop {
        if index != unsafe { DEVICE_START_INDEX } {
            break;
        };
        let callback_clone = Arc::clone(&callback);
        let mut result = RecogIDCardEXResult::default();
        let mut flg_in:i8=0;
        match unsafe { V7_WAIT_DOCINT.get_unchecked()(5.0,&mut flg_in) } {
            // 设备正常 检测是否有放入证件
            0 => {
                if flg_in==0{
                    //检查是否放入超时
                    result.record_type = ResultType::CheckCardNotInOrOut as i32;
                    break;
                }
                result.device_online = true;
                result.device_name =unsafe { DEVCIE_NAME.get_or_init(|| "".to_string()).to_string() };

                match unsafe { V7_PROCESS.get_unchecked()() } {
                    // 证件有放入
                    0 => {
                        result.record_type = ResultType::CheckCardInput as i32;
                    }
                    // 未检测到OCR区域
                    -1 => {
                        result.record_type = ResultType::OCRFail as i32;
                    }
                    // 设备离线
                    -3 => {
                        result.device_online = false;
                        result.record_type = init();
                    }
                    _ => {
                        result.record_type = ResultType::Unknown as i32;
                    }
                }
            }
            -3 => {
                //设备离线
                let init = init();
                result.device_online = false;
                result.record_type = init;
            }
            _ => {
                //未知错误
                result.record_type = ResultType::Unknown as i32;
            }
        };
        if unsafe { *NEED_RECORD.get_or_init(|| false) } {
            println!("手工点击识别+1");
            result.record_type = ResultType::CheckCardInput as i32;
        }
        // let time_now = std::time::Instant::now();
        if result.record_type == ResultType::CheckCardInput as i32 {
            let _result = recog_card();
            result.success = _result.success;
            result.img_base64 = _result.img_base64;
            result.reg_info = _result.reg_info;
            result.card_type = _result.card_type;
            result.card_name = _result.card_name;
        }
        let neet_sendinfo = if Some(true) == unsafe { NEED_RECORD.take() } {
            true
        } else {
            false
        };
        // let elapsed = time_now.elapsed();
        // println!("识别时间结束时间 {:.6?}", elapsed);
        if result.record_type != ResultType::CheckCardNotInOrOut as i32
            && (*unsafe { RESULT_TYPE.get_or_init(|| -10000) } != result.record_type
                || result.record_type == ResultType::CheckCardInput as i32
                || neet_sendinfo)
        {
            unsafe {
                RESULT_TYPE.take();
                RESULT_TYPE.set(result.record_type).unwrap();
            }
            channel.send(move |mut cx| {
                let result_json = serde_json::to_string(&result).unwrap();
                let callback = callback_clone.lock().unwrap().to_inner(&mut cx);
                let this = cx.undefined();
                let args = vec![cx.string(&result_json)];
                callback.call(&mut cx, this, args)?;
                Ok(())
            });
        }
        std::thread::sleep(std::time::Duration::from_millis(20));
    }
}

完整源码

use std::collections::HashMap;
use libc::{c_int, c_void};
use libloading::{Library, Symbol};
use neon::prelude::*;
use once_cell::sync::OnceCell;
use serde::Serialize;
extern crate encoding;
use encoding::all::GB18030;
use encoding::{DecoderTrap,EncoderTrap,Encoding};
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<Library> = 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"
};
//设置识别的证件 ID
// 设置当前要识别的证件类型,并将
// 之前已经设置的证件类型清除。
// nMainID 主要识别类型,nSubID 子类型
//  nSubID 头指针,默认将数组
// nSubID 第 一 个 元 素 赋 值 为 0 即
// nSubID[0]=0
// type S = c_int[];
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(antenna: c_int,atr: LPCTSTR, atr_len: c_int) -> c_int;
static V7_OPEN: OnceCell<Symbol<S2V7_open>> = OnceCell::new();
static V7_CLOSE: OnceCell<Symbol<S2V7_close>> = OnceCell::new();
static V7_SET_MODE: OnceCell<Symbol<S2V7_set_mode>> = OnceCell::new();
static V7_WAIT_DOCINT: OnceCell<Symbol<S2V7_wait_DocIn>> = OnceCell::new();
static V7_WAIT_DOCOUT: OnceCell<Symbol<S2V7_wait_DocOut>> = OnceCell::new();
static V7_PROCESS: OnceCell<Symbol<S2V7_process>> = OnceCell::new();
static V7_GET_CARDTYPE: OnceCell<Symbol<S2V7_get_cardType>> = OnceCell::new();
static V7_VIS_SAVECOLOR: OnceCell<Symbol<S2V7_VIS_saveColor>> = OnceCell::new();
static V7_VIS_SAVEIR: OnceCell<Symbol<S2V7_VIS_saveIR>> = OnceCell::new();
static V7_VIS_GETMRZTEXT: OnceCell<Symbol<S2V7_VIS_getMRZtext>> = OnceCell::new();
static V7_RDO_getBytesByIndex: OnceCell<Symbol<S2V7_RDO_getBytesByIndex>> = OnceCell::new();
static V7_VIS_getBytesByIndex: OnceCell<Symbol<S2V7_VIS_getBytesByIndex>> = OnceCell::new();
static V7_RF_active: OnceCell<Symbol<S2V7_RF_active>> = OnceCell::new();
// static
static mut INITDLL: bool = false;
static mut DEVICE_START_INDEX: u64 = 0;
static mut DIRECTORY_PATH: OnceCell<String> = OnceCell::new();
static mut USER_ID: OnceCell<String> = OnceCell::new();
static mut DEVCIE_NAME: OnceCell<String> = OnceCell::new();
static mut RESULT_TYPE: OnceCell<i32> = OnceCell::new();
static mut NEED_RECORD: OnceCell<bool> = OnceCell::new();
// 初始化dll
fn init_dll(dll_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
    unsafe {
        if INITDLL {
            return Ok(true);
        }
    }
    println!("加载dll");
    println!("dll_path");
    let library = LIBRARY.get_or_init(|| unsafe { Library::new(dll_path).unwrap() });
    println!("S2V7_open");
    V7_OPEN.get_or_init(|| unsafe { library.get::<S2V7_open>(b"S2V7_open").unwrap() });
    println!("S2V7_close");
    V7_CLOSE.get_or_init(|| unsafe { library.get::<S2V7_close>(b"S2V7_close").unwrap() });
    println!("S2V7_set_mode");
    V7_SET_MODE.get_or_init(|| unsafe {library.get::<S2V7_set_mode>(b"S2V7_set_mode").unwrap()});
    println!("S2V7_wait_DocIn");
    V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocIn>(b"S2V7_wait_DocIn").unwrap() });
    println!("S2V7_wait_DocOut");
    V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocOut>(b"S2V7_wait_DocOut").unwrap() });
    V7_PROCESS.get_or_init(|| unsafe { library.get::<S2V7_process>(b"S2V7_process").unwrap() });
    V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::<S2V7_get_cardType>(b"S2V7_get_cardType").unwrap() });
    V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveColor>(b"S2V7_VIS_saveColor").unwrap() });
    V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveIR>(b"S2V7_VIS_saveIR").unwrap() });
    V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::<S2V7_VIS_getMRZtext>(b"S2V7_VIS_getMRZtext").unwrap() });
    V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_RDO_getBytesByIndex>(b"S2V7_RDO_getBytesByIndex").unwrap() });
    V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_VIS_getBytesByIndex>(b"S2V7_VIS_getBytesByIndex").unwrap() });
    V7_RF_active.get_or_init(|| unsafe { library.get::<S2V7_RF_active>(b"S2V7_RF_active").unwrap() });
    unsafe {
        INITDLL = true;
    }
    Ok(true)
}
fn init() -> c_int {
    let directory = unsafe { DIRECTORY_PATH.get().unwrap() };
    let userid = unsafe { USER_ID.get().unwrap() };
    let directory_path = std::path::Path::new(directory).join(MACHINE_KIND);
    if directory_path.exists() {
        let dll_path = directory_path.join(libloading::library_filename("STAR200_V7_DRV"));
        println!("dll_path: {:?}", dll_path);
        if dll_path.exists() {
            match init_dll(dll_path.to_str().unwrap()).is_ok() {
                true => {
                    // 打开设备
                    let init_result = unsafe {V7_OPEN.get_unchecked()()};
                    if init_result == 0 {
                        println!("设备打开成功");
                        return ResultType::Success as c_int;
                    } else {
                        println!("设备打开失败,代码:{:?}",init_result);
                        return ResultType::DeviceNotFound as c_int;
                    }
                }
                false => {
                    return ResultType::INITDLLFail as c_int;
                }
            }
        } else {
            return ResultType::DllPathNotExist as c_int;
        }
    } else {
        println!("{:?}", directory_path);
        return ResultType::DirectoryPathNotExist as c_int;
    }
}
pub fn init_by_node(mut cx: FunctionContext) -> JsResult<JsNumber> {
    let directory = cx.argument::<JsString>(0)?.value(&mut cx);
    let userid = cx.argument::<JsString>(1)?.value(&mut cx);
    unsafe {
        DIRECTORY_PATH.take();
        DIRECTORY_PATH.set(directory).unwrap();
        USER_ID.take();
        USER_ID.set(userid).unwrap();
    };
    let result = init() as f64;
    Ok(cx.number(result))
}
#[allow(dead_code)] // 允许dead_code
enum ResultType {
    DirectoryPathNotExist = -2003, // 找不到运行目录
    DllPathNotExist = -2001,       // 找不到dll文件
    INITDLLFail = -2000,           // 初始化dll
    Success = 0,                   // 成功
    UserIdFail = 2001,             //用户 ID 错误
    DeviceInitFail = 2002,         //  设备初始化失败
    DeviceKernelInitFail = 2003,   // 初始化核心失败
    DeviceDatInitFail = 2004,      //未找到授权文件
    DeviceNotInit = 2101,          // 设备未初始化
    DeviceNotFound = 2102,         // 没有找到设备
    DeviceReConnect = 2103,        // 重新连接设备
    Unknown = -100,                // 未知错误
    CheckCardInput = 3001,         // 证件放入设备
    CheckCardOut = 3002,           // 证件移出设备
    CheckCardNotInOrOut = 3000,    // 证件无放入或拿出
    CheckCardBarCode = 3003,       // 检测到手机条码
    OCRFail=-1,                    // 未检测到OCR区域
}
type RecogIDCardEXResultItem = HashMap<i32, [String; 2]>;
#[derive(Default, Serialize)]
pub struct RecogIDCardEXResultObject {
    pub viz_result: RecogIDCardEXResultItem,
    pub viz_orc_result: RecogIDCardEXResultItem,
    pub mrz_result: RecogIDCardEXResultItem,
    pub mrz_ocr_result: RecogIDCardEXResultItem,
    pub chip_result: RecogIDCardEXResultItem,
}
#[derive(Default, Serialize)]
pub struct RecogIDCardEXResult {
    pub device_name: String,
    pub device_online: bool,
    pub reg_info: RecogIDCardEXResultObject,
    pub img_base64: HashMap<String, String>,
    pub card_type: i32,
    pub record_type: i32,
    pub card_name: String,
    pub success: bool, // 识别是否成功
}
static SAVE_IMAGE_REUSLT_NAME: [&str; 5] = [
    "tempHeadEC.jpg",
    "tempHead.jpg",
    "tempUV.jpg",
    "tempIR.jpg",
    "temp.jpg",
];
fn start(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
    let mut channel = cx.channel();
    channel.reference(&mut cx);
    println!("start {}", channel.has_ref());
    let index = unsafe {
        DEVICE_START_INDEX += 1;
        DEVICE_START_INDEX
    };
    std::thread::spawn(move || {
        // Do the heavy lifting inside the background thread.
        device_start(callback, channel, index);
    });
    Ok(cx.undefined())
}
use std::sync::{Arc, Mutex};
fn device_start(callback: Root<JsFunction>, channel: Channel, index: u64) {
    let index = index;
    let callback = Arc::new(Mutex::new(callback));
    //设置读证功能
    unsafe { V7_SET_MODE.get_unchecked()(1,1,1,1) };
    loop {
        if index != unsafe { DEVICE_START_INDEX } {
            break;
        };
        let callback_clone = Arc::clone(&callback);
        let mut result = RecogIDCardEXResult::default();
        let mut flg_in:i8=0;
        match unsafe { V7_WAIT_DOCINT.get_unchecked()(5.0,&mut flg_in) } {
            // 设备正常 检测是否有放入证件
            0 => {
                if flg_in==0{
                    //检查是否放入超时
                    result.record_type = ResultType::CheckCardNotInOrOut as i32;
                    break;
                }
                result.device_online = true;
                result.device_name =unsafe { DEVCIE_NAME.get_or_init(|| "".to_string()).to_string() };

                match unsafe { V7_PROCESS.get_unchecked()() } {
                    // 证件有放入
                    0 => {
                        result.record_type = ResultType::CheckCardInput as i32;
                    }
                    // 未检测到OCR区域
                    -1 => {
                        result.record_type = ResultType::OCRFail as i32;
                    }
                    // 未找到非接卡
                    // v if v/10 == -21 => {
                    //     result.record_type = ResultType::OCRFail as i32;
                    // }
                    // 设备离线
                    -3 => {
                        result.device_online = false;
                        result.record_type = init();
                    }
                    _ => {
                        result.record_type = ResultType::Unknown as i32;
                    }
                }
            }
            -3 => {
                //设备离线
                let init = init();
                result.device_online = false;
                result.record_type = init;
            }
            _ => {
                //未知错误
                result.record_type = ResultType::Unknown as i32;
            }
        };
        if unsafe { *NEED_RECORD.get_or_init(|| false) } {
            println!("手工点击识别+1");
            result.record_type = ResultType::CheckCardInput as i32;
        }
        // let time_now = std::time::Instant::now();
        if result.record_type == ResultType::CheckCardInput as i32 {
            let _result = recog_card();
            result.success = _result.success;
            result.img_base64 = _result.img_base64;
            result.reg_info = _result.reg_info;
            result.card_type = _result.card_type;
            result.card_name = _result.card_name;
        }
        let neet_sendinfo = if Some(true) == unsafe { NEED_RECORD.take() } {
            true
        } else {
            false
        };
        // let elapsed = time_now.elapsed();
        // println!("识别时间结束时间 {:.6?}", elapsed);
        if result.record_type != ResultType::CheckCardNotInOrOut as i32
            && (*unsafe { RESULT_TYPE.get_or_init(|| -10000) } != result.record_type
                || result.record_type == ResultType::CheckCardInput as i32
                || neet_sendinfo)
        {
            unsafe {
                RESULT_TYPE.take();
                RESULT_TYPE.set(result.record_type).unwrap();
            }
            channel.send(move |mut cx| {
                let result_json = serde_json::to_string(&result).unwrap();
                let callback = callback_clone.lock().unwrap().to_inner(&mut cx);
                let this = cx.undefined();
                let args = vec![cx.string(&result_json)];
                callback.call(&mut cx, this, args)?;
                Ok(())
            });
        }
        std::thread::sleep(std::time::Duration::from_millis(20));
    }
}
// 白光图、红外
// 图、紫外图、版面头像和芯片头像
pub fn recog_card() -> RecogIDCardEXResult {
    let time_now = std::time::Instant::now();
    let mut result = RecogIDCardEXResult::default();
    result.device_online = true;
    let img_path_directory = std::path::Path::new(unsafe { DIRECTORY_PATH.get().unwrap() });
    let ir_img_path = img_path_directory.join("ir.jpg");
    let color_img_path = img_path_directory.join("color.jpg");
    //显示红外照
    let irResult = unsafe {V7_VIS_SAVEIR.get_unchecked()(encode(ir_img_path.to_str().unwrap()).as_ptr() as LPCTSTR)};
    //显示彩照
    let colorResult = unsafe {V7_VIS_SAVECOLOR.get_unchecked()(encode(color_img_path.to_str().unwrap()).as_ptr() as LPCTSTR)};
    if irResult==0{
        if ir_img_path.exists() {
            match std::fs::read(&ir_img_path) {
                Ok(image_data) => {
                    println!("读取照片成功");
                    let image_data = base64::encode(&image_data);
                    let base64_string = String::from("data:image/jpg;base64,");
                    std::fs::remove_file(ir_img_path).unwrap();
                    result.img_base64.insert("0".to_string(), base64_string + &image_data);
                }
                Err(e) => {
                    println!("读取照片抛异常");
                    println!(
                        "{:?} {:?}",
                        e,
                        "ir.jpg",
                    );
                }
            };
        }
    }
    if colorResult==0{
        if color_img_path.exists() {
            match std::fs::read(&color_img_path) {
                Ok(image_data) => {
                    println!("读取照片成功");
                    let image_data = base64::encode(&image_data);
                    let base64_string = String::from("data:image/jpg;base64,");
                    std::fs::remove_file(color_img_path).unwrap();
                    result.img_base64.insert("1".to_string(), base64_string + &image_data);
                }
                Err(e) => {
                    println!("读取照片抛异常");
                    println!(
                        "{:?} {:?}",
                        e,
                        "color.jpg",
                    );
                }
            };
        }
    }
    let mut ocritem = RecogIDCardEXResultObject::default();
    let mut index: c_int = 0;
    //orc识别文字
    let mut mrztext = [0; 1024];
    let x = unsafe {V7_VIS_GETMRZTEXT.get_unchecked()(mrztext.as_mut_ptr())};
    if x==0{
        let result_item = ["MRZ".to_string(), decode(&mrztext)];
        ocritem.mrz_result.insert(index, result_item);
        index+=1;
    }
    let mut data:[u16; 256] = [0; 256];
    let mut len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(0,data.as_mut_ptr())};
    if len>0{
        ocritem.mrz_result.insert(index, ["编号".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(1,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["国籍".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(2,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["民族".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(3,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["姓名".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(4,data.as_mut_ptr())};
        let cardType= unsafe {V7_GET_CARDTYPE.get_unchecked()()};
        if cardType==101{
            //身份证是UTF8格式
            ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
        }else{
            ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
            // //中国护照的中文姓名 是GBK编码的
            // let name=GB18030.decode(&data, DecoderTrap::Strict).unwrap();
            // ocritem.mrz_result.insert(index, ["中文名".to_string(), name.replace("\u{0}","")]);
        }
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(5,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["性别".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(6,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["出生日期".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(7,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["地址".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(8,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["签发机关".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(9,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["有效期始".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(10,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["有效期止".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(20,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["港澳台ID".to_string(), decode(&data)]);
        index+=1;
    }
    else{
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(0,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["编号".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(1,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["国籍".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(2,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["民族".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(3,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["姓名".to_string(), decode(&data)]);
        index+=1;

        //中国护照的中文姓名 是GBK编码的, 身份证不会执行到这里
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(4,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
        // let name=GB18030.decode(&data, DecoderTrap::Strict).unwrap();
        // ocritem.mrz_result.insert(index, ["中文名".to_string(), name.replace("\u{0}","")]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(5,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["性别".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(6,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["出生日期".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(7,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["地址".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(8,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["签发机关".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(9,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["有效期始".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(10,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["有效期止".to_string(), decode(&data)]);
        index+=1;
        len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(20,data.as_mut_ptr())};
        ocritem.mrz_result.insert(index, ["港澳台ID".to_string(), decode(&data)]);
        index+=1;
    }

    result.reg_info=ocritem;
    result.success = true;
    result.card_type = unsafe {V7_GET_CARDTYPE.get_unchecked()()};
    let elapsed = time_now.elapsed();
    println!("{:.6?}", elapsed);
    return result;
}
pub fn regcord_by_node(mut cx: FunctionContext) -> JsResult<JsNull> {
    println!("regcord_by_node");
    unsafe {
        NEED_RECORD.take();
        NEED_RECORD.set(true).unwrap();
    };
    Ok(cx.null())
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
    cx.export_function("init", init_by_node)?;
    cx.export_function("start", start)?;
    cx.export_function("regcord_by_node", regcord_by_node)?;
    Ok(())
}

到此这篇关于使用rust调用c++静态库并编译nodejs包的文章就介绍到这了,更多相关rust调用c++静态库内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何正确使用Nodejs 的 c++ module 链接到 OpenSSL

    事情的起因是这样的, 因为某些原因, 最近在写 Nodejs 的 c++ module, 然后在js这边调用.  网络通信自然离不开ssl, 于是需要链接到Openssl的库. 我们本来的期望是,需要用户安装有Openssl的运行库, 然后我们的c++ module 动态链接到Openssl的so库上来运行. 起初一切看起来还不错,直到我们发现这个openssl的函数不能工作: PKCS7_sign() PKCS7_sign ( ) 我们发现: 如果我们的 c++ 模块与Openssl库动态链接

  • golang、python、php、c++、c、java、Nodejs性能对比

    本人在PHP/C++/Go/Py时,突发奇想,想把最近主流的编程语言性能作个简单的比较, 至于怎么比,还是不得不用神奇的斐波那契算法.可能是比较常用或好玩吧. 好了,talk is cheap, show me your code! 打开Mac,点开Clion开始Coding吧! 1.怎么第一是Go呢,因为我个人最近正在用,感觉很不错 package main import "fmt" func main(){ fmt.Println(fibonacci(34)) } func fib

  • C++ Cmake的构建静态库和动态库详解

    目录 静态库和动态库的区别 构建示例 ADD_LIBRARY 同时构建静态和动态库 SET_TARGET_PROPERTIES 动态库的版本号 安装共享库和头文件 使用外部共享库和头文件 解决 :make后头文件找不到的问题 解决:找到引用的函数问题 特殊的环境变量CMAKE_INCLUDE_PATH和CMAKE_LIBRARY_PATH 总结 静态库和动态库的区别 1.静态库的扩展名一般为".a"或者".lib":动态库的扩展名一般为".so"

  • C++的替代:微软如何使用rust?

    微软拥有世界上最大的C/C++代码库之一.从Windows.Office到Azure云,微软的所有核心产品都在该代码库上运行.但因为C++不是内存安全的语言,代码库中自然频频出现内存漏洞,大量的时间被耗费在修补漏洞上. 微软自去年开始寻找用以替代的编程语言来解决内存安全问题,寻找终有所得--微软开始尝试使用Rust,并在一些情境下将其集成进代码库中.Rust是一种相对较新的编程语言,具有与C和C++相同的底层性能,并具备现代编程语言应有的功能集. 微软认为Rust颇具潜力,本文就将介绍微软将其用

  • 关于使用rust调用c++静态库并编译nodejs包的问题

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

  • go语言静态库的编译和使用方法

    本文主要介绍go语言静态库的编译和使用方法,以windows平台为例,linux平台步骤一样,具体环境如下: >echo %GOPATH% E:\share\git\go_practice\ >echo %GOROOT% C:\Go\ >tree /F %GOPATH%\src 卷 work 的文件夹 PATH 列表 卷序列号为 0009-D8C8 E:\SHARE\GIT\GO_PRACTICE\SRC │ main.go │ └─demo demo.go 在%GOPATH%\src目

  • makefile如何调用静态库的方法实现

    makefile如何调用静态库 1.在windows下 g++环境中,调用一个python的静态链接库,在makefile文件有一段是全局的g++命令: $(PROG): $(OBJS) $(CXX) $(LFLAGS) $^ -o $@ 现在我要链接上一个python的静态库,lib存储在makefile同目录下libs文件夹下: $(PROG): $(OBJS) $(CXX) $(LFLAGS) $^ -o $@ ./libs/libpython37.a 2.在Linux Ubuntu 18

  • c/c++静态库之间相互调用的实战案例

    目录 一.c++项目如何调用c的库(以静态库为例) 1.将栈的源文件和头文件拷贝一份到Stack_c.lib这个项目的路径下 2.打包成静态库 3.建立一个c++项目(TestStacklib) 引入c项目中的头文件 4.设置附加库和附属关系 二.c项目如何调用c++的库(静态库为例) 1.建立c++项目(Stack_cpp) 并且将栈的源文件和头文件包含在该目录 将其打包为cpp的静态库 2.建立c项目 3.包含静态库的头文件 4 .添加打包的cpp动态库到附加库目录 设置依赖项 总结 本文主

  • iOS创建与使用静态库

    在日常项目开发中,不论是为了两个公司项目上的业务交流还是为了减少项目的编译时间,有的时候我们会把项目中的私密内容打包成静态库,或者是把项目中变动较少一部分打包成静态库以便提高编译效率,那么下面我们就来学习一下"iOS-静态库的创建与使用": (一)iOS静态库.动态库与Framework静态库与动态库的区别 (1)什么是库? 库(Library)直白一点说就是一段编译好的二进制代码,加上头文件就可以供别人使用;(例如: iOS中Objective-C编译下的.h和.m文件,打包静态库后

  • Flutter iOS开发OC混编Swift动态库和静态库问题填坑

    目录 引言 OC接入Swift 插件 静态库和 Framework 区别 新的问题: non-modular heade 不能在Framework Module中使用非Modular 的 Header 引言 Flutter 在 iOS 上的编译问题相信大家多多少少遇到过,不知道大家在搜索这方便的问题时,得到的答案是不是让你 clean 或者 install 多几次,很多时候就算解决完问题,也是处于薛定谔的状态,所以本篇也简单记录下 Flutter 开发中,OC 混编 Swift 遭遇动态库和静态

  • 动态库调用静态库示例讲解

    生成动态库: 需要的目标文件得用-fPIC选项生成. 而静态库所需的目标文件可以不用-fPIC选项. 例: 复制代码 代码如下: /////// static.h void static_print(); ///////static.cpp #include <iostream> #include "static.h" void static_print() { std::cout<<"This is static_print function&quo

  • Linux静态库与动态库实例详解

    Linux静态库与动态库实例详解 1. Linux 下静态链接库编译与使用 首先编写如下代码: // main.c #include "test.h" int main(){ test(); return 0; } // test.h #include<iostream> using namespace std; void test(); // test.c #include "test.h" void test(){ cout<< &quo

  • 深入探讨Linux静态库与动态库的详解(一看就懂)

    库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.库分静态库和动态库两种. 一.静态库和动态库的区别1. 静态函数库这类库的名字一般是libxxx.a:利用静态函数库编译成的文件比较大--空间,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了.当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译.2. 动态函数库这类库的名字一般是libxxx.so;相对于静态

随机推荐