Is it possible to use std::unique_ptr to manage DLL resource?
Asked Answered
V

4

10

I have many LoadLibrary in my project, and need to call FreeLibrary manually for each LoadLibrary. I want to use the std::unique_ptr with specific deleter to make it auto release my dll resource.

This is what I am trying to define:

std::unique_ptr<HMODULE, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);

But the compiler complains the type does not match. I found out it expects *HMODULE from LoadLibrary. That is std::unique_ptr<A> will expect A* as its pointer type. It looks I still need to define a new class to manage DLL resource(LoadLibrary in constructor and FreeLibrary in destructor).

Is is possible to make std::unique_ptr<A> to just expect the A as its pointer type?

Updated,

The following is pros and cons for new class and using std::unique_ptr, summarized from the answers.

Create another dll management class,

pros:

  • Fully controllable to customize for DLL semantic.
  • Isolate the DLL related parts into a class with one responsibility.
  • Easy to extend if need more functionality for DLL like exposing symbol.

cons:

  • Need rebuild the RAII part what stadard auto pointer has been done.
  • Has chance to make mistake in RAII part.
  • Need Declare a new class.

Use std::unique_ptr with custom deleter,

pros:

  • No need to declare another a class.
  • Reuse the RAII part of unique_ptr.
  • Maybe the move semantics prevents DLL Module instance to be copied?

cons:

  • The Dll resource semantic may not fit the standard auto pointer, and error-prone?
  • The template parameter in unique_ptr is complex and hard to find where error is.
  • HMODULE is void*, a type-less type, may be a problem to integrate with unique_ptr?

Please correct me at comment if I am wrong.

Viminal answered 29/7, 2015 at 6:32 Comment(5)
to be honest I think wrapping the DLL in a class is a bit more safe because of the error handling even though it initially make look elegant using unique_ptrLactoscope
@CyberSpock Fully agreed.Patriliny
So far I found that, I can test the whether theDll.get() is null or not to know Is LoadLibrary is succeeded?. By https://mcmap.net/q/476209/-does-the-standard-behavior-for-deleters-differ-between-shared_ptr-and-unique_ptr-in-the-case-of-null-pointers, if the theDll.get() is null, the std::unique_ptr will not call deleter on pointer. So I don't worry I pass a NULL into FreeLibrary.Viminal
My question may be possible duplicate: #12185279Viminal
ATL provides a HANDLE manage class, CHandle. msdn.microsoft.com/en-us/library/5fc6ft2t.aspx#chandle__chandleViminal
W
6

You need to provide a corresponding ::pointer type for unique_ptr, if you use it to manage a resource T which is not referred to by T*. Here T is the first template argument of unique_ptr.

If no ::pointer type is not defined, T* is used. In your case, it's HMODULE* which is wrong.

struct tLibraryDeleter
{
  typedef HMODULE pointer;
  void operator()(HMODULE h) { FreeLibrary(h); }
};

std::unique_ptr<HMODULE, tLibraryDeleter>(::LoadLibraryA("My.dll"));

Check out here and here.

Waddington answered 29/7, 2015 at 7:41 Comment(7)
Yes, this should work. But few more lines of code and you have nice, simple wrapper class instead of weird and error-prone custom deleters for DLLs, which is simple not the right thing to do.Patriliny
I suggest you rethink what you're saying. I have no idea why and how you consider this well-defined custom deleter as weird and error-prone. If you really think this, at least show us how it can go wrong. If you have any concern, take a look at here, and here. The fact is you're reinventing the wheels which is error-prone.Waddington
@MateuszGrzejek Why is the custom deleter error-prone? Is it just a description to tell unique_ptr what release function it should call when it is deleting?Viminal
This answer is what I am looking for. It looks it is the std way to describe the different pointer type for custom type, HMODULE instead *HMODULE, to integrate with std::unique_ptr.Viminal
@Chen Sure. Please keep in mind that if you need a cross-platform library handler or you want more custom behaviors(e.g., do something before the unload), a custom RAII class is your best choice.Waddington
Yeah, currently I just only need the auto release functionality for my dll resource on Windows platform. If I decide to extend more functionality for dll or makes the it more general, I will consider to move it into a dedicated class. Thanks.Viminal
@MateuszGrzejek, actually, NOT reusing the functionality provided by the standard library is more error-prone. Smart pointers are not so easy to write, if you forget to disallow copying, for example your application will crash. A custom deleter gives you all this for free and you can also use it for std::shared_ptr if you need it later.Rex
D
11

According to this page, HMODULE is HINSTANCE, HINSTANCE is HANDLE, HANDLE is PVOID, and PVOID is void *. Which means that HMODULE is a pointer type. So the following should work:

std::unique_ptr<std::remove_pointer_t<HMODULE>, BOOL(*)(HMODULE)> theDll(LoadLibrary("My.dll"), FreeLibrary);
Drunkometer answered 29/7, 2015 at 7:36 Comment(1)
You can also replace BOOL(*)(HMODULE) with decltype(&::FreeLibrary).Netsuke
W
6

You need to provide a corresponding ::pointer type for unique_ptr, if you use it to manage a resource T which is not referred to by T*. Here T is the first template argument of unique_ptr.

If no ::pointer type is not defined, T* is used. In your case, it's HMODULE* which is wrong.

struct tLibraryDeleter
{
  typedef HMODULE pointer;
  void operator()(HMODULE h) { FreeLibrary(h); }
};

