1

I have a DLL file which has a class called trial and it contains a function called test, I have another project in which I loaded the DLL using the loadlibrary function from the windows module, now I want to know how to create an object of type trial in the new project.

I tried defining the class as "class __declspec(dllexport) trial" but I don't know how to create an object in the main file now.

Trail.h is as follows:

class __declspec(dllexport) TRIALSHARED_EXPORT Trial
{
public:
    Trial();
    void test();
};

Trail.cpp is as follows:

extern "C"{
Trial::Trial(){
    cout<<"object is created"<<endl;
}
    void Trial:: test(){
    cout<<"dynamic thingy"<<endl;
}
}

The main function is as follows:

int main()
{
    HINSTANCE here=LoadLibrary(L"C:\\Users\\vinay\\Documents\\qt c++\\build-trial-Desktop_Qt_5_12_0_MinGW_64_bit-Debug\\debug\\trial.dll");
    if(!here){
        cout<<"could not load the lib"<<std::endl;
    }
    else{
        cout<<"library loaded"<<endl;
        typedef void(*FNPTR)();
        FNPTR fn=FNPTR(GetProcAddress(here,"Trial"));
        fn();
    }
}
Aimery
  • 1,559
  • 1
  • 19
  • 24
Vinay
  • 81
  • 1
  • 8
  • 2
    You basically need the corresponding header file. – Max Langhof Sep 05 '19 at 12:10
  • Did you consider load-time-binding? That means to not use `LoadLibrary`, but instead using the export library that can be created while the DLL is linked. The program shown with your `main` function would link with the stubs that are defined in the export library. With load-time-binding you still use the same approach with a DLL that implements the/some class(es). https://learn.microsoft.com/en-us/windows/win32/dlls/load-time-dynamic-linking – harper Sep 05 '19 at 12:10
  • @MaxLanghof do i need to include Trial.h in my main? for me to create an object of the type Trial? – Vinay Sep 05 '19 at 12:29
  • @harper i am supposed to achieve the goal via run time linking only. is there a way i can achieve this in run time binding? – Vinay Sep 05 '19 at 12:30
  • That's where the `Trial` class is defined. Without the header, you cannot tell the compiler what `Trial` looks like, so you can't use it in any meaningful way in your code (other than possibly an opaque handle). There is no reflection in C++ (yet), the compiled binary does not contain sufficient information to reconstruct a given type. See for yourself: https://godbolt.org/z/9Ctkxo – Max Langhof Sep 05 '19 at 12:32
  • 2
    Please don't extend your question in comments. If you need to clarify some requirements like "run time linking only", this should be part of the question. – harper Sep 05 '19 at 14:11

2 Answers2

4

If you can't use load-time binding you should use a pattern with a factory. The factory creates an instance of an object that implements an interface. The interface is a abstract class that can be used in the consumer.

Change your header file like this:

class ITrial
{
public:
    virtual void test() = 0;
};

class Trial : ITrial
{
public:
    Trial();
    virtual void test() override;
};

// declare a function name that can be easily found with GetProcAdress.
extern "C" TRIALSHARED_API ITrial* CreateTrial();
typedef ITrial* (*PFN_Factory)();

Edit: The class ITrial is the interface to the functionality. You define a contract between the implementation and the consumer of the DLL. Only members that are defined here can be accessed. The DLL defines a class that is inherited from the interface class as the implementing class. The implementing class can have any additional member as it needs.
Edit end.

The header file must not include the __declspec(dllexport). The macro TRIALSHARED_EXPORT should be defined using conditional compilation.

#ifdef TRIAL_EXPORTS // defined only in the exporting DLL project
#define TRIALSHARED_API __declspec(dllexport)
#else
#define TRIALSHARED_API __declspec(dllimport)
#endif

Add to your Trial implementation:

ITrial* CreateTrial()
{
    return new Trial;
}

Change your main function:

int main()
{
    auto here=LoadLibrary(L"C:\\Users\\vinay\\Documents\\qt c++\\build-trial-Desktop_Qt_5_12_0_MinGW_64_bit-Debug\\debug\\trial.dll");
    if(!here) {
        cout<<"could not load the lib"<<std::endl;
        return 1;
    }
    auto factory = reinterpret_cast<PFN_Factory>(GetProcAddress(here, "CreateTrial"));
    if (!factory) {
        cout<<"could not find factory"<<std::endl;
        return 1;
    }
    auto pTrial = factory();
    if (!pTrial) {
        cout<<"factory failed"<<std::endl;
        return 1;
    }
    pTrial->test();
    delete pTrial;
}

The error handling is a bit clumsy. Change it as you need.

harper
  • 13,345
  • 8
  • 56
  • 105
  • OK, so you can export classes using the factory approach! But it's clumsy, messy, and prone to mis-use. – Adrian Mole Sep 05 '19 at 14:02
  • @Adrian it also allows backwards-compatibility of new versions and not having to re-compile everything that depends on the library when you extend it. – PeterT Sep 05 '19 at 14:40
  • Thanks! i kinda used a similar approach and was able to solve the problem. – Vinay Sep 06 '19 at 06:29
3

There are numerous errors in your code and, more importantly, in the approach you are taking. First, you need to define class Trial as __declspec(dllexport) when you build the DLL but as __declspec(dllimport) when you build the EXE! This is conventionally done using pre-processor decisions, like so:

#ifdef _DLL // Assuming a Windows DLL build
#define IMPOREXP __declspec(dllexport)
#else
#define IMPOREXP __declspec(dllimport)
#endif
class IMPOREXP Trial
{
public:
    Trial();
    void test();
};

Next - and critically - you can't use a class defined in a DLL unless you use implicit 'load-time binding'! The LoadLibrary() and GetProcAddress() functions can do nothing about exported/imported classes.

I can give more, if you think it will be useful: comment a question or 'accept', and I'll try to guide you through the process with further examples.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 2
    The `#idef _DLL` works only if the header file is not included in a consuming DLL. To make this conditional compile robust you need to check a define that's unique in the implementing DLL preprocessor defines. – harper Sep 05 '19 at 13:57
  • @harper Re: '#ifdef _DLL` - Yes, my code sample was a 'conventional' example. – Adrian Mole Sep 05 '19 at 13:59