Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

AfxRegisterWndClass() within dll

212 views
Skip to first unread message

tfs

unread,
Aug 29, 2008, 10:23:00 AM8/29/08
to
I tried to implement a message-only CWnd class that can be instantiated
ANYWHERE. To use, I put this class's .h and .cpp file into a dll, and the I
use AfxLoadLibrary() and AfxFreeLibrary() to load and unload that dll, and
such dll will be unloaded and then reloaded (possibly repeatedly). (NOTE:
please don't question why I have to keep unloading and then reloading the
same dll; the app is for my client so I cannot tell any more about that).

For the first time the dll is loaded and my CMsgOnlyWnd is created, all is
fine. The problem arises when the dll is unloaded and then reloaded, thereby
re-executing AfxRegisterWndClass(0), it generates a rubbish class string, and
of course fail the window creation.

How to solve this problem, so that it can truly be instantiated anywhere?


class CMsgOnlyWnd : public CWnd
{
// ......
};

BOOL CMsgOnlyWnd::Create()
{
static LPCTSTR sg_lpszWndClass = ::AfxRegisterWndClass(0);

BOOL bCreated = CWnd::CreateEx(0, sg_lpszWndClass, _T(""), 0, 0, 0, 0, 0,
HWND_MESSAGE, NULL);
ASSERT(bCreated);
return bCreated;
}

Joseph M. Newcomer

unread,
Aug 29, 2008, 12:21:03 PM8/29/08
to
See below...

On Fri, 29 Aug 2008 07:23:00 -0700, tfs <t...@discussions.microsoft.com> wrote:

>I tried to implement a message-only CWnd class that can be instantiated
>ANYWHERE. To use, I put this class's .h and .cpp file into a dll, and the I
>use AfxLoadLibrary() and AfxFreeLibrary() to load and unload that dll, and
>such dll will be unloaded and then reloaded (possibly repeatedly). (NOTE:
>please don't question why I have to keep unloading and then reloading the
>same dll; the app is for my client so I cannot tell any more about that).
>
>For the first time the dll is loaded and my CMsgOnlyWnd is created, all is
>fine. The problem arises when the dll is unloaded and then reloaded, thereby
>re-executing AfxRegisterWndClass(0), it generates a rubbish class string, and
>of course fail the window creation.

***
Did you unregister the class in the DLL_PROCESS_DETACH event?

The use of a static variable as you did ranks as one of the single worst programming
techniques possible in Windows.

You would register the class in DLL_PROCESS_ATTACH (or InitInstance of the DLL) and
deregister it in DLL_PROCESS_DETACH (or ExitInstance of the DLL)
****


>
>How to solve this problem, so that it can truly be instantiated anywhere?
>
>
>class CMsgOnlyWnd : public CWnd
>{
> // ......
>};
>
>BOOL CMsgOnlyWnd::Create()
>{
> static LPCTSTR sg_lpszWndClass = ::AfxRegisterWndClass(0);

****
Take as a fundamental programming rule: writing a static declaration like this a mistake.
In the few, incredibly rare (once every five years or so) situations in which it could
possibly make sense, you will recognize them. But just assume that this technique ranks
among the poorest possible programming practices in any environment, including non-Windows
C code. Go from there. (Just because something is syntactically possible doesn't mean it
makes sense from a programming viewpoint)
joe
****


>
> BOOL bCreated = CWnd::CreateEx(0, sg_lpszWndClass, _T(""), 0, 0, 0, 0, 0,
>HWND_MESSAGE, NULL);
> ASSERT(bCreated);
> return bCreated;
>}

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

tfs

unread,
Aug 31, 2008, 8:33:00 AM8/31/08
to

"Joseph M. Newcomer" wrote:

> >
> >BOOL CMsgOnlyWnd::Create()
> >{
> > static LPCTSTR sg_lpszWndClass = ::AfxRegisterWndClass(0);


> ****
> Take as a fundamental programming rule: writing a static declaration like this a mistake.
> In the few, incredibly rare (once every five years or so) situations in which it could
> possibly make sense, you will recognize them. But just assume that this technique ranks
> among the poorest possible programming practices in any environment, including non-Windows
> C code. Go from there. (Just because something is syntactically possible doesn't mean it
> makes sense from a programming viewpoint)
> joe
> ****

Honestly, I hardly agree with you about your viewpoint (of using static
declaration in the way my example illustrated). As I am not an expert on
MFC/Win programming, I don't argue with you on that when involving any
Windows entity. But you also slam on the non-Windows C-code? This technique
is listed in Marshall Cline's C++ FAQ to resolve 2 global instances (defined
in different .c/.cpp files) referencing each other. And I have used this
technique, and conventional static global variables in my many singleton
classes, to resolve numerous cross-dll design issues that otherwise will be
far too complex to deal with, and they are not some
hack-and-hope-it-does-not-break codes, all created-when-needed, clean-exit
and no resource leak.

