Node.js 搭建 HTTP 服务器调用 DLL 库实战

作者:后端茶话

时间:2026-10-30

浏览:503

本文介绍如何在 Node.js 环境下,利用 ffi 模块加载本地 DLL 库,并通过 Express 框架搭建 HTTP 服务器,实现远程对 DLL 函数的调用。文章详细讲解了从环境准备、依赖安装、DLL 封装到路由配置及服务器启动的完整流程,适用于需要通过 Web 接口调用本地动态链接库的场景。

文章目录

如何搭建 HTTP 服务器调用 DLL 库

此前在协助朋友处理紧急任务时,构思出一种通过 HTTP 服务器调用本地 DLL 库的方案。在某些特定场景下,我们需要远程访问并调用本地的动态链接库(DLL)。为此,我们可以构建一个 HTTP 服务,通过发送标准的 HTTP 请求来触发 DLL 中的函数。这种方式的优势在于,无需将 DLL 文件部署到远程服务器,即可实现对本地 DLL 函数的远程调用。

运行环境:MacOS 系统配合 Node 18.0.0 版本。 应用场景:酒店房卡管理系统。通过 HTTP 服务器调用 DLL 库,实现对房卡的读取、写入及注销等操作。

准备阶段

  • 安装 Node.js:首先需确保系统中已安装 Node.js。Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时环境,能够用于构建高效的网络应用。
  • 创建 HTTP 服务器:利用 Node.js 内置模块,可以便捷地创建一个简单的 HTTP 服务。本文将演示如何编写 Node.js 脚本,创建一个监听指定端口的 HTTP 服务器,此处选用 express 框架进行开发。
  • 调用 DLL 库:为了实现 DLL 函数的调用,我们将使用 Node.js 的 "ffi" 模块。该模块支持加载和调用动态链接库中的函数。下文将演示如何使用 "ffi" 模块加载 DLL,并执行其中的函数。

搭建基础读取 DLL 库

首先创建一个文件夹:mkdir http-dll,然后进入该文件夹:cd http-dll。

1.1 创建 package.json 文件

在该文件夹下创建 package.json 文件:pnpm init -y。

1.2 安装依赖

安装依赖:pnpm i ffi ref-struct ref-array ref。

1.3 读取

搭建过程并非重点,关键在于理解为何使用该方法,其次是如何处理逻辑,最后才是设计具体的使用方式。

假设此处使用的是LockSDK.dll,仅为演示目的,提供了基础的 4 个方法,因此以这 4 个方法为例进行说明。

  • TP_Configuration:动态库初始化配置,用于完成门锁类型选择及发卡器连接等。
  • TP_MakeGuestCardEx:制作宾客卡。
  • TP_ReadGuestCardEx:读取宾客卡信息。
  • TP_CancelCard:注销卡片或进行卡片回收。

此处不展示具体的 C 语言代码,而是直接以 DLL 库的形式提供给使用者。

首先需要思考一个问题:如果仅从文档中获取接口进行直接调用,会出现什么问题?

  • 每次使用都需要查阅文档。
  • 每次使用都需要重新编写调用代码。
  • 缺乏统一的调用规范,容易导致管理混乱。

因此我们需要设定一个目标,将这些方法封装起来,实现统一调用,便于管理。为此我们创建一个 conf 目录用于存放配置文件,并创建dll.conf.js文件,用于存储 DLL 的配置信息。

/**
函数名:TP_Configuration
功能:动态库初始化配置,完成门锁类型选择/发卡器连接等
@param {number} lock_type - 门锁类型(也就是使用的卡片类型): 4-RF57门锁; 5-RF50门锁
@returns {number} - 错误类型
注意:此函数为 __stdcall 调用约定,如果您的函数库采用的是 __cdecl 调用约定,需要将其修改为 __stdcall 调用约定。
*/
export const TP_Configuration = {
  "TP_Configuration": [
    "int", // return
    // parameters
    ["int"],
  ],
};
  • 这种配置方式是为了方便 ffi 模块进行调用,此处暂不展开。

如果仅采用这种配置方式,我们仍需每次读取文件并重新编写代码,这便失去了封装的意义。因此,我们需要考虑一种更便捷的方式,即将所有方法放置在一个统一的文件中,通过调用该文件来获取所需的所有方法。

因此创建 conf.js 文件,用于集中存放所有的方法定义。

const { TP_Configuration, TP_MakeGuestCardEx, TP_ReadGuestCardEx, TP_CancelCard } = require('./dll.conf.js')
export const LIB_NAME = "../dll/mylib.dll"
export const lib_func = {
    TP_Configuration,
    TP_MakeGuestCardEx,
    TP_ReadGuestCardEx,
    TP_CancelCard
}
module.exports = {
    LIB_NAME,
    lib_func
}

