# 🚀node原生插件addon的编写

📅 2023/6/8

🙈由于我本身对c语言及c++语言并不熟悉,因此只能简单的做个入门笔记。

addon插件官方示例 (opens new window)
node-addon-api module (opens new window)
在官方示例中可能会有以下几种不同的代码

  • nan:Node 和直接 V8 API 之间基于 C++ 的抽象。
  • Node-API:基于 C 的 API,保证跨不同节点版本和 JavaScript 引擎的 ABI 稳定性。 (Node-API 以前称为 N-API。)
  • node-addon-api: header-only C++ 包装类,简化了基于 C 的 Node-API 的使用。
  • node-addon-api-addon-class:类似于 node-addon-api,但派生自 Napi::Addon 类。 1_hello_world 提供了一个例子。
    我使用的为node-addon-api版本,编写了一个可以根据窗口名置顶窗口的addon。

# 安装编译addon所需依赖

npm i node-addon-api bindings

编译同时需要python环境,我目前的环境为python 3.11.1与node 16.20.0 可以正常编译。

# 包含文件

  • binding.gyp:是一个用于描述 C++ 扩展的配置文件,它可以让你指定编译器、编译选项、源文件等等。由一个 JSON 对象组成,包含一个或多个 target。每个 target 描述了一个 C++ 扩展。常用的属性:target_name: 扩展的名称。 sources: 扩展的源文件。 include_dirs: 头文件的路径。 libraries: 需要链接的库。 conditions: 条件编译。
  • TopWin.cc:用于实现置顶功能的c++文件
  • index.js:调用编译好的addon的测试文件。
  • package.json

# 文件内容

🗃️binding.gyp

{
  "targets": [
    {
      "target_name": "TopWin",
      "cflags!": [ "-fno-exceptions" ],
      "cflags_cc!": [ "-fno-exceptions" ],
      "sources": [ "TopWin.cc" ],
      "target_arch": "ia32",
      "include_dirs": [
        "<!@(node -p \"require('node-addon-api').include\")"
      ],
      'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
    }
  ]
}

🗃️TopWin.cc

// node-addon-api
#include <napi.h>
#include <windows.h>
Napi::Boolean TopWin(const Napi::CallbackInfo &info)
{
  Napi::Env env = info.Env();

  int parame_length = info.Length();
  std::string title_name = info[0].As<Napi::String>().Utf8Value();
  HWND hwnd = FindWindow(NULL, title_name.c_str());

  if (parame_length == 2)
  {

    boolean is_top = info[1].As<Napi::Boolean>();

    if (is_top)
    {
      SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    }
    else
    {
      SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    }
  }
  else if (parame_length == 4)
  {
    boolean is_position = info[3].As<Napi::Boolean>();
    if (is_position)
    {
      int position_x = info[1].As<Napi::Number>().Int32Value();
      int position_y = info[2].As<Napi::Number>().Int32Value();
      SetWindowPos(hwnd, HWND_TOPMOST, position_x, position_y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
    }
    else
    {
      int width = info[1].As<Napi::Number>().Int32Value();
      int height = info[2].As<Napi::Number>().Int32Value();
      SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, width, height, SWP_NOZORDER | SWP_NOMOVE);
    }
  }
  else if (parame_length == 5)
  {
    int position_x = info[1].As<Napi::Number>().Int32Value();
    int position_y = info[2].As<Napi::Number>().Int32Value();
    int width = info[3].As<Napi::Number>().Int32Value();
    int height = info[4].As<Napi::Number>().Int32Value();
    SetWindowPos(hwnd, HWND_TOPMOST, position_x, position_y, width, height, SWP_NOZORDER);
  }
  return Napi::Boolean::New(env, true);
}

Napi::Object Init(Napi::Env env, Napi::Object exports)
{
  exports.Set(Napi::String::New(env, "TopWin"), Napi::Function::New(env, TopWin));
  return exports;
}

NODE_API_MODULE(TopWin, Init)

🗃️package.json

{
  //build为编译TopWin.cc的命令
  ...
  "scripts": {
    "build": "node-gyp configure && node-gyp build"
  },
  ...
}

🗃️index.js

const top = require('你编译好的.node文件路径')
console.log(top.TopWin('需要置顶程序的窗口名'))

编译好的测试文件点此下载
该插件的功能大概为:

  • 如果传输两个参数(窗口名称,是否置顶)则会置顶窗口与取消窗口置顶。
  • 如果传输四个参数(窗口名称,改变位置|改变大小,x坐标|窗口宽度,y坐标|窗口高度)则会修改窗口的位置或大小,第二个参数为true,修改位置,第二个参数为false,否则修改大小。
  • 如果传输五个参数(窗口名称,x坐标,y坐标,窗口宽度,窗口高度)则会修改窗口的位置和大小

在Electron中可以直接require相应原生模块使用,如果渲染进程开启了node功能,渲染进程也可以直接调用。