godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.48k stars 21.26k forks source link

Can't open dynamic library #61452

Open FelipeRamones opened 2 years ago

FelipeRamones commented 2 years ago

Godot version

3.4.4.stable

System information

Windows 10 Pro x64, Intel I7-9750H x64, 16GB RAM, NVIDIA GeForce GTX 1650, Driver Version 30.0.15.1179, Backend GLES3

Issue description

I'm trying to link PostgreSQL to Godot thru GDNative using libpqxx, after i link all the necessary files and the windows socket in the scons file it generates a .dll file, but every time i try to instantiate the GDNative class it crashes and throws the error "Attempting to call function 'new' in base 'NativeScript' on a null instance".

I also get this message in the cmd:

ERROR: Can't open dynamic library: C:/Users/Avell/Projetos de Jogos/Godot Projects/gdnative_cpp_example/demo/bin/win64/libgdexample.dll, error: Error 126: Couldn't find the specified module.
.
   at: (platform/windows/os_windows.cpp:2353)
ERROR: No valid library handle, can't get symbol from GDNative object
   at: get_symbol (modules/gdnative/gdnative.cpp:510)
ERROR: No nativescript_init in "res://bin/win64/libgdexample.dll" found
   at: init_library (modules/gdnative/nativescript/nativescript.cpp:1510)
ERROR: Condition "!script_data" is true.

Steps to reproduce

I'm using this GDNative example project and the regular godot cpp repo, cloned the godot-cpp repo using --recursive -b 3.x thru git bash and built it with: scons generate_bindings=yes platform=windows target=release bits=64

After that i draged it into the GDNative example directory ending up with something like this:

image

Then i've downloaded and installed PostgreSQL Version 12.11 for Windows x86_64

After that i cloned the libpqxx repo inside of my GDNative directory, ending up with something like this:

image

Then i've built it following this tutorial 'till the minute 10:20, the only difference being that in the Visual Studio 2019 interface i got the C++ version to standard C++17 and not latest in both the files he built.

After that i've linked the necessary paths to the SConstruct file. Here's the code:

# make sure our binding library is properly includes
env.Append(CPPPATH=[
    '.',
    godot_headers_path,
    cpp_bindings_path + 'include/',
    cpp_bindings_path + 'include/core/',
    cpp_bindings_path + 'include/gen/',
    "C:/Program Files (x86)/Windows Kits/10/Include/10.0.22000.0/",
    "C:/Program Files/PostgreSQL/12/include/",
    "C:/Program Files/libpqxx/include/",
    ]
)
env.Append(LIBPATH=[
    cpp_bindings_path + 'bin/',
    'C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22000.0/um/x64/',
    'C:/Program Files/PostgreSQL/12/lib/',
    'C:/Program Files/libpqxx/lib',
    ]
)
env.Append(LIBS=[
    cpp_library,
    "WS2_32",
    "libpq",
    "pqxx",
    ]
)

# tweak this if you want to use different folders, or more folders, to store your source code in.
env.Append(CPPPATH=['src/'])
sources = Glob('src/*.cpp')

library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources)

Default(library)

# Generates help for the -h scons option.
Help(opts.GenerateHelpText(env))

After that i've added /std:c++17 as a CCFLAG as in the following code:

elif env['platform'] == "windows":
    env['target_path'] += 'win64/'
    cpp_library += '.windows'
    # This makes sure to keep the session environment variables on windows,
    # that way you can run scons in a vs 2017 prompt and it will find all the required tools
    env.Append(ENV = os.environ)

    env.Append(CCFLAGS = ['-DWIN32', '-D_WIN32', '-D_WINDOWS', '-W3', '-GR', '-D_CRT_SECURE_NO_WARNINGS'])
    if env['target'] in ('debug', 'd'):
        env.Append(CCFLAGS = ['-EHsc', '-D_DEBUG', '-MDd', '/std:c++17'])
    else:
        env.Append(CCFLAGS = ['-O2', '-EHsc', '-DNDEBUG', '-MD', '/std:c++17'])

