112

I'm having a little look at .dll files, I understand their usage and I'm trying to understand how to use them.

I have created a .dll file that contains a function that returns an integer named funci()

using this code, I (think) I've imported the .dll file into the project(there's no complaints):

#include <windows.h>
#include <iostream>

int main() {
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop  \\fgfdg\\dgdg\\test.dll");

  if (hGetProcIDDLL == NULL) {
    std::cout << "cannot locate the .dll file" << std::endl;
  } else {
    std::cout << "it has been called" << std::endl;
    return -1;
  }

  int a = funci();

  return a;
}

# funci function 

int funci() {
  return 40;
}

However when I try to compile this .cpp file that I think has imported the .dll I have the following error:

C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp||In function 'int main()':|
C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp|16|error: 'funci' was not     declared in this scope|
||=== Build finished: 1 errors, 0 warnings ===|

I know a .dll is different from a header file so I know I can't import a function like this but it's the best I could come up with to show that I've tried.

My question is, how can I use the hGetProcIDDLL pointer to access the function within the .dll.

I hope this question makes sense and I'm not barking up some wrong tree yet again.

Leniency
  • 4,984
  • 28
  • 36

3 Answers3

180

LoadLibrary does not do what you think it does. It loads the DLL into the memory of the current process, but it does not magically import functions defined in it! This wouldn't be possible, as function calls are resolved by the linker at compile time while LoadLibrary is called at runtime (remember that C++ is a statically typed language).

You need a separate WinAPI function to get the address of dynamically loaded functions: GetProcAddress.

Example

#include <windows.h>
#include <iostream>

/* Define a function pointer for our imported
 * function.
 * This reads as "introduce the new type f_funci as the type: 
 *                pointer to a function returning an int and 
 *                taking no arguments.
 *
 * Make sure to use matching calling convention (__cdecl, __stdcall, ...)
 * with the exported function. __stdcall is the convention used by the WinAPI
 */
typedef int (__stdcall *f_funci)();

int main()
{
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop\\test.dll");

  if (!hGetProcIDDLL) {
    std::cout << "could not load the dynamic library" << std::endl;
    return EXIT_FAILURE;
  }

  // resolve function address here
  f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "funci");
  if (!funci) {
    std::cout << "could not locate the function" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "funci() returned " << funci() << std::endl;

  return EXIT_SUCCESS;
}

Also, you should export your function from the DLL correctly. This can be done like this:

int __declspec(dllexport) __stdcall funci() {
   // ...
}

As Lundin notes, it's good practice to free the handle to the library if you don't need them it longer. This will cause it to get unloaded if no other process still holds a handle to the same DLL.