此时我们已经配置好了 conf 目录下的配置文件,下一步则需要实现读取和调用的具体逻辑。

我们创建一个 core 目录,用于存放核心方法,并创建 load.js 文件,用于实现核心加载逻辑。

const { lib_func, LIB_NAME } = require('../conf/conf.js')
const ffi = require('ffi');
const ref = require('ref');
const {
    TP_Configuration,
    TP_MakeGuestCardEx,
    TP_ReadGuestCardEx,
    TP_CancelCard
} = ffi.Library(LIB_NAME, lib_func);
module.exports = {
    TP_Configuration,
    TP_MakeGuestCardEx,
    TP_ReadGuestCardEx,
    TP_CancelCard
}
  • ffi模块,用于加载和调用动态链接库(DLL)中的函数。
  • ref模块,用于处理 C 语言中的指针相关问题。
  • 我们将从 DLL 中读取出的函数放置在一个对象中,以便外部调用。此处由 HTTP 服务器负责调用这些封装好的函数。

1.4 创建 HTTP 服务器

安装express框架,然后在创建server目录,用于存放 HTTP 服务器相关代码。

在server目录下创建app.js文件,用于启动 HTTP 服务器。

const express = require('express');
class App {
    routes;
    app = express();
    constructor(routes) {
        this.routes = routes;
        this.requestEntry()
    }
    requestEntry() {
        this.routes.forEach(route => {
            this.app[route.method](route.path, route.handler)
        })
    }
    listen(port) {
        app.listen(port, () => {
            console.log(`Example app listening at http://localhost:${port}`)
        })
    }
}
module.exports = {
    App
}
  • 此处我们将 HTTP 服务器的创建与路由配置分离,以便于代码管理。

下面我们创建目录文件routes.js,用于存放路由配置信息。

const CancelController = require('./controller/cancel.controller.js')
const ConfigurationController = require('./controller/configuration.controller.js')
const MakeGuestCardEx = require('./controller/MakeGuestCardEx.controller')
const ReadGuestCardExController = require('./controller/ReadGuestCardExController.controller')
const routes = [
    {
        path: '/cancel',
        method: 'post',
        handler: new CancelController(),
    },
    {
        path: '/configuration',
        method: 'post',
        handler: new ConfigurationController(),
    },
    {
        path: '/make-guest-card-ex',
        method: 'post',
        handler: new MakeGuestCardEx(),
    },
    {
        path: '/read-guest-card-ex',
        method: 'post',
        handler: new ReadGuestCardExController(),
    }
]
module.exports = {
    routes
}

在逻辑上,我们需要将每个路由的处理逻辑分离出来,因此我们创建controller目录,用于存放具体的路由处理逻辑。

在controller目录下创建cancel.controller.js文件,用于存放具体的路由处理逻辑代码。

const ref = require("ref");
const { TP_CancelCard } = require("../../core/load");
class CancelController {
  constructor() {}
  async handler(_req, res) {
    try {
      const card_snr = _req.body.card_snr || ref.allocCString("", 20);
      const result = await TP_CancelCard(card_snr);
      const card_snr_str = ref.readCString(card_snr);
      return res.status(201).send({
        data: {
          result,
          card_snr_str,
        },
      });
    } catch (error) {
      res.status(500).json(error);
    }
  }
}
module.exports = CancelController
  • 至此,我们已经完成了对 DLL 函数的完整调用,并将其封装为一个 HTTP 服务器,方便通过网页端进行处理。

此外,我们还需要一个main.js文件,用于启动 HTTP 服务器。

const App = require('./server/app.js')
const { routes } = require('./server/routes.js')
const app = new App(routes);
app.listen(3000)

到这一步,整体流程即告完成。我们只需要在终端中输入node main.js,即可启动 HTTP 服务器。

最终结果

目录结构如下:

.
├── conf
│   ├── conf.js
│   └── dll.conf.js
├── core
│   └── load.js
├── dll
│   └── LockSDK.dll
├── main.js
├── mock
│   └── mockUse.js
├── package.json
├── pnpm-lock.yaml
└── server
    ├── app.js
    ├── controller
    │   ├── cancel.controller.js
    │   ├── configuration.controller.js
    │   ├── makeGuestCardEx.js
    │   └── readGuestCardEx.controller.js
    └── routes.js

Reference Node-DLL

以上就是一文学会搭建 HTTP 服务器调用 DLL 库的详细内容,更多关于 HTTP 服务器调用 DLL 库搭建的资料请关注本站其它相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章