std::unique_ptr<HMODULE, tLibraryDeleter>(::LoadLibraryA("My.dll"));

Check out here and here.

Waddington answered 29/7, 2015 at 7:41 Comment(7)
Yes, this should work. But few more lines of code and you have nice, simple wrapper class instead of weird and error-prone custom deleters for DLLs, which is simple not the right thing to do.Patriliny
I suggest you rethink what you're saying. I have no idea why and how you consider this well-defined custom deleter as weird and error-prone. If you really think this, at least show us how it can go wrong. If you have any concern, take a look at here, and here. The fact is you're reinventing the wheels which is error-prone.Waddington
@MateuszGrzejek Why is the custom deleter error-prone? Is it just a description to tell unique_ptr what release function it should call when it is deleting?Viminal
This answer is what I am looking for. It looks it is the std way to describe the different pointer type for custom type, HMODULE instead *HMODULE, to integrate with std::unique_ptr.Viminal
@Chen Sure. Please keep in mind that if you need a cross-platform library handler or you want more custom behaviors(e.g., do something before the unload), a custom RAII class is your best choice.Waddington
Yeah, currently I just only need the auto release functionality for my dll resource on Windows platform. If I decide to extend more functionality for dll or makes the it more general, I will consider to move it into a dedicated class. Thanks.Viminal
@MateuszGrzejek, actually, NOT reusing the functionality provided by the standard library is more error-prone. Smart pointers are not so easy to write, if you forget to disallow copying, for example your application will crash. A custom deleter gives you all this for free and you can also use it for std::shared_ptr if you need it later.Rex
R
2

Please, look at unique_hmodule in Windows Implementation Libraries (WIL)

One may use it as is or just borrow implementation.

Rhynchocephalian answered 21/1, 2020 at 19:20 Comment(0)
P
0

It looks I still need to define a new class to manage DLL resource

Why do you think it is a bad idea?

Why not this way?

class DynamicLibrary
{
protected:
    HANDLE      _handle;
    string      _path;

public:
    DynamicLibrary();
    DynamicLibrary(const string& path, bool autoLoad = false); // if(autoLoad) this->Load()

    ~DynamicLibrary(); // if(this->IsLoaded()) this->Unload()

    Result Load(); // LoadLibrary() here
    void Unload(); //FreeLibrary() here

    bool IsLoaded() const; // return this->_handle != nullptr
};

Then, you can even extend this class to make symbols retrieving cleaner:

class DynamicLibrary
{
    //cut

    void* RetrieveSymbol(const char* symName) const;

    template <class Cast_type>
    Cast_type RetrieveSymbolAs(const char* symName) const;
};

Example:

DynamicLibrary lib("XXX.dll", true);
assert( lib.IsLoaded() );

FuncType dll_func = lib.RetrieveSymbolAs<FuncType>("dll_func_name");

Explicit type designed to represent DLL instance is much better, I think. Then, you can store array of such objects and all goes automatically. Clean, simple, easy to implement.

Storing list/array of std::unique_ptr<HANDLE, Some_custom_unloader> seems to me like a bad design.

Patriliny answered 29/7, 2015 at 6:46 Comment(12)
The std::unique_ptr has implemented the delete-on-destruct, so I think why not just reuse its code and just pass the custom deleter. To define a typedef to hide its complex declaration may be less effort to create a explict type class.Viminal
You're avoiding typing but increasing error possibility and reducing code clarity by hiding very important and specific problem in strange constructs instead of simple, easy-to-maintain, customizable class with single responsibility But it's your code - your choice.Patriliny
although not answering his question I think this is the better way +1Lactoscope
I don't see how writing your own class is less error prone than using a standard feature.Drunkometer
@Drunkometer There is no such thing like "standard feature" for handling DLLs in C++. Error handling, symbols loading - do you really want this code to be spread out somewhere? Class means encapsulation. DLL handling is the perfect candidate for this.Patriliny
@MateuszGrzejek OP didn't ask for error handling or symbol loading. unique_ptr is a standard feature and it works perfectly for OP's use case.Drunkometer
This solution may work for him better and it's his choice whether he goes for it or not. If he doesn't like it, he will simply not use my solution. It's not your task to decide what did he ask for and what he didn't.Patriliny
-1: I don't recommend this code. It asks OP to rewrite something that already exists. Moreover, this is error-prone. E.g., is there supposed to be an inheritance hierarchy? Why members are protected? Do you want to prevent the copying? As you don't want a library to be unloaded twice right? etc. The point is, the more code you write, the more mistakes you're likely to make.Waddington
is there supposed to be an inheritance hierarchy? Why members are protected? Do you want to prevent the copying? So, the solution is wrong because it requires thinking, giving you in return well-defined semantics for DLL handling? Let's end this discussion, it's pointless.Patriliny
I personally prefer a dedicated class, but the execution is flawed. E.g. as Eric Z mentioned, the copy c'tor is not suppressed, which can easily result in a double free. That's UB, not "well-defined semantics". In addition to the above criticism, the two argument constructor should be explicit. IMO, the data members should be private and the class final. It should be given move semantics as well.Elasticity
One advantage of an independent class is that it can have the same interface but different implementations for different operating systems.Crowson
Maybe this question #14878621 is one of possible issues that using unique_ptr to manage dll.Viminal

© 2022 - 2024 — McMap. All rights reserved.