buttercup
  • 1,096
  • 16
  • 37
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • Might sound like a stupid question but what is/should be the type of f_funci? –  Jan 02 '12 at 01:17
  • 8
    Other than that, the answer is excellent and easily understandable –  Jan 02 '12 at 01:18
  • 6
    Note that `f_funci` in fact _is a type_ (rather than _has_ a type). The type `f_funci` reads as "pointer to a function returning an `int` and taking no arguments". More information about function pointers in C can be found at http://www.newty.de/fpt/index.html. – Niklas B. Jan 02 '12 at 01:21
  • Thank you again for the reply, funci takes no arguments and returns an integer; I edited the question to show the function that was compiled? into the .dll. When I tried to run after including "typedef int (*f_funci)();" I got this error: C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp||In function 'int main()':| C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp|18|error: cannot convert 'int (*)()' to 'const CHAR*' for argument '2' to 'int (* GetProcAddress(HINSTANCE__*, const CHAR*))()'| ||=== Build finished: 1 errors, 0 warnings ===| –  Jan 02 '12 at 01:28
  • Well I forgot a cast there (edited it in). The error however seems to be another one, are you sure you use the correct code? If yes, can you please paste your failing code and the complete compiler output on http://pastie.org? Also, the typedef you wrote in your comment is wrong (an `*` is missing, which could have caused the error) – Niklas B. Jan 02 '12 at 01:31
  • The updated answer compiled the program but it did not return the value of funci() it instead returned undefined numbers and threw the "do not send error report" error message –  Jan 02 '12 at 01:35
  • http://pastie.org/3109815, I placed the code which creates the .dll in this link just in case I messed something up there (I don't know if the .def file was even used or if it is needed). –  Jan 02 '12 at 01:36
  • Please use DependencyWalker to check whether the DLL really exports the function (and add error checking for that case, see above). – Niklas B. Jan 02 '12 at 01:40
  • There is no error now, the file is compiling but not returning the expected result (from the funci() function) - I edited my pastie link to include the .dll code as well as the code which now compiles with errors –  Jan 02 '12 at 01:43
  • Using DependencyWalker gives me the following: Warning: At least one delay-load dependency module was not found. Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module. –  Jan 02 '12 at 01:46
  • @Michael: You are supposed to check whether test.dll really exports "funci". The warnings are irrelevant. If the function is not exported, the problem is not within my code (and your EXPORTS section suggests that this is the case). – Niklas B. Jan 02 '12 at 01:47
  • Ah sorry. It is showing that no function at all is being exported. –  Jan 02 '12 at 01:51
  • See my updated answer. If further problems exist, please open a new question for those. – Niklas B. Jan 02 '12 at 01:52
  • @NiklasB. The code above: if (hGetProcIDDLL == NULL) Should be changed to if (hGetProcIDDLL != NULL) And the std::cout text changed too to have approp. meaning. – maximus Jan 09 '13 at 05:27
  • @maximus: I see, I exited in the wrong branch. I think it's fixed. – Niklas B. Jan 09 '13 at 13:07
  • I'd like to add one more thing: when calling from windows native c++ (i.e. function from user32.dll, but also any other function with __stdcall calling convention) typedef should look like this: `typedef int (_stdcall*f_funci)();` – baderman May 20 '14 at 14:10
  • @NiklasB. visual studio highlights both versions as a keyword, and it does work with both versions too. But it'll be probably better, when one will use identical name as declared in function. Unfortunately I can't correct the above comment. – baderman May 20 '14 at 14:16
  • 1
    Note that you need to call `FreeLibrary()` at the end of the program. – Lundin May 20 '14 at 14:19
  • @Lundin Certainly not at the end of the program (it will be freed anyway), but maybe if you don't need it any longer during the runtime of the program. But if it's a common library it will be in memory anyway (and shared between processes) – Niklas B. May 20 '14 at 14:20
  • @NiklasB. It is good programming practice to clean up your own mess, no matter what the OS might do. And of course if you don't need it but want to continue execution, you should free it as well. – Lundin May 20 '14 at 14:38
  • 1
    Why `__stdcall`? On Windows 10 with VS2013 this causes name mangling for me and prevents `GetProcAddress()` from finding the function. `__cdecl` does work for me. Also, it's not *impossible* to link all the dll functions upon loading a dll (but it's very likely undesirable). I don't know the equivalent on Windows, but on Linux this is done by passing `RTLD_NOW` to `dlopen()`. This is an unusual thing to do with dynamic loading but it's basically what happens with dynamic linking. – Praxeolitic Apr 24 '16 at 03:05
  • The part in the snippet where you demonstrated the usage of calling conventions in dynamic function calls was very helpful. Thanks. – golosovsky Oct 25 '16 at 15:19
  • Does this code actually work? `funci` doesn't use C linkage and should be mangled. You shouldn't be able to find it like that without an `extern "C"` on the declaration of `funci`. – Timo Apr 23 '20 at 09:00
40

In addition to the already posted answer, I thought I should share a handy trick I use to load all the DLL functions into the program through function pointers, without writing a separate GetProcAddress call for each and every function. I also like to call the functions directly as attempted in the OP.

Start by defining a generic function pointer type:

typedef int (__stdcall* func_ptr_t)();

What types that are used aren't really important. Now create an array of that type, which corresponds to the amount of functions you have in the DLL:

func_ptr_t func_ptr [DLL_FUNCTIONS_N];

In this array we can store the actual function pointers that point into the DLL memory space.

Next problem is that GetProcAddress expects the function names as strings. So create a similar array consisting of the function names in the DLL:

const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] = 
{
  "dll_add",
  "dll_subtract",
  "dll_do_stuff",
  ...
};

Now we can easily call GetProcAddress() in a loop and store each function inside that array:

for(int i=0; i<DLL_FUNCTIONS_N; i++)
{
  func_ptr[i] = GetProcAddress(hinst_mydll, DLL_FUNCTION_NAMES[i]);

  if(func_ptr[i] == NULL)
  {
    // error handling, most likely you have to terminate the program here
  }
}

If the loop was successful, the only problem we have now is calling the functions. The function pointer typedef from earlier isn't helpful, because each function will have its own signature. This can be solved by creating a struct with all the function types:

typedef struct
{
  int  (__stdcall* dll_add_ptr)(int, int);
  int  (__stdcall* dll_subtract_ptr)(int, int);
  void (__stdcall* dll_do_stuff_ptr)(something);
  ...
} functions_struct;

And finally, to connect these to the array from before, create a union:

typedef union
{
  functions_struct  by_type;
  func_ptr_t        func_ptr [DLL_FUNCTIONS_N];
} functions_union;

