0

I am working in Visual Studio C++.

I made an class with a non-static function and packaged it as a dll. Here is the code to generate my dll:

// head file
#ifndef FUNCTIONS_H_
#define FUNCTIONS_H_

#include <string>
#include <memory>

#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport) 
#else
#define MATHFUNCSDLL_API __declspec(dllimport) 
#endif

MATHFUNCSDLL_API class Functions
{
public:
    MATHFUNCSDLL_API void func(int, std::string);
};


extern "C" MATHFUNCSDLL_API Functions * __cdecl create_class();

#endif


// cpp file
#include "stdafx.h"
#include "Functions.h"
#include <iostream>

void Functions::func(int id, std::string name)
{
    std::cout << "Your ID: " << id << ". Your name: " << name << std::endl;
}

Functions * create_class()
{
    std::cout << __FUNCTION__ << std::endl;
    return new Functions();
}

Now I have a C++ project that loads this dll dynamically. Here is the code:

#include <iostream>
#include <Windows.h>
#include "../../testDmcDLL/testDmcDLL/Functions.h"
typedef Functions *(__stdcall *f_funci)();
int main(int argc, char ** argv)
{
    HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents\\Visual Studio 2013\\Projects\\testDmcDLL\\Debug\\testDmcDLL.dll");
    f_funci func_create_class = (f_funci)GetProcAddress(hGetProcIDDLL, "create_class");
    Functions * pf = func_create_class();
    ////LNK error////pf->func(1, "toto");
    system("pause");
    return 0;
}

