0

I have a project that is compiled into a library and declares a certain function to be implemented by the user of the library:

//To be defined by user
Application* CreateApplication();

When compiling the code into a shared library on Linux this works perfectly. Any user of the library can define an implementation for the declared function and it can be used inside the library. If the user of the library forgets to define an implementation, they will get an error pointing this out.

I'm now in the process of porting the library to Windows, where it is supposed to be compiled into a dll. However, I'm running into problems as the linker used by Visual Studio is complaining:

unresolved external symbol Application* __cdecl CreateApplication(void)

I tried adding the extern keyword to indicate that the definition of the function is somewhere else, but this didn't work.

Why can't I declare (but not define) a function in a dll like this? How should I fix my code so it works both on Linux and on Windows?

Antiro42
  • 177
  • 8
  • If you do like this in Linux and use `dlopen` + `dlsym` on the shared library you'll get a runtime error unless you've previously loaded the user's library. "_The function `dlsym()` takes a "handle" of a dynamic loaded shared object returned by `dlopen(3)` along with a null-terminated symbol name, and returns the address where that symbol is loaded into memory. If the symbol is not found, in the specified object or any of the shared objects that were automatically loaded by `dlopen(3)` when that object was loaded, `dlsym()` returns `NULL`._" – Ted Lyngmo Oct 14 '20 at 19:32
  • I'm compiling the user of the library in the same project. Maybe that's why it works? Maybe the question should be how should I do it in Windows and in Linux? – Antiro42 Oct 14 '20 at 19:34
  • Yes, that sounds fragile. – Ted Lyngmo Oct 14 '20 at 19:36
  • Any suggestions on how I should approach this? Would making this a static library solve the issue? – Antiro42 Oct 14 '20 at 19:36
  • Hmm, perhaps it's possible to declare a dependency to the users DLL from your DLL so that it's automatically loaded first when yours is loaded. If not, load it manually when yours is loaded. You shouldn't declare the function as being a part of your DLL in either case. if the function is supposed to be a part of an API, make a wrapper function in your DLL that calls the users (hidden) function. – Ted Lyngmo Oct 14 '20 at 19:42
  • I've currently declared the function in a header and am using it in the source files that get compiled into the shared library. The idea is that the user of the dll, which in my case is a binary/exe defines an implementation for the function. I'm not that familiar with the way shared libraries or dll's work. How would the suggestion of "loading it manually" work? – Antiro42 Oct 14 '20 at 19:46
  • Loading it manually may look like a lot of work but it's usually not. I put together an example from some old code [here](https://godbolt.org/z/nWKEhx). This uses `std::string` and the Ansi versions of the WinAPI (I have it in a ~20 year old project of mine). It loads `kernel32.dll` and the symbol `OpenThread`. – Ted Lyngmo Oct 14 '20 at 19:59
  • That does indeed look doable. However, I'm looking for a cross platform solution. Any hints on where I could find information or examples on how to approach that? (also, If you want to formulate it in an answer, I'm happy to accept it :D) – Antiro42 Oct 14 '20 at 20:06
  • :-) The long answer to the question is a bit too long right now. I've made a similar wrapper for `.so` files somehere. Edit: [here](https://stackoverflow.com/questions/56932785/automatically-creating-wrappers-for-classes-loaded-with-dlopen/56971263#56971263). If you return _objects_ from your DLL, be sure to also _delete_ the objects in the DLL. It's a bit tricky and I didn't cover that in that SO-answer, but I mention it in the comments. – Ted Lyngmo Oct 14 '20 at 20:12
  • 1
    .... or do as Remy suggests below :-) – Ted Lyngmo Oct 14 '20 at 20:16

1 Answers1

2

What you are attempting to do only works in a static library, it cannot work in a dynamic library like a DLL. For that, you will have to change the code to use a function pointer instead. The application that is using the DLL can pass in the address of the desired function from its own code, and the DLL can then assign that address to a variable that it uses as needed, eg:

HEADER:

#ifndef MYLIB_H
#ifndef MYLIB_H

#ifdef COMPILING_MY_LIB
#define MY_EXPORT __declspec(dllexport)
#else
#define MY_EXPORT __declspec(dllimport)
#endif

// declare Application as needed...

typedef Application (*lpCreateApplicationFunc)();

#ifdef __cplusplus
extern "C" {
#endif

MY_EXPORT void SetCreateApplicationFunc(lpCreateApplicationFunc func);

#ifdef __cplusplus
}
#endif

#endif

DLL:

#define COMPILING_MY_LIB
#include "MyLib.h"

//To be defined by user
lpCreateApplicationFunc CreateApplication = NULL;

void SetCreateApplicationFunc(lpCreateApplicationFunc func)
{
    CreateApplication = func;
}

void doSomething()
{
    Application *app = NULL;

    if (CreateApplication)
        app = (*CreateApplication)();

    if (app)
    {
        ...
    }
}

EXE:

#include "MyLib.h"

Application MyCreateApplicationFunc()
{
    ...
}

// during startup, call this...
SetCreateApplicationFunc(&MyCreateApplicationFunc);
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770