Now you can load all the functions from the DLL with the convenient loop, but call them through the by_type union member.

But of course, it is a bit burdensome to type out something like

functions.by_type.dll_add_ptr(1, 1); whenever you want to call a function.

As it turns out, this is the reason why I added the "ptr" postfix to the names: I wanted to keep them different from the actual function names. We can now smooth out the icky struct syntax and get the desired names, by using some macros:

#define dll_add (functions.by_type.dll_add_ptr)
#define dll_subtract (functions.by_type.dll_subtract_ptr)
#define dll_do_stuff (functions.by_type.dll_do_stuff_ptr)

And voilà, you can now use the function names, with the correct type and parameters, as if they were statically linked to your project:

int result = dll_add(1, 1);

Disclaimer: Strictly speaking, conversions between different function pointers are not defined by the C standard and not safe. So formally, what I'm doing here is undefined behavior. However, in the Windows world, function pointers are always of the same size no matter their type and the conversions between them are predictable on any version of Windows I've used.

Also, there might in theory be padding inserted in the union/struct, which would cause everything to fail. However, pointers happen to be of the same size as the alignment requirement in Windows. A static_assert to ensure that the struct/union has no padding might be in order still.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    This C style approach would work. But wouldn't it be appropriate to use a C++ construct to avoid the `#define`s? – harper May 20 '14 at 15:01
  • @harper Well in C++11 you could use `auto dll_add = ...`, but in C++03 there is no construct I could think of that would simplify the task (I also don't see any particular problem with the `#define`s here) – Niklas B. May 20 '14 at 17:24
  • 1
    Since this is all WinAPI-specific, you don't need to typedef your own `func_ptr_t`. Instead you can use `FARPROC`, which is the return type of `GetProcAddress`. This could allow you to compile with a higher warning level without adding a cast to the `GetProcAddress` call. – Adrian McCarthy Sep 12 '16 at 23:53
  • @NiklasB. you can only use `auto` for one function at a time, which defeats the idea of doing it once for all in a loop. but what's wrong with an array std::function – Francesco Dondi Oct 18 '16 at 13:42
  • 1
    @Francesco the std::function types will differ just like the funcptr types. I guess variadic templates would help – Niklas B. Oct 18 '16 at 17:50
  • You're completely right, of course. Should I delete the stupid comment in cases like this? Maybe it would be less confusing for someone skimming through. – Francesco Dondi Oct 19 '16 at 12:04
  • @Lundin Couldn't you remove the need to either access functions through a macro or by using "by_type", simply by embedding "functions_struct" inside of "functions_union" as an anonymous struct and access the functions through "functions_union". Such as the following: `typedef union { struct { int (__stdcall* add)(int, int); }; func_ptr_t func_ptr [DLL_FUNCTIONS_N]; } functions_union;` – Bja Dec 13 '18 at 17:04
1

This is not exactly a hot topic, but I have a factory class that allows a dll to create an instance and return it as a DLL. It is what I came looking for but couldn't find exactly.

It is called like,

IHTTP_Server *server = SN::SN_Factory<IHTTP_Server>::CreateObject();
IHTTP_Server *server2 =
      SN::SN_Factory<IHTTP_Server>::CreateObject(IHTTP_Server_special_entry);

where IHTTP_Server is the pure virtual interface for a class created either in another DLL, or the same one.

DEFINE_INTERFACE is used to give a class id an interface. Place inside interface;

An interface class looks like,

class IMyInterface
{
    DEFINE_INTERFACE(IMyInterface);

public:
    virtual ~IMyInterface() {};

    virtual void MyMethod1() = 0;
    ...
};

The header file is like this

#if !defined(SN_FACTORY_H_INCLUDED)
#define SN_FACTORY_H_INCLUDED

#pragma once

The libraries are listed in this macro definition. One line per library/executable. It would be cool if we could call into another executable.

#define SN_APPLY_LIBRARIES(L, A)                          \
    L(A, sn, "sn.dll")                                    \
    L(A, http_server_lib, "http_server_lib.dll")          \
    L(A, http_server, "")

Then for each dll/exe you define a macro and list its implementations. Def means that it is the default implementation for the interface. If it is not the default, you give a name for the interface used to identify it. Ie, special, and the name will be IHTTP_Server_special_entry.

#define SN_APPLY_ENTRYPOINTS_sn(M)                                     \
    M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, def)                   \
    M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, special)

#define SN_APPLY_ENTRYPOINTS_http_server_lib(M)                        \
    M(IHTTP_Server, HTTP::server::server, http_server_lib, def)

#define SN_APPLY_ENTRYPOINTS_http_server(M)

With the libraries all setup, the header file uses the macro definitions to define the needful.

