NodeJs Addon Width C++/CLI and C# assembly I can not make it run

430 Views Asked by At

I am trying to build an addon in nodejs using CMAKE. The CPP file has a very basic dependency created by me in CSharp, a class that has to be called from the CPP. When I perform this operation in a CPP console application compiled with Visual Studio everything works correctly. The problem comes when I do the compilation with CMAKE-JS to create the node addon then, when it has to call a method that is in the assembly dll that I have created in Csharp it tells me that the specified file is not found.


This is my class library in CSharp which outputs a file named writeText.dll (Framework Version 4.8)

writeText.cs

using System;
using System.Linq;


namespace writeText
{
    public class CsTemplate
    {
        public string string1;
        public string string2;

        private static CsTemplate oInstance;

        public CsTemplate()
        {

        }

        /**
         * @method  Instance
         * 
         * Singleton
         * 
         * @returns {CsRecognizer}      Clase instanciada.
         */
        public static CsTemplate Instance()
        {

            if (oInstance == null)
            {
                oInstance = new CsTemplate();
            }

            return oInstance;
        }

        /**
         * @method  setString
         * 
         * Asign values to properties
         * 
         * @returns {CsRecognizer}      Clase instanciada.
         */
        public string setString(string str1, string str2)
        {
            string1 = str1;
            string2 = str2;

            return "El se asigna como " + string1 + " y la gramática se asignaa como " + string2;
        }
    }
}

This is the Cpp file that makes use of the dependency created in Csharp

main.cpp

#include <node_api.h>
#include <assert.h>

using namespace writeText;

namespace myaddon
{
    #pragma managed

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

    void CallCsharp()
    {
        System::String^ str1Cpp = gcnew System::String("Uno");
        System::String^ str2Cpp = gcnew System::String("Dos");

        System::Console::WriteLine("String definidos: " + str1Cpp + " -> " + str2Cpp);
        CsTemplate::Instance()->setString(str1Cpp, str2Cpp);
    }

    void printResults() {
        System::Console::WriteLine("Defined strings: " + CsTemplate::Instance()->string1 + " -> " + CsTemplate::Instance()->string2);
    }

    #pragma unmanaged

    napi_value Test(napi_env env, napi_callback_info info) {
        napi_status status;
        napi_value response;

        callManaged();

        status = napi_create_string_utf8(env, "OK", NAPI_AUTO_LENGTH, &response);
        assert(status == napi_ok);

        return response;
    };

    napi_value SetStrings(napi_env env, napi_callback_info info) {
        napi_status status;
        napi_value response;

        CallCsharp();

        status = napi_create_string_utf8(env, "OK", NAPI_AUTO_LENGTH, &response);
        assert(status == napi_ok);

        return response;
    };

    napi_value PrintStrings(napi_env env, napi_callback_info info) {
        napi_status status;
        napi_value response;

        printResults();

        status = napi_create_string_utf8(env, "OK", NAPI_AUTO_LENGTH, &response);
        assert(status == napi_ok);

        return response;
    };

    #define DECLARE_NAPI_METHOD(name, func) { name, 0, func, 0, 0, 0, napi_default, 0 }

    napi_value Init(napi_env env, napi_value exports) {
        napi_status status;

        napi_property_descriptor listen = DECLARE_NAPI_METHOD("test", Test);
        status = napi_define_properties(env, exports, 1, &listen);
        assert(status == napi_ok);

        napi_property_descriptor set = DECLARE_NAPI_METHOD("set", SetStrings);
        status = napi_define_properties(env, exports, 1, &set);
        assert(status == napi_ok);

        napi_property_descriptor print = DECLARE_NAPI_METHOD("print", PrintStrings);
        status = napi_define_properties(env, exports, 1, &print);
        assert(status == napi_ok);

        return exports;
    }

    NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
}

This is my CmakeLists.txt which compiles the nodejs addon

CmakeLists.txt

cmake_minimum_required(VERSION 3.17)

project (my-addon CXX CSharp)

include_directories(${CMAKE_JS_INC})

file(GLOB SOURCE_FILES "src/*.cpp")

add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})

target_compile_options(${PROJECT_NAME} PRIVATE /clr)
target_compile_options(${PROJECT_NAME} PRIVATE /fp:precise) #/fp:strict is incompatible with /clr

set_property(TARGET ${PROJECT_NAME} PROPERTY VS_GLOBAL_ROOTNAMESPACE ${PROJECT_NAME})
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_GLOBAL_KEYWORD "ManagedCProj")
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_GLOBAL_CLRSupport "true")
set_property(TARGET ${PROJECT_NAME} PROPERTY DOTNET_TARGET_FRAMEWORK_VERSION "4.8")
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_GLOBAL_WindowsTargetPlatformVersion "10.0.18362.0")
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_REFERENCES_COPY_LOCAL "true")

set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_REFERENCES
    "System" 
)

# Add reference writeText.dll (This dll is writeText.cs compiled)
set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DOTNET_REFERENCE_writeText "src/writeText.dll")

set_target_properties(${PROJECT_NAME} PROPERTIES 
  PREFIX ""
  SUFFIX ".node"
  COMMON_LANGUAGE_RUNTIME ""
)

target_include_directories(${PROJECT_NAME}
  PRIVATE ${CMAKE_SOURCE_DIR}/node_modules/node-addon-api
  PRIVATE ${CMAKE_JS_INC})

target_link_libraries(${PROJECT_NAME}
    PUBLIC
        ${CMAKE_JS_LIB}
)

Once this is done, everything compiles perfectly, resulting in the build/Release folder as follows:

Struct build/Release image

Everything seems to be correct, the addon is created perfectly and the writeText.dll is copied inside the build / Release folder as expected.

Then I use the addon

main.js

var addon = require('bindings')('my-addon');

console.log("Test: " + addon.test());
console.log("Set: " + addon.set());
console.log("Print: " + addon.print());

And i get this

$ node main.js
It works: hello
Test: OK

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or 
assembly 'writeText, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system could not find especified file
  on myaddon.CallCsharp() 

In the test method call, everything works fine and prints to my screen using the System namespace perfectly, but when the set method is called which makes use of my writeText.dll it tells me the file cannot be found.

As you can see in the image of the output structure the file is there. Also, when I open the project created by CMake in Visual Studio the assembly seems to be correct.

This is the structure of the project built by Cmake:

Project Cmake Struct

Everything seems to be correct and in principle it should work fine but for some reason it tells me that writeText cannot be found.

0

There are 0 best solutions below