在之前的一篇文章中,介绍了编写NodeJS AddOn的三种方式,N-API已经是AddOn的未来,N-API摆脱了Node各个大版本引用的chrominum V8不一致导致的API修改的问题。在之前的文章中介绍了N-API是Node team自己维护的,N-API内部会做V8中api匹配的问题。下面用一个例子来学习写一个N-API来调用已经存在的dll.
1. 写一个dll
利用机器上安装的vs2017(各个版本都可以),创建一个c++的动态链接库工程。
在工程里创建一个接口文件InterfaceHeader.h
#pragma once
class InterfaceHeader
{
public:
virtual ~InterfaceHeader() = default;
public:
virtual void SetMessage(char const *&& pMsg) = 0;
virtual char* PrintMessage() = 0;
};
在动态链接库工程里面创建一个类(ExportHelloWorld)实现这个接口,例如如下声明:
#pragma once
#include "InterfaceHeader.h"
#ifdef EXPORT_API
#define EXPORT_CLASS __declspec(dllexport)
#else
#define EXPORT_CLASS __declspec(dllimport)
#endif
class ExportHelloWorld: public InterfaceHeader
{
public:
ExportHelloWorld();
ExportHelloWorld(const char* msg);
virtual ~ExportHelloWorld();
private:
char m_Msg[MAX_PATH];
public:
void SetMessage( const char *&& pMsg) override;
char* PrintMessage() override;
};
创建输出API函数声明
extern "C" {
EXPORT_CLASS InterfaceHeader* CreateHeader();
EXPORT_CLASS void InvokeHeader();
}
在cpp文件里面实现API和相关的类(ExportHelloWorld),
#include "pch.h"
#include "ExportHelloWorld.h"
ExportHelloWorld::ExportHelloWorld(): m_Msg{0}
{
}
ExportHelloWorld::ExportHelloWorld(const char* msg)
{
strcpy_s(m_Msg, msg);
}
ExportHelloWorld::~ExportHelloWorld()
{
m_Msg[0] = 0;
}
void ExportHelloWorld::SetMessage(char const *&& pMsg)
{
if (strlen(pMsg) > sizeof(m_Msg))
memcpy(m_Msg, pMsg, sizeof(m_Msg) - 1);
else
strcpy_s(m_Msg, pMsg);
}
char* ExportHelloWorld::PrintMessage()
{
return m_Msg;
}
InterfaceHeader* g_header = nullptr;
InterfaceHeader* CreateHeader()
{
return new ExportHelloWorld();
}
void InvokeHeader()
{
if (!!g_header)
g_header->PrintMessage();
}
编译,生成动态链接库。
2. 撰写N-API add on module
2.1 写code调用刚才产生的dll.
#include "pch.h"
#include <node_api.h>
#include "Common.h"
#include <cstring>
#include <thread>
#include <Windows.h>
#include <WinBase.h>
#include <tchar.h>
#include "../Extention.dll/InterfaceHeader.h" // we can remove the include_dirs information in binding.gyp
//#include "InterfaceHeader.h" // we should add include_dirs information in binding.gyp
#