【问题标题】:How can I receive a string from c++ dll using node and ffi-napi?如何使用 node 和 ffi-napi 从 c++ dll 接收字符串?
【发布时间】:2021-06-26 05:05:40
【问题描述】:

我正在尝试做一个简单的概念证明,并使用 ffi-napi 从节点调用一个非常基本的 c++ dll。

当我使用获取/返回 int 或 char 的方法时,它工作正常,但我返回字符串的尝试失败了。

c++代码,main.h:

#include <string>
#include <stdlib.h>
using namespace std;

#define EXPORT __declspec(dllexport) 

extern "C" {
    EXPORT string hello(string x);
    EXPORT int count(int x);
}

c++代码,main.cpp:

#include "pch.h"
#include <string>
#include <stdlib.h>
#include "Main.h"
using namespace std;


string hello(string name)
{
    return "Hello " + name + "! ";
}

int count(int x)
{
    return x+1;
}

当我在下面运行节点代码时,

import { Console } from "node:console";

var ffi = require('ffi-napi');
var ref = require('ref-napi');

export class DllTester {

  LibName: string = 'D:\\temp\\DemoDll\\x64\\Debug\\DemoDll.dll';
  Lib: any = null;

  private setupOcrLib()
  {
    this.Lib = ffi.Library(this.LibName, 
      {'hello': [ 'string', ['string']],
      'count': [ ref.types.int, [ref.types.int]] 
      }
    );
  }

  count(x: number): number {
    this.setupOcrLib();
    console.log("Calling count");
    return this.Lib.count(x);
  }

  hello(name: string): string {
    this.setupOcrLib();
    console.log("Calling hello: " + name);
    return this.Lib.hello(name);
  }

}

const tester = new DllTester();
console.log("Count: " + tester.count(3));
console.log("Hello: " + tester.hello("Markus"));
console.log("Count: " + tester.count(10));

我得到以下控制台输出:

Calling count
Count: 4
Calling hello: Markus

所以第一次调用(发送 int,接收 int)工作正常,但第二次调用(发送字符串,接收字符串)静默失败并且 由于第二次调用 count 没有被记录,我相信我不成功的调用会扰乱流程。

尝试了以下方法:

我对 c++ 的理解有限,可能遗漏了一些关于类型和指针的部分,但这看起来很简单,应该不会出错...

平台:windows 10, 64 bit, node v14.12.0, VS 2019 Community

版本: "ffi-napi": "^4.0.3", "ref-napi": "^3.0.2"

感谢您的意见 :-)

【问题讨论】:

  • 您是否尝试过使用std::string?在 JS 方面,没有什么类似于 C++ 方面的using namespace std; 的操作。顺便说一句:在 C++ 中这样做是不受欢迎的,因为它会污染命名空间,但这是一个不同的问题。
  • 在 DLL 甚至不同语言之间编组字符串的传统方式是客户端创建一个缓冲区,然后客户端将该缓冲区发送到 DLL 以填充字符数据。 std::string 不是通用的内置类型,例如 intstd::string 内部在 C++ 编译器、构建设置等方面有所不同。如果您想要一个真实的示例,请查看 Windows API 以及它如何处理字符串数据。用户创建缓冲区,将其发送到 API 函数,函数填充缓冲区。
  • @UlrichEckhardt 在“双方”都试过了。仅在 c++ 端使用它会得到与以前相同的结果,如果我在 typescript 中尝试它,我会收到错误:无法确定正确的“类型”来自:'std::string' 还尝试在 c++ 端使用 CString ,也没有用。感谢您的意见!
  • @PaulMcKenzie 好的,所以我不应该期望这么容易返回一个字符串?所以我想我应该发送一个指向我的方法的指针而不是使用返回值。你在使用 ffi 之前做过这个吗?希望看到一个如何处理字符串的代码示例。谢谢!

标签: c++ node.js node-modules ffi


【解决方案1】:

在查看@PaulMcKenzies 的答案后让它工作:我不是简单地使用字符串,而是创建一个缓冲区并使用指针。

C++ 代码,main.h:

#include <string>
#include <stdlib.h>
using namespace std;

#define EXPORT __declspec(dllexport) 

extern "C" {
    EXPORT void helloPtr(char* x);
    EXPORT int count(int x);
}

c++代码,main.cpp:

#include "pch.h"
#include <string>
#include <stdlib.h>
#include "Main.h"
using namespace std;

void helloPtr(char* name)
{
    std::string names(name);
    std::string results = "Hello " + names + "!";
    strcpy_s(name, 200, results.c_str());
}

int count(int x)
{
    return x+1;
}

节点:

import { Console } from "node:console";

var ffi = require('ffi-napi');
var ref = require('ref-napi');

export class DllTester {

  LibName: string = 'D:\\temp\\DemoDll\\x64\\Debug\\DemoDll.dll';
  Lib: any = null;

  constructor() {
    this.setupLib();
  }

  private setupLib()
  {
    this.Lib = ffi.Library(this.LibName, 
      {'helloPtr': [ ref.types.void, ['char *']],
      'count': [ ref.types.int, [ref.types.int]] 
      }
    );
  }

  count(x: number): number {
    return this.Lib.count(x);
  }


  helloPtr(name: string): string {

    //setup buffer
    var maxLength = 200;
    var nameBuffer = Buffer.alloc(maxLength);
    nameBuffer.fill(0); 
    nameBuffer.write(name, 0, "utf-8"); 

    this.Lib.helloPtr(nameBuffer);

    //get result from buffer
    var helloResult = nameBuffer.toString('utf-8');
    var terminatingNullPos = helloResult.indexOf('\u0000');
    if (terminatingNullPos >= 0) {
      helloResult = helloResult.substr(0, terminatingNullPos);
    }

    return helloResult;
  }

}

const tester = new DllTester();
console.log(tester.helloPtr("Markus"));

这会将所需的消息打印到控制台中:Hello Markus!

如果有人知道更简单的方法或这样做(而不是发送字符串?),那就太好了。

【讨论】:

    猜你喜欢
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多