Introduction
Windows resources, like GDI handles, are hard to manage correctly without
creating leaks. This article shows how smart pointers can simplify this, and
almost eliminate a major source of errors. It continues my previous article,
Smart Pointers to boost your code,
providing a practical example for using shared_ptr. (If you are not
familiar with boost::shared_ptr, you should read it now!)
Update: While the first version was more or less like "concept art", I
brushed up the sources a bit to make the more useful. I added a
reset() function that sets the HandleRef to 0, brushed up the
sources a bit and added a special implementation for HDC's. Also, I fixed some
snippets here, and added a few things to the article.
Contents
- Background will examine the problem in
detail, and discuss the common solutions.
- Smart Pointers to rescue will show how
smart pointers can help here, with a simple example.
- Developing a Solution will
develop the idea into a set of template classes, that make reusing the idea
flexible and allow adding new types. Sample code discusses the drop-in header
provided.
- Using the Solution - jump here if you
want to know how to use it.
- CDCRef for Device Contexts
(new)
- Discussion (new)
Many resource handles encountered in Win32 programming do not fit an
object-oriented environment very well. Here is a list of problems:
- How you acquire the handle determines if you have to call some cleanup
function on it. If you use
CreateFont, you have to call
DeleteObject when you no longer need it. However, if you got the
HFONT from a Window, you must not delete the font.
- There is no way to tell from a handle whether we should delete it or have to
leave it alone.
- There are many handle types, with many different Delete/Release functions,
which must be matched exactly.
- Handles have "pointer" semantics, i.e., copy constructor and assignment
create only a new reference to the actual resource. This is desirable for
performance reasons, however, this makes it complicated to use RAII in an object
oriented environment.
If you have a function returning such a handle, you must at least specify if
and when to release the handle. Things get much more complicated if the handle
is a member of the class. Take a look at this innocent code snippet:
class CMessage
{
protected:
HFONT m_font;
public:
~CMessage();
};
CMessage CreateMessage(CString const & msgText,
LPCTSTR fontName, int fontSize);
Code Snippet (1): Demonstrating the problem
Let me ask one question: Should the destructor of CMessage call
DeleteObject(m_font)?
- Yes? Then the return value of
CreateMessage will give
you a corrupted font handle to a font that does not exist anymore.
- No? Who will delete the font then?
One way or another, the user of the class is concerned with managing your
HFONT resource, or what he can do with your class is severely
limited.
A multitude of solutions exist, some are simple, some not:
- Prohibiting Copy Constructor, and Assignment operator (you can't have the
CreateMessage function, then).
- Keeping a "
bool deleteTheFont" flag together with the font
handle. (I worked with std::pair<HANDLE, bool> for a while,
but this is still a pain.)
- Count the references to the font. (E.g., the copy constructor would have to
increment the reference count.)
- Use some kind of internal reference counting. (This works for File, Event,
Thread, and many other handles, using
DuplicateHandle. However,
this is another can of worms opening up. No such luck with GDI handles.)
- Always copying the object (complicated, expensive, sometimes not possible).
- Wrapping one of the solutions into a
CFont class.
The nature of handles makes them prime candidates for a reference counting
mechanism:
- They act as "references to resources", but the resources themselves
cannot/should not be copied.
- The same resource is reused in many places, and must not be deleted as long
as someone is using it.
- It should, however, be released as soon as possible, to "play nice" with the
system resources.
- These resources discussed here don't reference each other, so circular
references should virtually never appear.
Further, we would like to keep the possibility of "unmanaged" handles (i.e.,
it is not deleted automatically). This flag should be set when the smart pointer
is created (close to where we acquire the resource, because this is the place
where we know how to treat it).
See below: Why Not MFC?, why the MFC solution
doesn't do it for me.
As seen above, a reference counting smart pointer is ideal to handle
Handle.
Why boost::shared_ptr is great for this:
shared_ptr does not put any requirements on the type of the
resource. (We don't need to inherit HFONT from some
CRefCountable class. Phew!)
shared_ptr allows a custom deleter, which can do the
resource-specific cleanup. (DeleteObject for fonts,
CloseRegKey for handles, etc.)
- The custom deleter also allows to not delete the resource
automatically.
Note: I mentioned custom deleters only briefly (if at all) in
my previous article. See the boost
documentation for more.
Let's look at the example for an HFONT:
void delete_HFONT(HFONT * p)
{
_ASSERTE(p != NULL);
DeleteObject(*pFont);
delete pFont;
};
typedef boost::shared_ptr<HFONT> CFontPtr;
CFontPtr CreateFontPtr(HFONT font, bool deleteOnRelease)
{
if (deleteOnRelease) {
return CFontPtr(new HFONT(font), delete_HFONT);
}
else {
return CFontPtr(new HFONT(font));
}
}
Code Snippet (2): The initial idea
The line (A) is the "magic" one: here we initialize the CFontPtr
as usual, but specify that the object should be deleted using
delete_HFONT when the last reference is gone.
Now, we can make heavy use of CFontPtr: we can use it as return
value from functions. We can have it as class member, and the default copy
constructor, assignment operator, and destructor do exactly what they
should.
Do you remember the first rule for using smart pointers? Put the resource
into a smart pointer as soon as you get it. This rule holds up here as well:
because when we get the font handle, we know exactly if it should be deleted or
not. The smart pointer will carry around this flag, and automatically "do what
we want".
There are some problems even this solution cannot solve:
- Someone could delete the font behind our back.
- We could create the font pointer with
deleteOnRelease = false,
and then forget to delete it ourselves.
But such is life in C++, you are always free to shoot yourself in the foot
you like most.
Some problems, however, can be solved better:
But this is the question of the next part.
This paragraph shows how to turn the concept into a complete extensible
solution. It might be helpful in understanding how template libraries evolve
into the complex beasts they are.
The following goals were set:
Encapsulation
First, we encapsulate the details of the smart pointer, solving the first two
requirements:
class CFontRef
{
protected:
typedef boost::shared_ptr<HFONT> tBoostSP;
tBoostSP m_ptr;
public:
explicit CFontRef(HFONT font, bool deleteOnRelease)
{
if (deleteOnRelease)
m_ptr = tBoostSP(new HFONT(font), delete_HFONT);
else
m_ptr = tBoostSP(new HFONT(font));
}
operator HFONT() const { return m_ptr ? *m_ptr : NULL; }
};
Code Snippet (3): Moving to a specialized class
You might (or should) notice the following things:
- The public interface is reduced to the bare minimum. (A "good thing".)
- The
CreateFontPtr function has become the constructor.
- An automatic cast operator allows both the if (font) test, and using the
class in a place where a
HFONT is expected.
- The deleter remains an associated function (not shown here).
- The class was renamed from "
Ptr" to "Ref", since
it syntactically cast more like a reference than a pointer.
- The constructor does not specify a default parameter. This was done since no
value is the "obvious default". Further, it makes the construction explicit (so
the
explicit keyword isn't really needed).
- I
typedef'ed the boost pointer inside the class. It occurs in
quite a few places, the typedef makes the code easier to read, but
"outside" the class, no one really needs it.
Another note: Now would be a good time to make the simplification used
below. However, I'd like to go the "safe & clean way" a bit
further.
Second, we can make both the Handle type and the Deleter a template
parameter. It's simple for the Handle, but some compilers can't handle a
function as template argument. The standard solution is to turn the Deleter
function into a functor - that is, a class that overloads
operator().
template <typename HDL, typename DELETER>
class CHandleRefT
{
protected:
typedef boost::shared_ptr<HDL> tBoostSP;
tBoostSP m_ptr;
public:
CHandleRefT(HDL h, bool deleteOnRelease)
{
if (deleteOnRelease) m_ptr = tBoostSP(new HDL(h), DELETER());
else m_ptr = tBoostSP(new HDL(h));
}
operator HDL() const { return m_ptr ? *m_ptr : NULL; }
};
struct CDeleterHFONT
{
void operator()(HFONT * p) { DeleteObject(*p); delete p; }
};
typedef CHandleRefT<HFONT, CDeleterHFONT> CFontRef;
Code snippet (4): turning the class into a template
DeleteObject is used for many types, so we don't want to write
our own deleter for each. However, we would like to keep everything strictly
typed, so we make it a template again:
template <typename GDIHANDLE>
struct CDeleter_GDIObject
{
void operator()(GDIHANDLE * p) { DeleteObject(*p); delete p; }
};
typedef CHandleRef<HFONT, CDeleter_GDIObject<HFONT> > CFontRef;
Code snippet (5): a helper template
Specialized, more lightweight implementation
There are two things still bugging me:
- The deleters still need to remember the "
delete p" part.
- I would like to avoid the heap copy of the resource handle if possible.
The first problem could be solved with another helper template, but that
wouldturn the actual type of CFontRef into something like
boost::shared_ptr<HFONT, GenericDeleter<
DeleteObjectDeleter<HFONT> > >. I see no solution for the
second in a generic approach.
However, both solve themselves when using some internal knowledge about
Windows: all resource handles in Windows can be represented by a void
*, and we can use a cast to get the strictly typed handle. This is so
deeply rooted in the Win32 API (in fact, if you #undef the
STRICT macro, most handle types are declared as void
*), that we won't see a change till .NET takes over completely. Further,
boost::shared_ptr can use void as template
argument.
So, with the following code, we can go:
struct CDeleter_Void
{
void operator()(void *) {}
}
template <typename HDL, typename DELETER>
class CHandleRef
{
protected:
typedef boost::shared_ptr<void> tBoostSP;
tBoostSP m_ptr;
public:
explicit CHandleRef(HDL h, bool deleteOnRelease)
{
if (deleteOnRelease)
m_ptr = tBoostSP(h, DELETER());
else
m_ptr = tBoostSP(h, CDeleter_Void());
}
operator HDL() const { return (HDL) m_ptr.get(); }
};
struct CDeleter_GDIObject
{
void operator()(void * p) { DeleteObject( (HGDIOBJ) p); }
};
typedef CHandleRef<HFONT, CDeleter_GDIObject> CFontRef;
Code snippet (6): the final code
Before, the smart pointer stored a pointer to our resource handle. Now, we
store the resource handle directly in the smart pointer (represented as
void *).
(I'll repeat a few things here for the impatient who skipped over all
that boring explanation stuff)
CHandleRefT is a template class implementing a counted reference
to a Windows resource handle. Usage rules are similar to a reference counted
smart pointer. It is used to implement a variety of Windows Resource handles,
like:
HIMAGELIST ==> CImageListRef
HMENU ==> CMenuRef
HANDLE ==> CHandleRef
HBITMAP ==> CBitmapRef
HBRUSH ==> CBrushRef
HPEN ==> CPenRef
Template parameters:
HDL: type of the resource handle (e.g., HFONT).
DELETER : a functor releasing the resources of Handle of type
HDL (e.g., a functor calling DeleteObject). The handle is passed as
void *.
A custom deleter can be passed to the constructor.
However, DELETER is not a template parameter of the class (which avoids template
propagation)
Automatic vs. manual handles
When constructing a HandleRef from a raw handle, you pass a
bDeleteOnRelease flag indicating if the handle should be released
automatically when the last reference to it goes out of scope.
For an automatic handle, assign the raw handle to a HandleRef
immediately after construction, specifying bDeleteOnRelease=true.
Then, pass it around only as HandleRef. This guarantees the handle
is deleted when it is no longer used.
For a manual handle, that you will delete yourself manually, or that must not
be deleted, specify bDeleteOnRelease=false.
The idea here is that you can pass around, copy, store, or return a
HandleRef, it remembers its deletion policy.
Guidelines
Utility functions that merely receive and use a resource handle (without
storing it) may use the raw handle as argument. However, when the Handle is
stored (e.g., as a class member) or used as a return value from a function, a
HandleRef is recommended.
As an example, the CImageListRef class has the following
members:
Constructor
CImageListRef(HIMAGELIST il, bool deleteOnRelease)
Initializes a new CImageListRef.
il [HIMAGELIST]: Image list to hold
deleteOnRelease [bool]: if true, the image List il
is destroyed when the last reference to it (made through the
CImageListRef instance) goes out of scope. The correct destroy
function (ImageList_Destroy) is used.
operator HIMAGELIST
- implicitly casts the
CImageListRef to the contained
HIMAGELIST.
reset()
Releases the image list.
To add support for a new type:
The type must be convertible to and from void *.
- Write a Delete functor for the type:
struct CDeleter_MyType { void operator()(void * p) { MyTypeRelease(p); }
- use a
typedef typedef CHandleRef<CMyType, CDeleter_MyType> CMyTypeRef;
MFC tried to solve the problem with its own wrappers. Unfortunately, they
chose to wrap the first option from the "possible solutions" above: no copy
functionality, but they pass around CFont *. This might have been a
good design decision back then, but now, it is just a pain. Consider a function
returning a CFont *. Now, there are two objects that need to be
released correctly: the Windows HFONT, and the CFont C++ object. Should you:
- Delete the
CFont * when you are done with it, because it was
allocated dynamically?
- Not delete the
CFont *, but use it only until some other class
is destroyed (because the other class "holds" the font)?
- Not delete the font, but use it only in the current message handler (because
it's a temporary MFC object that is deleted in the next
OnIdle
call)?
- Detach the
HFONT from the CFont * before deleting
it (because you have to get rid of the MFC object, but the Windows resource is
still in use somewhere)?
When turning UI code into general routines and classes, I regularly stumble
over all these four scenarios. It is not a pleasant experience, I can tell you -
so I end up using raw resource handles instead, to have "only the normal
problems".
Further, I like UI code not to be dependent on MFC, if possible. The best
code IMO is a library that relies solely on the Win32 API interface, and does
not put any requirements on the framework.
The CDCRef class, referencing a HDC, deserves
additional discussion.
Device Contexts are the most complicated resource I've come across: There are
two cleanup functions: DeleteDC and ReleaseDC,
and the latter needs an additional parameter (the HWND the DC was
acquired from).
CDCRef is implemented as separate class, since I didn't want the complexity
of HDC to "creep" into the Other HandleRef classes. The main difference is the
constructor - instead of a flag, you directly pass the deleter:
CDCRef(hdc, CDCRef::Null())
CDCRef(hdc, CDCRef::Delete())
CDCRef(hdc, CDCRef::Release(hwnd))
For ReleaseDC, the HWND is passed as parameter to the deleter. Again, we
associated all information required for cleanup with the object when it is
constructed, which is a design principle of HandleRef.
Further, CDCRef provides static member functions that wrap the Win32 API
functions that acquire a HDC. For example,
CDCRef::GetDC implements the GetDC Win32 API function,
but returns a CDCRef.
The solution provided here is not new. The idea of the article is to show
that the solution becomes very simple when using an appropriate library (about
20 lines of actual code), and how such a solution would be developed from the
initial idea to use shared_ptr.
Now, How good is the solution?
- You need to know how Win32 Resources are managed
- You need to know how smart pointers work
- You have a slight overhead over plain Win32 code: a separately allocated
object (8 Bytes) for each managed handle, and the cleanup call is through a
function pointer.
The second is not an issue: smart pointers are such a fundamental technique
that you shouldn't be caught without. The last might be an issue when handling
lots of resources. However, such an application likely benefits most from
automated resource management, and you can keep performance under control with
using a raw handle (or a CHandleRefT<> const &) where
appropriate.
The first actually reflects a design choice: CHandleRef does not
isolate you from the underlying API, but makes it transparent and easier to use.
(MFC, in contrast, isolates you very well in the default case, and fails
misderably in all others). Additional advantage: CHandleRef is not only library
independent, it also integrates easily with other libraries.
Is a "better" solution possible?
Definitely. You still can do things wrong: release an object while it is
still selected into a device context, specify the wrong cleanup policy, or
delete the handle while it is still in use. However, a completely safe solution
would require to wrap the entire GDI API: all functions creating or using an GDI
object.
CDCRef actually show the difference between an open
wrapper (allowing access to the underlying API handles) and a close
wrapper (denying the same access).
To make CDCRef construction "foolproof" the CDCRef
constructor would have to be protected, so they can be constructed only through
dedicated functions that initialize correctly. But this requires wrappers for
all functions acquiring a DC. Further, to make CDCRef completely
foolproof, one also would have to wrap all functions accepting a DC, and remove
the operator HDC() from the class. The open wrapper leaves you some
responsibility, but you have no problems if I forgot to provide a wrapper (or
the original API is extended).
Conclusion and Thank You
Thank you all for the encouraging feedback! It is great to see the time to
write the article was well spent. 
For a more formal conclusion: We have seen that
- Resource Handles, as many other objects, can have a variety of destruction
policies, which are not "visible" from the handle itself
- The deletion policy should be "attached" to the handle when the handle is
acquired
- reference counted smart pointers are perfect for wrapping resource handles
boost::shared_ptr provides the features to make an
implementation simple
- by using some platform-specific knowledge, we can make the solution more
efficient, and less complex in terms of code, while keeping the original
interface
| You must Sign In to use this message board. |
|
|
 |
|
 |
Nice article.
Several years passed since this article was posted - just some heads-up to the readers who are unfamiliar with WTL - an originally MS extension to ATL.
WTL has many wrapper-like classes to standard window handles, such as HFONT, HDC, HBRUSH, HWND and many many more.
For example: typedef CMenuT CMenuHandle; typedef CMenuT CMenu; typedef CPenT CPenHandle; typedef CPenT CPen;
atlgdi.h: // CPenT // CBrushT // CLogFont // CFontT // CBitmapT // CPaletteT // CRgnT // CDCT
atluser.h: // CMenuItemInfo // CMenuT // CAcceleratorT // CIconT // CCursorT
----
Cheers
/Zeev
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
operandx wrote: Several years passed since this article was posted
Scary, somehow.
I've said that before some posts below - my problem is that the "delete it or not" flag is a template parameter, meaning I can't assign a CMenuT<true> to a CMenuT<false> - so I can't decide about policy only when I create the handle. They work very well as "scope guards" - i.e. making sure some handle is destroyed when the wrapper leaves scope. I aimed at a bit more than that.
Anyway, thanks for your feedback  As mos feedback shows, this is only a partial and/or incomplete solution - this is also my experience using them.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The kernel has a reference count for handles (all handles) that you ask it to create. The problem is that many people don't pass handles correctly. If you are going to do anything that you think should require an increment to the reference count, you should use DuplicateHandle (and a subsequent call to the appropriate release method). Adding another reference count to it will just decrease the performance of your applications.
As an aside, MFC knows about the kernel reference counting and hence its wrappers handle them properly (in most cases).
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
| Sign In·View Thread·PermaLink | 3.40/5 |
|
|
|
 |
|
 |
Important point. Still, I'm not convinced 
Zac Howland wrote: many people don't pass handles correctly
The problem I try to solve is not passing, but returning and storing.
I see the following advantagesof this solution:
(1) Not all "Handle-Like things" have an DuplicateHandle. One example: Registry keys (not technically kernel handles, you can open another handle to the same key, but you lose the access rights of the original HKEY)
(2) The solution is generic for all pointer-sized handles. Less code, less test cases, less things to know. A custom implementation for fonts would be very expensive (HFONT -> LOGFONT -> HFONT, or is there a better one?)
(3) The kernel reference count isn't available (directly or indirectly) in user mode.
E.g. DuplicateHandle has two drawbacks: It returns a new handle value (I tried XP SP2, file handle, DUPLICATE_SAME_ACCESS). So we can safely assume using DuplicateHandle takes longer and eats additional resources per copy. Further, the copy operation could fail, which must be handled.
using CHandleRef in turn has the following drawbacks: - additional tracking object allocated on the heap for each handle (but not for each copy). That makes CHandleRef unsuitable if you rapidly create and destroy thousands of handles. It's the one major drawback I admit freely  - twice the size. No real problem - pass by value is bit more expensive than passing a handle by value. - additional dependency
In the end I think it's 90% abot taste and style. Only with the remaining 10% I'd insist that class/library interfaces using handle wrappers like this are more elegant and usable, and once "reference counted" us understood, I can safely forget about so many things, which is my personal holy grail of software design.
At the sides, MFC in most cases opts to make the wrappers uncopyable. A proper decision design wise, but damn inconvenient.
Some of us walk the memory lane, others plummet into a rabbit hole
Tree in C# || Fold With Us! || sighist
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
peterchen wrote: The problem I try to solve is not passing, but returning and storing.
Returning is a form of passing. And generally speaking, you shouldn't be storing handles. There is a slim chance (depending on the type of handle) that the OS can decide to move it. If you kept the original handle, you will end up with a very random (meaning extremely difficult to debug) crash.
peterchen wrote: (1) Not all "Handle-Like things" have an DuplicateHandle. One example: Registry keys (not technically kernel handles, you can open another handle to the same key, but you lose the access rights of the original HKEY)
Anything that is equivalent to HANDLE can be used in DuplicateHandle. In fact, some of the APIs for other types of handles actually make calls to DuplicateHandle. Yes, it will be less efficient than passing a reference to your handle value, but it will also be safer in some cases (actually, almost all cases).
peterchen wrote: (2) The solution is generic for all pointer-sized handles. Less code, less test cases, less things to know. A custom implementation for fonts would be very expensive (HFONT -> LOGFONT -> HFONT, or is there a better one?)
Your intensions aren't the problem. Unfortunately, when it comes to kernel objects, you can't treat them like any other pointer. If you don't follow the proper steps for creation/deletion for a given type of kernel object, you will easily cause resource leaks that will bog down your entire system (until you reboot). Your choices for things like this are to read the documentation very carefully and wrap those steps into your own custom classes, or to use libraries that already do it for you. MFC does the font stuff fairly well (though, it does take some getting use to).
peterchen wrote: (3) The kernel reference count isn't available (directly or indirectly) in user mode.
That is how it should be. You shouldn't care what the reference count is. The only time you really need to look at it is when you are debugging, and you can get to it then (through the debugger).
peterchen wrote: E.g. DuplicateHandle has two drawbacks: It returns a new handle value (I tried XP SP2, file handle, DUPLICATE_SAME_ACCESS). So we can safely assume using DuplicateHandle takes longer and eats additional resources per copy. Further, the copy operation could fail, which must be handled.
DuplicateHandle (and its equivalents) basically create a new pointer to the resources. You aren't going to use this method for things like passing the handle into a function to operate on the handle (say, to use it to draw something to the screen). An example of when you want to use it is when you want to have 2 threads drawing to the screen (each thread would have its own copy of the handle).
If the copy operation fails, it means something in the system went very wrong (e.g. you ran out of resources).
I don't question your motives behind this article (I actually like the direction you are thinking), it is just wrapping something that is already done by the OS (the reference counting) creates duplicate functionality and increases complexity (what happens if your reference count gets out of synch with the OS's?). Another way you could wrap handles is to create a method that automatically dupilcates it for you and returns a new wrapper (you probably wouldn't want to do that in the copy-constructor nor the copy-assignment methods though ... that would prevent you from passing the class by value, which you may want to do sometimes).
If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week
Zac
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Zac Howland wrote: what happens if your reference count gets out of synch with the OS's
The shared_ptr references don't need to be in sync with the kernel. They key is the usgae guide: create, and assign, e.g. I'm using:
CHandleRef OpenLogFile() { ... }
The OpenLogFile method opens the handle and immediately assigns it to a CHandleRef. The raw handle is now "owned" by CHandleRef, meaning I must make sure it is not closed manually, or stored in raw form. As long as I guarantee this, my reference count will be on top of a single reference to the actual file object (I'm not sure how familiar you are with COM, but the same mechanism is used for reference counting across an apartment boundary.)
I agree that this requires some bit of "training", and maybe I find it easy only because I know smart pointers inside out from years of COM. But this pattern (both the idea and the code) is highly reusable, e.g. just two days ago for enumerating firewire devices. Two lines of code for an exception safe HDEVINFO (and another shared_ptr for the dynamic sized structures used in hardware enumeration)
I'm not trying to brush away your comments (even if it may sound like), I often notice that I "learn" something from such a discussion only a few weeks (even months) later.
Zac Howland wrote: you shouldn't be storing handles.
Sorry, I can't follow you here. The "behavior" of handkles is well defined. Of course I might accidentally close a handle behind the stores back etc. but code can't protect us from stupidity anyway.
What I'm trying to do is making write good code simpler. Most Windows bugs are in resource management. By associating the cleanup function with the handle there's one thing less to remember.
I've always been unhappy with the MFC solution, particulary because it requires you to read lot of MFC documentaiton carefully, and it doesn't interoperate with other libraries very well.
Zac Howland wrote: If the copy operation fails, it means something in the system went very wrong (e.g. you ran out of resources).
Agreed, if the DuplicateHandle faisl, you can probably shut down the app anyway. But error handling is an application aspect that cannot be decided by the library (CHandleRefT doesn't include any error handling. but it makes it much much easier)
Anyway, enjoy the weekend 
Some of us walk the memory lane, others plummet into a rabbit hole
Tree in C# || Fold With Us! || sighist
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I don't see the advantages of your approach:
- Implementation Why do you use boost::shared_ptr? Seriously, all you need is a reference counter that indicates if a resource must be destroyed in the destructor.
-Design Resource handling is 'partly managed' which is error-prone: Ownership is controlled by a flag that has to be set by the user. On the other hand, managed resources are converted automatically to unmnaged handles by operator HDL().
But maybe I have overlooked something?
Best wishes, Roland Pibinger
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
There are definitely questionable design choices, the following is the motivaiton for taking them, I don't think they are "better" than some other options.
(1) because boost provides exactly what I need, and reduces the implementation effort. You need: - A class / struct for the shared resource itself - The "smart pointer" class (whatever it is) - A "plugin point" for the custom resource deleter
This takes me to three entities, one of which is visible to the user and needs utmost care in handling (I've written smart pointer like stuff before, it's nothing I wouldn't like to have thorughly tested in production code, and a C++ guru should look for pitfalls)
boost::shared_ptr provides just that: holding the reference count, and the handle, a fully tested reference counting pointer, and a custom deleter that is NOT a template parameter (which is important to avoid template infection of the using application)
(2) It was not a design goal to completely isolate the client from GDI resource handling. I've pondered it a long time, and concluded it is not possible wihtout wrapping huge chunks of the windows API.
Rather, I wanted: - a solution that can be integrated with any other library that can work somehow with raw handles. - SIMPLIFY common resource handling problems - Move the "How to delete" decision to the point where the resource is acquired - low amount of code
Using the library can simplify resource handling, if you understand reference counted entities (not too simple, but very common), and understand Win32 GDI resource handling (which is IMO a necessity, unless you wrap the entire GDI API).
implicit cast to HANDLE type: I was uneasy about the cast to raw handle, it's a tradeoff between comfort and bulletproofness. I opted for comfort, that's all. The alternative would be: - a cast to "unspecified_bool_type" (as boost does) for the "Handle zero" check - a HANDLET get() const member I would prefer the latter if HANDLET could be a complex type. However, we are basically using void * / unspecified_type * here. In this case, I believe, the implicit cast is safe.
Also, since I want to integrate with "anything that takes raw handles", I cannot protect the client from accidentally deleting the raw handle with the wrong function or at the wrong time.
The core implementation is less than 2 dozen lines (not counting curly braces)
I think I stated this before - but the idea of this article was to privde a practical example for my previous one, and see how/if it could solve a long-nagging problem (reference counting for handles).
we are here to help each other get through this thing, whatever it is Vonnegut jr. boost your code || Fold With Us! || sighist | doxygen
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
How does this object play with SelectObject, a function which changes the delete requirements of an object.
For Example
CSmartPenHandle pen( ::CreatePen( PS_SOLID, 1, RGB(0,0,0) ) ); // now the pen should be deleted by SelectObject
// I think that ref counting smart pointers are good, but not for DCs, // there too complex a resource for that. You don't want to leave // them lying around for sure and ref counting might let that happen HDC hdc = ::GetDC( hMyWnd );
::SelectObject( hdc, pen.GetWin32Handle() ); // pen now mustn't be deleted. Especially if hdc is a class or window dc. // if it is neither then typically you'd select the return value back in, // but if you don't then ReleaseDC deletes the pen, and your destructor // will fail.
Note that this is just a thought excersise. The example is great as it stands, but i'm simply trying to give an example of why GDI objects in particular don't really lend themselves to smart pointers.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Stewart Tootill wrote: Note that this is just a thought excersise.
Actually, it is not. It is the weak point of my article 
I was considering this when writing, but I finally left it out for several reasons.
You could use a "DC Selector" helper (see below), but ultimately, I don't think any amount of code can circumvent all possibilities to shoot yourself in the foot. In the end, you will have to follow some rules.
The basic idea would be to (a) make sure the object is deselected (e.g. using SaveDC/ReleaseDC, possibly with a scope guard to be safe), and (b) hold a strong reference to the object until it is deselected
I wouldn't even use HandleRef for temporary objects created in the WM_PAINT itself, they original idea was for GDI resources you "keep around" instead of reinitalizing them again and again. But the following could help a bit (but it's just a rough outline):
template struct CDCSelector { CHandleRef dc; HANDLE oldHandle; CHandleRef handle;
CDCSelector(CHandleRef _dc, CHandleRef h) : dc(_dc), handle(h) { oldHandle = 0; if (handle) oldHandle = (HANDLE) ::SelectObject(dc, h); }
void Restore() { if (handle) { ::SelectObject(dc, oldHandle); handle = CHandleRef(); } }
~CDCSelector() { Restore(); } };
(It would require special CDC support, since DC's come with two destructors - ReleaseDC and DeleteDC, and you would still need to do the nesting correctly. So it's not perfect either. The only "bulletproof" solutions I see would require a complete wrapping of the DC handle, which I would dislike)
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist Fold With Us! || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I think that the two destruction methods for DCs (there are more than two if you start to include DirectDraw/3D/Show and GDI+ but that may be taking things two far) are the best reason for not attempting to wrap DCs in classes.
Were you to attempt to do this however, MFCs approach would probably be a good guide. There are three DC classes (plus some other ones for printing that don't matter in this case). CPaintDC gets its DC by calling BeginPaint and releases it with EndPaint and is constructed with a window handle. CClientDC/CWindowDC use the GetDC/GetDCEx functions and ReleaseDC to release them, and straight CDC (which is the base class, but obviously the derived classes override the destruction) which uses DeleteDC to delete the DC which covers most (but not all - see previous comment about DirectX and GDI+) other cases.
Its really a shame that one of the trickiest things to wrap in C++ is also one of the hardest to avoid. I remember starting SDK programming for real on windows having previously used other systems (most notably BeOS) and being thoroughly confused by these DC things. But you can't write even the most trivial GDI application without encountering them.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Please allow me to reply in a single post 
I don't think virtual is to high of a cost - after all, the bost::shared_ptr solution comes in even higher (and would have made it a no-go for the machines MFC 1.x was intended for)
However, the "battlefield", as I see it, is not virtual vs. templates, but Copy Construction and Assignment.
Since the MFC approach does not provide these, I am forced to pass around CDC * or CDC &, so the wrapper trades in management of one resource (GDI) for another (C++ object lifetime) which is a beast of it's own, and I lose many advantages of scoping.
Reference counting does not plug all holes (SelectObject, as you pointed out, is the gaping barn door, and the template suggested is more a fix than a solution), but it plugs many more holes than it opens, and reduces the "mental overhead" of using GDI resources.
Both the virtual destructor, and the "custom deleter" achieve one thing: associating the destruction policy with the resource at time of acquisition. However, the virtual-only solution prohibits common functionality (passing as argument or return value) and moves the complexity only up one level in the hierarchy.
I would like to emphasize that I don't see the solution presented here as the only one. It is neither perfect, nor the most simple one. But I consider it well balanced, and surprisingly simple once you have "accepted smart pointers in your life".
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist Fold With Us! || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I just updated the article, including a CDCRef (in a separate, but single class). It was more a exercise to see if DC's could be fit into the HandleRef model, but I'm happy with the results.
let me know what you think
we are here to help each other get through this thing, whatever it is Vonnegut jr. boost your code || Fold With Us! || sighist | doxygen
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I haven't had time to try this, bit I thought you might be able to replace the bool delete on release flag with a template argument and use specialisation to resolve it. Sure it would make the code bigger (but who cares, memory is cheap and hopefully all this stuff gets inlined anyway) but typically you would never pass a non-const value into that field. The type of delete should almost always be known at compile time. And writing a function that can take a dynamic value once you've done it with specialisation is easy. That way the extra cost (small until you start allocation alot of pens with it, or trying to walk thousands of registry keys) is only there for those that need it.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
you mean CHandleRef<HANDLE, bool> ? I don't think that's a good idea, since all functions taking such a HandleRef would have to be templates, too. (Or am I missing something? your Stewart Tootill wrote: And writing a function that can take a dynamic value once you've done it with specialisation is easy sounds like I do...)
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist Fold With Us! || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Maybe a common base a policy specialisation would solve that
class CGdiHandle { public: virtual ~CGdiHandle() = 0; protected: HGDIOBJECT GetHandle() const { return m_hGdiObject; } private: HGDIOBJECT m_hGdiObject; };
struct FreeObject { static void FreeObject( HGDIOBJECT handle ) { ::DeleteObject( handle ); } };
struct DontFreeObject { static void FreeObject( HGDIOBJECT ) { } };
template < class deletePolicy > class CGdiHandleImpl : public CGdiHandle { public: virtual ~CGdiHandleImpl() { deletePolicy::FreeObject( GetHandle() ); } };
template < bool bDeleteObject > class CGdiSmartPtr : public CGdiHandleImpl< FreeObject > { };
template <> class CGdiSmartPtr : public CGdiHandleImpl< DontFreeObject > { };
Then use CGdiSmartPtr when creating them but functions that only need references, so don't need the concrete type, can accept a reference to the first abstract class, and don't need to be templated. Its probably a lot of effore though
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
CAutoPtr newFont(new CFont); ...
// in copy constructor or assignment operator etc left.newFont = right.newFont; // Exchange ownership, whoever owns it ultimately, the GDI object will be deleted in destructor
You can certainly extend MFC to suit your case, but never the less MFC has often provide a good ground to start with.
You may also use specialize template instead of functor, to have compiler automatically choose which API to call, when closing a handle.
Good day.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I guess this is partly a question of taste (antd tools you know inside out). I am still convinced MFC has to many drawbacks, and even auto_ptr cannot save that:
- auto_ptr does not share, but transfer ownership. I still have to figure out who gets it last.
- auto_ptr cannot handle temporary objects, or pointers-to-member
The interface provided by MFC is not simpler than the raw API interface, it requires me to remember more things (That's it's main failure). It works great in an OnPaint function, but beyond it's just silly. Dragging around the deletion policy in a separate variableis silly. Detaching and reattaching the HFONT in a CFont-Member-Setter is silly. (And all functions of MFC expecting a CGdiObject * instead of a HGDIOBJECT is silly, too. But that's another topic completely).
If this sounds like a rant against your favorite method, I am sorry. I am currently in the mood 
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist Fold With Us! || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
1. When sharing HANDLE, only 1 auto pointer should hold the resource and expose (share?) via method or attribute, this auto pointer will release the HANDLE gradually. Is it necessary to known exactly which object holds it? Maybe.
2. You can easily extend auto_ptr (instead of create new class) or simply use auto_ptr_ref to support temporary etc. And in ATL 7 above, there is a class call CAutoPtr.
3. Usually in MFC we try to "remember" (instead of understand?) which class, which method and when. While API you do the same but a "series of APIs". MFC does this for as it wrap "series of APIs" into one call. Example is when main frame resize, how size is calculated amongs various sub-controls.
4. You may stick to MFC default Attach/Detach, or you can extend it with one call, even automatic call. Remember MFC provide a common ground, not a silver bullet. There is no one framework suit all.
Of course, nothing stop you from going deep down to API, or simply WTL.
Good day.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
1) at least you have to know that the one owning it lives longest.
2) didn't know about auto_ptr_ref, thanks for pointing out! However (you knew that was coming ), it only tries to fix what is already broken: auto_ptr, compared to a refcounted smart pointer, trades of some performance win for quite some additional complexity. I think itwas a bad move to include auto_ptr as the only smart pointer in the STL. If at all, it saved "them" a few tricky design decisions (thread safety? where to store the ref count?)
3) TW wrote: Usually in MFC we try to "remember" (instead of understand?) which class, which method and when. This perfectly describes my main "issue" with MFC, and nowhere this is standing out clearer than with the MFC resource handles (Embedded COM object handling, maybe)
Classes should wrap concepts, not series of commands. Classes should not be silver bullets killing one kind of problem perfectly if shot at the right angle. Rather, classes must be 1) unbreakable in almost every situation and 2) simpler to use than the things they wrap. A kind of Kalashnikow: sturdy, reliable, and usable by everybody who read "programming for dummies".
4) That's basically what I am doing when writing MFC code: use API calls, and using tight attach/detach. Horrible for an MFCian, but it allows me to live with one API intrically known, not two.
The motivation for this article being: MFC did what was appropriate at it's time, for it's purpose. But we can do better, and with the tools available, in much less lines of code than MFC does.
Enjoy 
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist Fold With Us! || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Talking about resource intensive application, VISIO is certainly one of its kind. Guess what, MFC is used heavily in this VISIO product, you can use Spy++ to check it out.
I won't say MFC is perfect, but I think till today, it is still the best C++ framework available on Windows. It's really up to you how much you want to extend it. We can't keep complaining about MFC "as-is" but does not extend it, right? You can also check out Stingray Objective Studio to see how far MFC can go, just don't be surprise.
Unfortunately MS has no plan to further improve MFC, like Delphi VCL.
Nice talking to you, bye.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
You should try the QT framework from Throlltech, they gave me a productivity boost over MFC, ATL etc. etc. and they are portable to Mac and linux!!!!! I have found MFC's one of the worst frameworks around.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
We are using Qt here as well. Comparing with Qt for Windows, MFC is not designed to be OS indepedent, also MFC is much easier to understand and fast.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
MFC did what was appropriate at it's time, for it's purpose.
I have to disagree with this. MFC was obviously flawed at birth. They took the Model-View-Controller scheme from Smalltalk and broke it by combining the model and controller. Consequently, unless you're very disciplined, the View and Document classes end up as a monolithic disaster, where very little of the code is reusable because the model and UI are all mixed together. This is my main gripe with MFC - it encourages appallingly bad program design.
> I think it was a bad move to include auto_ptr as the only smart pointer in the STL.
I think *everyone* on the Standards committee agrees with that. I heard that the smart pointers were stuffed up because they just ran out time. (Likewise the omission of copy_if). Note that shared_ptr was the first thing admitted into C++0x.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Don Clugston wrote: I think *everyone* on the Standards committee agrees with that. heh! Me agreeing with the Standards commitee on STL 
MFC: The first thing I did was drop their Doc/View-Model, (or simply work around it) and do the stuff "my way". I was never sure if this was "just me" or a general flaw. For the rest (i.e. trying to abstract the Windows API) it isn't to bad considering the time it was made, hough a few choices could have been better (and GDI wrapper handles are broken IMO)
we are here to help each other get through this thing, whatever it is Vonnegut jr.
sighist Fold With Us! || Agile Programming | doxygen
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|