How to use mixed C++ & .Net dll in node.js? (Error

2019-06-02 22:19发布

问题:

I want to create a native node extension using a dll containing C++ and C# code in Visual Studio 2015. I cannot make it work following my own instructions of yesteryear, which is based on the latest node-gyp.

When not using the /clr option, I can run a program like the following just fine.

console.log("1");
const addon = require('./build/Release/addon');
console.log("2");

When enabling /clr, only the first call to log gets executed. When compiling the dll in debug mode, I get the following message:

How to fix / debug this?

(I know there's edge, but I am trying to go the node-gyp way)

回答1:

After unsuccessfully twiddling all (?) the compiler and linker options in VS2015, I found out how to set up my binding.gyp instead, in order to get .Net to work:

{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "hello.cc" ],
      "msbuild_settings": {
        "ClCompile": {
          "CompileAsManaged": "true",
          "ExceptionHandling": "Async",
        },
      },
    }
  ]
}

I verified the build by successfully executing the following mix of managed and unmanaged code:

#include <node.h>
#include <v8.h>

namespace demo {

  #pragma managed

  void callManaged()
  {
    System::String^ result = gcnew System::String("hola");
    System::Console::WriteLine("It works: " + result);
  }

  #pragma unmanaged

  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;

  void Method(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    callManaged();
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "woooooorld"));
  }

  void init(Local<Object> exports) {
    NODE_SET_METHOD(exports, "hello", Method);
  }

  NODE_MODULE(addon, init)

}


回答2:

Unless one really must use node-gyp, these days cmake-js and node-addon-api (provides a ABI so you don't need to rebuild for a new Node.js version) should be used. This CMakeLists.txt for cmake-js compiles mixed native/managed code:

cmake_minimum_required(VERSION 2.8)

project (my-addon)
set(SOURCE_FILES src/main.cc)
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES 
  PREFIX ""
  SUFFIX ".node"
  COMMON_LANGUAGE_RUNTIME "") # "pure" and "safe" unsupported in VS 2017
target_include_directories(${PROJECT_NAME}
  PRIVATE ${CMAKE_SOURCE_DIR}/node_modules/node-addon-api
  PRIVATE ${CMAKE_JS_INC})
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})

Start with a package.json like

{
  "name": "my-addon",
  "version": "1.0.0",
  "description": "My node.js addon",
  "main": "main.js",
  "scripts": {
    "test": "node main.js",
    "install": "cmake-js compile"
  }
}

with a main.js like

var addon = require('bindings')('my-addon');
console.log("hello: " + addon.doSomething());

a src/main.cc like

#include <napi.h>

namespace myaddon
{
  #pragma managed

  void callManaged()
  {
    System::String^ result = gcnew System::String("hola");
    System::Console::WriteLine("It works: " + result);
  }

  #pragma unmanaged

  Napi::String MyMethod(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    callManaged();
    return Napi::String::New(env, "world");
  }

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

  NODE_API_MODULE(myaddon, Init)
}

To build, run

npm install bindings
npm install node-addon-api
npm install cmake-js
npm install

To execute, run

npm test