nodejs / abi-stable-node

Repository used by the Node-API team to manage work related to Node-API and node-addon-api
239 stars 47 forks source link

Using function from external library: Build is fine, but error on require() #319

Closed TravisGesslein closed 6 years ago

TravisGesslein commented 6 years ago

I'm currently trying to write a wrapper to the fmod (www.fmod.com) C-API using NAPI.

For this, I'm including fmod's headers as well as referencing some pre-built external libraries (.lib, I'm on Windows) in the binding.gyp file. This seems to work fine, as there are no errors building the module with node-gyp, and when I remove the library references I get linker errors as expected.

I'm starting minimally, by exposing a simple test method from the NAPI examples (the hello() one), which works fine. I also add two functions, one which calls an fmod function and one that just 'tests' the value returned by that function by printing the pointer value as an integer with printf. I will post the code below, but for now let me describe the problem further.

When I compile it like this and try to call the functions in a separate node.js program, the application just throws an exception ('module not found') in the line that tries to load the new module via require(). So to make this clear, the exception is thrown when the module is being loaded, not when the function is being called.

When I simply remove the line that exposes that one specific wrapper function, but still leave the other two (the basic hello() one, and the other custom one), I actually get the expected behavior: The two working functions are called, and the app throws an exception when I try to call the now-unexported function (because it's not there).

Then I tried around a bit, and the only thing that seems to be triggering the 'module not found' error is the fact that a function from fmod is referenced inside one of the exposed functions. In particular, declaring something like a struct instance from a type also provided by fmod, and then not using it, does not trigger this error (though the compiler gives a warning about unused variables of course).

Is something going wrong during linking and the compiler doesn't report it?

The fmod.cc file looks like this:

#include <node_api.h>
#include <fmod_studio.h>
#include <fmod.h>
#include <fsbank.h>
#include <cstdio>

namespace nodefmod
{

    napi_value Method(napi_env env, napi_callback_info args) {
        napi_value greeting;
        napi_status status;

        status = napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &greeting);
        if (status != napi_ok) return nullptr;
        return greeting;
    }

    napi_value Studio_System_Create(napi_env env, napi_callback_info info)
    {
        size_t argc = 2;
        napi_value args[2];
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

        int32_t arg1;
        napi_get_value_int32(env,args[1], &arg1);

        FMOD_STUDIO_SYSTEM *system;

        FMOD_Studio_System_Create(&system, arg1);

        napi_value out_arg0;
        napi_create_external(env, system, nullptr, nullptr, &out_arg0);

        napi_value arg0 = args[0];
        napi_set_named_property(env, arg0, "val", out_arg0);

        printf("%d", (int)system);
        return nullptr;
    }

         //this is a commented out version of the same function above
        //its content is actually just a clone of the teststudio function
        //but with a simple call to FMOD_Studio_System_Create added
        //this function will trigger the module not found error, and if I remove
       //the FMOD function call, it will behave exactly like teststudio()
    /*napi_value Studio_System_Create(napi_env env, napi_callback_info info)
    {
        FMOD_STUDIO_SYSTEM *system;
        FMOD_Studio_System_Create(nullptr, 0);

        size_t argc = 1;
        napi_value args[1];
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

        void *result;
        napi_get_value_external(env, args[0], &result);

        printf("%d\n", (int)result);
        return nullptr;
    }*/

    napi_value teststudio(napi_env env, napi_callback_info info) 
    {
        size_t argc = 1;
        napi_value args[1];
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

        void *result;
        napi_get_value_external(env, args[0], &result);

        printf("%d\n", (int)result);
        return nullptr;
    }

#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 desc[] = {
            DECLARE_NAPI_METHOD("hello", Method),
            DECLARE_NAPI_METHOD("Studio_System_Create", Studio_System_Create),
            DECLARE_NAPI_METHOD("teststudio", teststudio)
        };

        status = napi_define_properties(env, exports, sizeof(desc)/sizeof(desc[0]) , desc);

        if(status != napi_ok) printf("Something went wrong when initializing the fmod node.js native module");

        return exports;
    }

    NAPI_MODULE(NODE_GYP_MODULE_NAME, Init);
}

binding.gyp:

{
  "targets": [
    {
      "target_name": "node_fmod",
      "sources": [ "fmod.cc" ],
      "include_dirs":[
        "./api/studio/inc",
        "./api/lowlevel/inc",
        "./api/fsbank/inc"
      ],
      "libraries":[
            "../api/studio/lib/fmodstudioL64_vc.lib",
            "../api/lowlevel/lib/fmodL64_vc.lib",
            "../api/fsbank/lib/fsbank64_vc.lib"
          ]
    }
  ]
}

and the index.js that I use for testing:

var node_fmod = require("bindings")("node_fmod");

console.log(node_fmod.hello());

var outval = {val: 42};
node_fmod.Studio_System_Create(outval.val, 1);
node_fmod.teststudio(outval.val);

I will also attach a zip of the current project, run on Win10 x64 with Node.js Version 10.1.0

node_fmod.zip

Unfortunately, to try this yourself you have to download the fmod C++ API manually as I'm not permitted the distribute it (as far as I know). You have to create a free account at fmod.com and download the windows API package at https://www.fmod.com/download . Place its 'api' folder inside the directory of the node.js project and it should run after npm install, node-gyp configure and node-gyp build.

Any idea what this could be?

TravisGesslein commented 6 years ago

Small Update:

I should've caught this sooner, but it seems to be related to the corresponding .dlls. Placing them inside the root folder of the project makes the 'module not found' error disappear. Though now the project crashes somewhere inside the called functions (without error), which may just be because I'm using the API wrong. Investigating....

It would be helpful if nodejs / NAPI detected missing DLLs and gave feedback to the caller.

TravisGesslein commented 6 years ago

Yup, error was because of I handed a nullptr to a function where I shouldn't have.

edit: Don't know if I should open a separate issue about this. It seems more like a nodejs error that missing DLLs are not reported.

NickNaso commented 6 years ago

Hi @Heishe, based on my experience on window you need the dlls and not only the .lib files on your module. See at examples on how to use and configure your GYP file here: https://github.com/nodejs/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild In the past I had problems like yours on my native module called ghostsrcipt4js It is in the list of examples reported on the node-gyp wiki.