#define APPLY_ENTRY(A, N, L) \
    SN_APPLY_ENTRYPOINTS_##N(A)

#define DEFINE_INTERFACE(I) \
    public: \
        static const long Id = SN::I##_def_entry; \
    private:

namespace SN
{
    #define DEFINE_LIBRARY_ENUM(A, N, L) \
        N##_library,

This creates an enum for the libraries.

    enum LibraryValues
    {
        SN_APPLY_LIBRARIES(DEFINE_LIBRARY_ENUM, "")
        LastLibrary
    };

    #define DEFINE_ENTRY_ENUM(I, C, L, D) \
        I##_##D##_entry,

This creates an enum for interface implementations.

    enum EntryValues
    {
        SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_ENUM)
        LastEntry
    };

    long CallEntryPoint(long id, long interfaceId);

This defines the factory class. Not much to it here.

    template <class I>
    class SN_Factory
    {
    public:
        SN_Factory()
        {
        }

        static I *CreateObject(long id = I::Id )
        {
            return (I *)CallEntryPoint(id, I::Id);
        }
    };
}

#endif //SN_FACTORY_H_INCLUDED

Then the CPP is,

#include "sn_factory.h"

#include <windows.h>

Create the external entry point. You can check that it exists using depends.exe.

extern "C"
{
    __declspec(dllexport) long entrypoint(long id)
    {
        #define CREATE_OBJECT(I, C, L, D) \
            case SN::I##_##D##_entry: return (int) new C();

        switch (id)
        {
            SN_APPLY_CURRENT_LIBRARY(APPLY_ENTRY, CREATE_OBJECT)
        case -1:
        default:
            return 0;
        }
    }
}

The macros set up all the data needed.

namespace SN
{
    bool loaded = false;

    char * libraryPathArray[SN::LastLibrary];
    #define DEFINE_LIBRARY_PATH(A, N, L) \
        libraryPathArray[N##_library] = L;

    static void LoadLibraryPaths()
    {
        SN_APPLY_LIBRARIES(DEFINE_LIBRARY_PATH, "")
    }

    typedef long(*f_entrypoint)(long id);

    f_entrypoint libraryFunctionArray[LastLibrary - 1];
    void InitlibraryFunctionArray()
    {
        for (long j = 0; j < LastLibrary; j++)
        {
            libraryFunctionArray[j] = 0;
        }

        #define DEFAULT_LIBRARY_ENTRY(A, N, L) \
            libraryFunctionArray[N##_library] = &entrypoint;

        SN_APPLY_CURRENT_LIBRARY(DEFAULT_LIBRARY_ENTRY, "")
    }

    enum SN::LibraryValues libraryForEntryPointArray[SN::LastEntry];
    #define DEFINE_ENTRY_POINT_LIBRARY(I, C, L, D) \
            libraryForEntryPointArray[I##_##D##_entry] = L##_library;
    void LoadLibraryForEntryPointArray()
    {
        SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_POINT_LIBRARY)
    }

    enum SN::EntryValues defaultEntryArray[SN::LastEntry];
        #define DEFINE_ENTRY_DEFAULT(I, C, L, D) \
            defaultEntryArray[I##_##D##_entry] = I##_def_entry;

    void LoadDefaultEntries()
    {
        SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_DEFAULT)
    }

    void Initialize()
    {
        if (!loaded)
        {
            loaded = true;
            LoadLibraryPaths();
            InitlibraryFunctionArray();
            LoadLibraryForEntryPointArray();
            LoadDefaultEntries();
        }
    }

    long CallEntryPoint(long id, long interfaceId)
    {
        Initialize();

        // assert(defaultEntryArray[id] == interfaceId, "Request to create an object for the wrong interface.")
        enum SN::LibraryValues l = libraryForEntryPointArray[id];

        f_entrypoint f = libraryFunctionArray[l];
        if (!f)
        {
            HINSTANCE hGetProcIDDLL = LoadLibraryA(libraryPathArray[l]);

            if (!hGetProcIDDLL) {
                return NULL;
            }

            // resolve function address here
            f = (f_entrypoint)GetProcAddress(hGetProcIDDLL, "entrypoint");
            if (!f) {
                return NULL;
            }
            libraryFunctionArray[l] = f;
        }
        return f(id);
    }
}

Each library includes this "cpp" with a stub cpp for each library/executable. Any specific compiled header stuff.

#include "sn_pch.h"

Setup this library.

#define SN_APPLY_CURRENT_LIBRARY(L, A) \
    L(A, sn, "sn.dll")

An include for the main cpp. I guess this cpp could be a .h. But there are different ways you could do this. This approach worked for me.

#include "../inc/sn_factory.cpp"