And besides, a local CONSTANT static declaration is to reduce global
namespace pollution and enhance data encapsulation. It is indeed an old
technique, but it will still make sense now and in the years to come. If you
are trying to talk about multi-thread issue, then I may answer: do I have to
get every single classe and function multi-thread ready?

tfs


Joseph M. Newcomer

unread,
Aug 31, 2008, 10:59:18 AM8/31/08
to
On Sun, 31 Aug 2008 05:33:00 -0700, tfs <t...@discussions.microsoft.com> wrote:

>
>
>"Joseph M. Newcomer" wrote:
>
>> >
>> >BOOL CMsgOnlyWnd::Create()
>> >{
>> > static LPCTSTR sg_lpszWndClass = ::AfxRegisterWndClass(0);
>
>
>> ****
>> Take as a fundamental programming rule: writing a static declaration like this a mistake.
>> In the few, incredibly rare (once every five years or so) situations in which it could
>> possibly make sense, you will recognize them. But just assume that this technique ranks
>> among the poorest possible programming practices in any environment, including non-Windows
>> C code. Go from there. (Just because something is syntactically possible doesn't mean it
>> makes sense from a programming viewpoint)
>> joe
>> ****
>
>Honestly, I hardly agree with you about your viewpoint (of using static
>declaration in the way my example illustrated). As I am not an expert on
>MFC/Win programming, I don't argue with you on that when involving any
>Windows entity. But you also slam on the non-Windows C-code? This technique
>is listed in Marshall Cline's C++ FAQ to resolve 2 global instances (defined
>in different .c/.cpp files) referencing each other. And I have used this
>technique, and conventional static global variables in my many singleton
>classes, to resolve numerous cross-dll design issues that otherwise will be
>far too complex to deal with, and they are not some
>hack-and-hope-it-does-not-break codes, all created-when-needed, clean-exit
>and no resource leak.

***
Feel free to disagree. But you have absolutely demonstrated why it cannot work correctly
in this context, and there are many, many more contexts in which it will fail.

And yes, it was a bad idea in C code as well. I learned this over many, many years of
finding (and sometimes creating) bugs because of using it, and gradually came to the
conclusion that there is no sane condition in which a non-const local static makes sense.
It interferes with both windowing and threading. The presence of such declarations are
simply land mines waiting for the innocent programmer to step on them.

Note that just because something is syntactically possible does not mean it makes sense.
No matter how many FAQs use it (since you didn't give a URL, I didn't bother to look it
up). But "to resolve 2 global instances" immediately makes me question why there are two
global instances of anything; it suggests that this is a way of solving a problem that
should not have been allowed to exist in the first place.

Singleton classes are a different case; they do not use static local variables.

And for DLL solutions, you have to ask "is this DLL thread-safe" and worry about what
happens if functions in the DLL are called from separate threads; that's where
locally-scoped static variables really break code. They also break code badly when you
have to avoid "resource leaks" that are not "storage leaks", which is the exact case you
have. Had you not immediately thought of using a locally-scoped static variable, you
would not have created a solution that cannot ever possibly be made to work correctly
under any conditions. It gives the brief illusion of working, but it is only an illusion.
As you discovered. It doesn't work. It doesn't work because you are not able to
unregister the class because the onlly reference to the class is hidden and cannot be
found.

Statically-allocated storage must be handled very carefully in windowing context and
multithreading contexts, and in DLL contexts where the DLLs can be loaded dynamically. If
you develop bad programming habits, as soon as you extend these habits into contexts where
they cannot work, you get screwed. So the best solution is to not develop the habits at
all, that way, you don't casually do something that cannot work, but breaks in subtle ways
only under unusual conditions.
****


>
>And besides, a local CONSTANT static declaration is to reduce global
>namespace pollution and enhance data encapsulation. It is indeed an old
>technique, but it will still make sense now and in the years to come. If you
>are trying to talk about multi-thread issue, then I may answer: do I have to
>get every single classe and function multi-thread ready?

****
Remember, I said the only valid use was when the declaration was preceded by 'const'?

It is not a bad idea to program all your classes as if they will be multithreaded; that
way, when you do add threading, you don't have everything implode, and you don't have to
go looking for hard-to-find bugs that are timing-sensitive. They aren't there.

I've watched people make these same mistakes, over and over, for decades. So I try to
avoid creating situations in which these mistakes are fatal. I'm usually the guy called
into "fix our multithreading problems" and this is one of the most common failure modes.
1975 coding techniques do not work well in multithreaded code (oh yes, if you say, "I
don't use threads", be aware that windowing looks an awful lot like multithreading with
cooperative rather than preemptive allocation of the time)
joe
****
>
>tfs

0 new messages