I can make sure that hGetProcIDDLL and func_create_class have been initialized successfully (I've tested them with if, but here I removed the if).

When I run this project, I can see that create_class is shown on the console because there is std::cout << __FUNCTION__ << std::endl; in that function. So everything looks fine.

However, when I compile it with the code pf->func(1, "toto") uncommented, I get a linker (LNK2019) error:

Error 1 error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall Functions::func(int,class std::basic_string,class std::allocator >)" (__imp_?func@Functions@@QAEXHV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function _main c:\documents\visual studio 2013\Projects\testLoadDmcDLL\testLoadDmcDLL\main.obj testLoadDmcDLL

valiano
  • 16,433
  • 7
  • 64
  • 79
Yves
  • 11,597
  • 17
  • 83
  • 180
  • using absolute paths will lead to problems in the long run - relative ones are to be preferred. – specializt Nov 06 '15 at 12:35
  • also : you can link the DLL statically within Visual Studio - which will remove the need for manual loading entirely ... loading libraries completely by yourself is very error-prone and requires quite some skill, i'd recommend sticking to Visual Studio-based linking instead, that way you wont even need to define any dllimports as everything is done automatically, your DLL functions can even be changed without the need for a rebuild - MSVC will rebuild the DLL once you build/start your app. – specializt Nov 06 '15 at 12:40
  • @specializt: You've got things backwards. Using compile-time linking requires a re-link of an application that links against a DLL, when that DLL changes. With runtime-linking, however, no rebuild of the application is required, when the DLL changes. – IInspectable Nov 08 '15 at 22:44
  • @IInspectable thats exactly what MSVC does - starting applications within Visual Studio requires no re-link or compile. At all, because its done automatically. Thats pretty much how every advanced compiler and IDE works. – specializt Nov 09 '15 at 06:44
  • @specializt: *"requires no re-link or compile [...] because its done automatically"* - I don't understand what you are trying to say here. – IInspectable Nov 09 '15 at 08:27
  • @IInspectable automatic compilation & linking. Visual Studio does it : http://superuser.com/a/135634/69240. In fact, changes to an included DLL-project may be reflected nigh-immediately. – specializt Nov 09 '15 at 10:41
  • @specializt: Automatically invoking a link and not requiring a link are different things. Using compile-time dynamic linking invokes an automatic re-link, when the DLL changes. With runtime dynamic linking, however, no additional linking is required, as the import library (LIB) isn't used. – IInspectable Nov 09 '15 at 13:57
  • @IInspectable Exactly, thats what im saying. – specializt Nov 09 '15 at 16:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94628/discussion-between-iinspectable-and-specializt). – IInspectable Nov 09 '15 at 17:18

2 Answers2

1

The class definition is not quite right with the exports, it should be of the form;

class MATHFUNCSDLL_API Functions // MATHFUNCSDLL_API moved
{
public:
    void func(int, std::string); // MATHFUNCSDLL_API removed
};

Once a class is exported, all its members are exported.

You don't mention how MATHFUNCSDLL_EXPORTS is defined during compilation (from the command line or possibly in the stdafx.h), but make sure it is defined when building the dll, but not when building the exe. Be sure to link against the .lib produced with the .dll.


Notes on the LoadLibrary and GetProcAddress usage; if you require the dll to be loaded dynamically, you need to get the C++ class member function bound to the exported function. I've not seen a successful implementation of this or if it is even reasonable possible. If the use of the LoadLibrary and GetProcAddress is required, consider using an abstract class and create the object in a factory of some sort.

You don't detail the motivation for the dynamic loading of the dll, but consideration could also be given the delay loading the dll.

If the motivation is to delay the loading of the dll, but the same dll is always used, then the delay-load linking may help. If the motivation is to load an unknown dll (by name/location) based on some runtime parameter (or configuration), then the virtual base class and a single C-style function as a factory for the object is probably the preferred solution.

There is a good code project article on this describing various solutions for this. In particular using the abstract base class is very portable.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • The idea of `LoadLibrary/GetProcAddress` is that you _don't_ link against the .lib. – MSalters Nov 06 '15 at 10:35
  • The issue with using the LoadLibrary/GetProcAddress is assigning the member method functions to the class definition members. I've never seen a successful implementation of this (don't know if it is even possible). – Niall Nov 06 '15 at 10:37
  • I have no idea what "assigning the member method functions" even means. (BTW, " method" is a synonym for "member functions" - you're being redundant). Nor do I have an idea what "class definition members" are. – MSalters Nov 06 '15 at 10:41
  • 1
    [wrt the edit] My usual solution is delay-loading of the DLL. In this case, MSVC++ will generate the necessary `GetProcAddress` calls behind the scenes. – MSalters Nov 06 '15 at 10:45
  • in fact I don't quite understand why I should define MATHFUNCSDLL_EXPORTS. Well, I tried to define it when I generate dll, but I still get the same error. – Yves Nov 06 '15 at 10:48
  • I just want to load a dll dynamically to use the class and its members in this dll. – Yves Nov 06 '15 at 10:51
  • The reason to define `MATHFUNCSDLL_EXPORTS` is to make sure that the correct version of the `__declspec()` is used; one to get the export of the class and one for the import. Visual Studio doesn't export all the symbols associated with the code out of the dll, only the ones you specify. – Niall Nov 06 '15 at 10:51
  • @Niall I add `#define MATHFUNCSDLL_EXPORTS` in the head file. It means that `MATHFUNCSDLL_API == __declspec(dllexport) `. So in the class and the function all become `__declspec(dllexport)`. Is this what I should do? – Yves Nov 06 '15 at 10:57
  • No, if you define it in the header, both the dll and the exe see the same version. Define it on the command line for the dll `/DMATHFUNCSDLL_EXPORTS` – Niall Nov 06 '15 at 10:59
  • yes, the motivation is to load an unknown dll (by name/location) based on some runtime parameter (or configuration), meaning that I load a dll file to do something. My colleague can change the dll if he needs but I don't need to touch my things anymore. – Yves Nov 06 '15 at 11:00
  • @Thomas. Then look to use the [abstract base class technique](http://www.codeproject.com/Articles/28969/HowTo-Export-C-classes-from-a-DLL#CppMatureApproach). It will solve the issues you see now and avoid other issues down the line. – Niall Nov 06 '15 at 11:03
  • @Niall thanks a lot. It works! But I don't know why a virtual table can do this. I means why we can call those functions in the dll with a virtual table. Maybe this is a weird question....:D Do you know why? – Yves Nov 06 '15 at 12:21
  • 1
    @Thomas. I don't have a reference for you on that, but the virtual functions are essentially pointers (read up on vtable) that are replaced at runtime with the actual implementation, so they don't need to be (but can be) exported at link time. – Niall Nov 06 '15 at 12:27
  • Thanks dude. You helped me a lot. – Yves Nov 06 '15 at 13:04
  • @Niall: v-tables are constructed at compile-time. When an object is constructed, the compiler assigns a pointer to a v-table (usually as the first class member). Since the type is known at compile-time, so is the v-table that needs to be stored. Nothing of this happens during runtime. – IInspectable Nov 09 '15 at 01:20
  • @Niall: The contents of the v-tables are generated at compile-time. All necessary information is available to the compiler. The virtual dispatch is implemented through indirect calls. Nothing is replaced at runtime. – IInspectable Nov 09 '15 at 08:23
0

If you don't rely on the import library but call GetProcAddress, you need to do that for every function you're importing. You never called GetProcAddress for __imp_?func@Functions@@QAEXHV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (which is how your Functions::func is mangled in the DLL).

Also, be aware that you get a function pointer from GetProcAddress. While that points to the code implementing pf->func, function pointers aren't called with member function call syntax.

The root problem is that GetProcAddress really is designed for C, not C++.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • `GetProcAddress` is neither designed for C nor for C++. It is designed to get the address of a symbol exported from a PE image. Since C and C++ code produces a PE image, `GetProcAddress` is applicable to both. Exported symbols from C are decorated as well, so there really is no difference. – IInspectable Nov 08 '15 at 23:09