Search code examples
c++.netnode.jsvisual-studio-2015clr

How to use mixed C++ & .Net dll in node.js? (Error: abort() has been called)


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:

enter image description here

How to fix / debug this?

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


Solution

  • 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