if env['target'] in ('debug', 'd'):
    cpp_library += '.debug'
else:
    cpp_library += '.release'

cpp_library += '.' + str(bits)

So then i changed the .cpp and .h files with the necessary code, they ended up like this:

.CPP FILE:

#include "postgres.h"

using namespace godot;

// Convert C++ const char* to Godot::String
#define GOSTR(value) ( String( std::string( value ).c_str() ) )
// Convert Godot::String to C++ const char *
#define CSTR(value) ( value.alloc_c_string() )

void Postgres::_register_methods() {
    // register_method("_process", &Postgres::_process);
    // register_method("db_insert", &Postgres::db_insert);
    // register_method("db_update", &Postgres::db_update);
    // register_method("db_delete", &Postgres::db_delete);
    // register_method("db_upsert", &Postgres::db_upsert);
    register_method("remote_connect", &Postgres::remote_connect);
}

void Postgres::_init(){
    Godot::print(GOSTR("Hello World"));
}

Postgres::Postgres() {

}

Postgres::~Postgres() {
    // add your cleanup here
}

void Postgres::remote_connect(const String conn_str) {
    // initialize any variables here
    try
    {
        pqxx::connection conn = pqxx::connection(CSTR(conn_str));
    }
    catch(const std::exception& e)
    {
        Godot::print(GOSTR(e.what()));
    }

}

.H FILE:

#ifndef POSTGRES_H
#define POSTGRES_H

#include <Godot.hpp>
#include <Reference.hpp>
#include <pqxx/pqxx>

namespace godot {

class Postgres : public Reference {
    GODOT_CLASS(Postgres, Reference)

private:

public:
    static void _register_methods();

    Postgres();
    ~Postgres();

    void _init(); // our initializer called by Godot
    void remote_connect(const String con_str);
    Dictionary db_insert(const String query);
    Dictionary db_update(const String query);
    Dictionary db_delete(const String query);
    Dictionary db_upsert(const String query); //INsert data if doesnt exist, or update if exists

};

}

#endif

GDLIBRARY.CPP FILE:

#include "postgres.h"
using namespace godot;

extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
    godot::Godot::gdnative_init(o);
}

extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
    godot::Godot::gdnative_terminate(o);
}

extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
    godot::Godot::nativescript_init(handle);

    godot::register_class<Postgres>();
}

The src directory content is like this:

image

After All that was built using:

scons platform=windows target=release bits=64

Every scons line was typed on x64 Native Tools Command Prompt for VS2019 running as administrator

After that all dlls were linked in Godot so i clicked the gdexample.gdns file and in the editor inside Godot i typed on Script Class property Name "GDExample" and changed the Main.gd script to:

extends Node

var test = GDExample.new()

#func _on_Sprite_position_changed(node, new_pos):
#       print("The position of " + node.name + " is now " + str(new_pos))

the bug happened as soon as i opened the Godot project with the library files but then after i tried to run it crashed and reinforced the error.

Minimal reproduction project

Couldn't upload the file (maybe it's too big) [73.1MB]

Here's a link:

https://ufile.io/ebcz844v

Calinou commented 2 years ago

As far as I know, a Godot binary compiled with MinGW (like the official builds) cannot use GDNative add-ons compiled with MSVC. This is especially the case for GDNative add-ons written in C++ that link to the standard library, as the MSVC and GNU ABIs are incompatible with each other.

bruvzg commented 2 years ago

As far as I know, a Godot binary compiled with MinGW (like the official builds) cannot use GDNative add-ons compiled with MSVC.

GDNative / GDExtension API is pure C, so it should work fine across MinGW/MSVC, as long as you are not passing file/socket descriptors between different libs. But a different runtime used by libpqxx and add-on might cause issues.

bruvzg commented 2 years ago

As long as libpq and all of its dependencies are in the same folder as GDNative lib, it will load fine:

Screenshot 2022-05-27 121932
FelipeRamones commented 2 years ago

It worked indeed, thanks @bruvzg and @Calinou for your answers ^^ tried adding libpq.dll before but never it's dependencies too, appreciate it ^^