2

My situation is as follows: I have a .dll file and a .h header with a purely virtual class MainWindow declaration. There are two type definitions in the header file:

typedef MainWindow* (*CREATE_INTERFACE)();
typedef void (*DELETE_INTERFACE)(MainWindow* Window);

which allow me to create and delete an object of the same type as a virtual class.

I would like that loading support for this dynamic library to be defined in my class, let's call it class Loader. I would like one of my class members to be a smart pointer defined as follows:

std::unique_ptr <MainWindow,DELETE_INTERFACE> UI_Driver;

The simplified code below (without error handling):

class Loader
{
    private:
    HINSTANCE DllHandle;
    CREATE_INTERFACE FactoryFunction;
    DELETE_INTERFACE CustomDeleterFunction;
    std::unique_ptr<MainWindow,DELETE_INTERFACE> UI_Driver;

    std::unique_ptr<MainWindow,DELETE_INTERFACE> LoadExternalLibrary()
    {
        std::wstring WideFileName=L"MainWindow.dll";
        std::string FileName(std::begin(WideFileName),std::end(WideFileName));
        this->DllHandle=::LoadLibrary(WideFileName.c_str());
        // Get the function from the DLL
        FactoryFunction=reinterpret_cast<CREATE_INTERFACE>(::GetProcAddress(DllHandle,"Create"));
        CustomDeleterFunction=(DELETE_INTERFACE)GetProcAddress(DllHandle,"Delete");
        return std::unique_ptr<MainWindow,DELETE_INTERFACE>(FactoryFunction(),CustomDeleterFunction);
     };

public:

     UI_Service() : UI_Driver(LoadExternalLibrary())
     {

     }

     ~UI_Service()
     {
         if(UI_Driver)
             ::FreeLibrary(this->DllHandle);
     }

     void ShowWindow()
     {
        UI_Driver->Show();
     }
};

The code compiles correctly, loading functions from the .dll library is also succesfull. A MainWindow defined in header has a Show() method that draws the user interface. If I try to call it like above with the ShowWindow() method from my class Loader, the window does not appear.

int main()
{
 Loader MyLoader;
 MyLoader.ShowWindow(); // <- window does not appear, program crashes
}

BUT it appears if I call this method immediately after creating the pointer in the LoadExternalLibrary() method as below:

so instead:

return std::unique_ptr<Eol_MainWindow_Driver_Generic,DELETE_INTERFACE>(FactoryFunction(),CustomDeleterFunction);

i can write:

std::unique_ptr<Eol_MainWindow_Driver_Generic,DELETE_INTERFACE> UI(FactoryFunction(),CustomDeleterFunction);
UI->Show(); // <- Window appears
return UI; // <- It will never happen because the Show() is blocking the thread

What is going on? Why does the created unique_ptr with custom deleter stop working after copying?

EDIT: I found the answer partially -
FactoryFunction and CustomDeleterFunction only work in LoadExternalDll function scopes. I have no idea why.

Umberto
  • 25
  • 3
  • Is the issue reproducible with the simplified code you provided or only with the full code? – Rinat Veliakhmedov Mar 06 '20 at 12:27
  • Since you are using `c++11` I'm guessing the custom deleter is being called with a `nullptr` after the move when you return it by value. Have you looked at the implementation of the deleter? Does calling it with a `nullptr` break something? – super Mar 06 '20 at 12:52
  • @RinatVeliakhmedov Result of simplified code is exactly the same. – Umberto Mar 06 '20 at 13:00
  • @super extern "C" __declspec(dllexport) void __stdcall Delete(MainWindow* Driver) { std::cout<<"Custom deleter works."< – Umberto Mar 06 '20 at 13:01
  • @super deleter prints to standard output if it is called - it not happens here. – Umberto Mar 06 '20 at 13:14
  • You're right. I checked as well on old compiler version for both gcc and clang. It does not get called when the pointer is `nullptr`. – super Mar 06 '20 at 13:15
  • I don't see any issues with using unique_ptr here, so I don't know what's going on. Only thing I can suggest is to go trough the program step by step with a debugger. – Rinat Veliakhmedov Mar 06 '20 at 13:30
  • @RinatVeliakhmedov Debbuger I give up. The debugger shows me segmentation fault. There is no way to copy this pointer even without custom deleter. Even raw pointer could be used only betwen LoadExternalDll() scopes{}; I checked it with gcc and clang. No idea what is going on. – Umberto Mar 06 '20 at 14:55

1 Answers1

0

I personally feels that this is not the problem related to the smart pointer with custom delete function. Because there is a concept called return value optimisation, which happens when function return a std::unique_ptr by value and no copy/move happens. you can read it more here Copy_elision.

To show this, i have created a small example having std::unique_ptr with custom delete function.

#include <iostream>

#include <memory>
#include <vector>

using customDeleteType = void(*)(std::vector<int*>*);

class Demo{
public:
    Demo();
    ~Demo();

    void printVector() const;
private:
    std::unique_ptr<std::vector<int*>, customDeleteType> populateVector() const;

    std::unique_ptr<std::vector<int*>, customDeleteType> vec;
};

Demo::Demo(): vec(populateVector()){

}

Demo::~Demo(){

}

void Demo::printVector() const{
    for(int* i: *vec){
        std::cout<< *i<< " ";
    }
    std::cout<< '\n';
}

std::unique_ptr<std::vector<int*>, customDeleteType> Demo::populateVector() const
{
    return std::unique_ptr<std::vector<int*>, customDeleteType>(
                new std::vector<int*>{new int{0}, new int{1}},
                [](std::vector<int*>* v){ for(int* i: *v){delete i;} delete v;}
                );
}

int main(int , char *[]){
    Demo d;
    d.printVector();
}

output: 0 1

Vikas Awadhiya
  • 290
  • 1
  • 8