在使用Nodejs的addon导入自己编写的cpp的dll时出现的一系列问题记录
标签: __declspec
、Napi
、LoadLibraryA
、GetLastError
、dumpbin /exports
。
正常创建一个使用Napi的nodejs addon项目(网上都有,在这里不赘述),主要代码如下:
#include <napi.h>
#include <iostream>
#include <atlstr.h>
#include "Static_Function.h"
using namespace std;
//定义调用函数签名模板
typedef bool (__stdcall *DetectorInitializeFun)( );
//例如表示返回值是bool,参数列表的两个 int
typedef bool (__stdcall *DetectorInitializeFun2)(int,int);
Napi::Boolean DetectorInitialize_test(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();
//1、获取从js传入的参数(方便修改dll名称)
string dllPath = info[0].As<Napi::String>();
//2、通过LoadLibraryA载入(无法通过LoadLibrary,因为他的参数是字符串常量,而我的参数是从js传过来的)
HINSTANCE hinstLib = LoadLibraryA(dllPath.c_str());
DWORD error_id = GetLastError();
cout << "(1) dllPath:[" << dllPath << "] hinstLib:[" << hinstLib << "] error_id:[" << error_id << "]" << endl;
if (hinstLib != NULL){
//3、通过符号查找导出函数
DetectorInitializeFun ProcAddress = (DetectorInitializeFun)GetProcAddress(hinstLib, "DetectorInitialize");
error_id = GetLastError();
cout << "(2)" << " error_id:[" << error_id << "]" << endl;
if (ProcAddress != NULL){
cout << "(3)" << endl;
//4、调用导出函数
ProcAddress();
//5、释放导入的库
FreeLibrary(hinstLib);
return Napi::Boolean::New(env, true);
}
FreeLibrary(hinstLib);
}
cout << "(4)" << endl;
return Napi::Boolean::New(env, false);
}
Napi::Object Initialize(Napi::Env env, Napi::Object exports){
exports.Set(Napi::String::New(env, "DetectorInitialize_test"), Napi::Function::New(env, DetectorInitialize_test));
return exports;
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Initialize)
问题记录
- 1、无法通过
LoadlibraryA
导入dll
cout << "(1) dllPath:[" << dllPath << "] hinstLib:[" << hinstLib << "] error_id:[" << error_id << "]" << endl;
输出结果是 (1) dllPath:[DllTest.dll] hinstLib:[00000000] error_id:[193]
(在这里不赘述路径不正确情况)
通过查询原因是导入的DllTest.dll是64位,而当前项目是32位(当前nodejs是32位),解决办法:重新将dll编译为32位,或者切换nodejs为64位(注:切换之后需要重新npm install,保证你的node_modules都变成了64位)
.
.
- 2、成功载入DllTest_x86.dll,但是无法调用内部方法
可以看到我的代码中是这样调用的
DetectorInitializeFun ProcAddress = (DetectorInitializeFun)GetProcAddress(hinstLib, "DetectorInitialize");
通过查阅相关资料,需要使用dumpbin /exports TestDll_x86.dll查询dll导出的符号表,使用符号进行调用,而不是导出的函数名称
通过dumpbin,得到结果如下
Microsoft (R) COFF/PE Dumper Version 14.36.32532.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file .\DllTest_x86.dll
File Type: DLL
Section contains the following exports for DllTest.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 000111F9 ?DetectorInitialize@@YA_NXZ = @ILT+500(?DetectorInitialize@@YA_NXZ)
Summary
1000 .00cfg
1000 .data
1000 .idata
1000 .msvcjmc
3000 .rdata
1000 .reloc
1000 .rsrc
8000 .text
10000 .textbss
会发现其中一段0 000111F9 ?DetectorInitialize@@YA_NXZ = @ILT+500(?DetectorInitialize@@YA_NXZ)
,这就是被导出的符号列表,只有这个,才代表你的dll导出了函数供你调用
,符号是 ?DetectorInitialize@@YA_NXZ
,需要把代码改成:
DetectorInitializeFun ProcAddress = (DetectorInitializeFun)GetProcAddress(hinstLib, "?DetectorInitialize@@YA_NXZ");
最后在下面通过ProcAddress();
调用即可。
.
.
-
3、如何定义导入的函数签名与编写具备导出性质的dll
- 通过下面方式定义函数签名,用来表示导入的函数模板
//例如表示返回值是bool,参数列表的两个int typedef bool (__stdcall *DetectorInitializeFun2)(int,int);
- 通过如下方式可以定义导出dll的函数
下面的代码是visual studio自动生成的dll项目的头文件,需要在函数声明前使用
__declspec(dllexport)
来表示导出函数#ifndef PCH_H #define PCH_H // 添加要在此处预编译的标头 #include "framework.h" #ifdef DETECTOR_EXPORTS #define DETECTOR_API __declspec(dllexport) #else #define DETECTOR_API __declspec(dllimport) #endif DETECTOR_API bool DetectorInitialize(); #endif //PCH_H
实际上不需要按照上面的
DETECTOR_API bool DetectorInitialize();
来定义,可以用__declspec(dllexport)bool DetectorInitialize();
来定义。之所以使用宏定义是为了方便管理,而不用到处都是__declspec(dllexport)
。然后函数正常在.cpp
文件实现即可。
标签:__,Nodejs,dll,bool,DetectorInitialize,cpp,NXZ,1000 From: https://www.cnblogs.com/MCMonkey/p/17682498.html