AFXEXT: Soap Opera Part 2
Matthias Bohlen -- MATTES@logotec.com
Thursday, May 09, 1996
> Date: Fri, 26 Apr 96 00:06:23 UT
> From: "Mike Blaszczak"
> To: mfc-l@netcom.com
> Subject: RE: AFXEXT: complete Soap Opera
> Reply-to: mfc-l@netcom.com
---
Environment: Windows NT or 95, Visual C++ 4.0
---
Hello Mike and the other fellow MFC programmers,
thank you for the 2 replies to my "AFXEXT soap opera" message. They
show me that it is REALLY difficult to state a problem precisely.
I'll try to make the remaining points clear because it is very
important to solve the problem.
- My EXE is MFC _AFXDLL compiled.
- My DLL's are all real MFC extension DLL's (_AFXDLL, _AFXEXT
defined).
- Each extension DLL exports classes with member functions. The
fully mangled C++ names of these member functions are written into
the EXPORT sections of the *.DEF file of the extension DLL. This
keeps the sources free from _declspec's.
- Calls to these member functions come from the main EXE or from
other extension DLL's.
- In one of my extension DLL's (let's call it PROBLEM.DLL), I try to
find a resource from within itself (that means its own *.RES file).
It is an owner-drawn bitmap button (CBitmapButton-derivative).
- The bitmap that was loaded was not the bitmap from the extension
DLL but from the main EXE program. Both bitmaps had the same
resource ID (automatically generated by the resource editor). So, a
small piece of my main program's splash screen appeared on the bitmap
button which was not the effect I wanted.
This was the original problem. Now, how did I try to solve it?
- The docs made by MS made me think that I had to manage the module
state myself. Mike has now made clear that this is _not_ true if
one MFC EXE or extension DLL calls another MFC extension DLL:
> The article you quoted should have explained that the entry points
> in question come from outside of your modules. If your
> _AFXDLL-build MFC application is running and calls into an MFC
> extension DLL, MFC will maintain the module state for you. If some
> other module that is _not_ an MFC program or not an _AFXDLL-built
> MFC program calls into your DLL, you'll need to worry about managing
> the module state yourself.
Thank you, Mike, for this statement. The article "Managing the State
Data of MFC Modules" did not explain that.
- Now let's see what happened when I tried to manage the module
state myself. When trying to call
AFX_MANAGE_STATE( AfxGetStaticModuleState() ) from inside
PROBLEM.DLL, I got really heavy linking problems: :-(
> mfcs40d.lib(dllmodul.obj) : error LNK2005: __pRawDllMain already
> defined in DLLINIT.OBJ mfcs40d.lib(dllmodul.obj) : error LNK2005:
> _DllMain@12 already defined in DLLINIT.OBJ mfcs40d.lib(dllmodul.obj) :
> warning LNK4006: __pRawDllMain already defined in DLLINIT.OBJ; second
> definition ignored mfcs40d.lib(dllmodul.obj) : warning LNK4006:
> _DllMain@12 already defined in DLLINIT.OBJ; second definition ignored
- These linking problems (as I have explained in the "soap opera"
message) come from two pRawDLLMain's (from MFC's DLLMODUL.CPP and from
AFXDLLX.H) and two DLLMain's (one from DLLMODUL.CPP, the other from my
own DLL initialization module).
- Now, Mike, from your previous message:
> > b) MS does not want me to call AFX_MANAGE_STATE inside an MFC
> > extension DLL but forgot to say that.
>
> There are situations when you need to do this; see a couple of
> paragraphs ago.
Are there really situations where I need to call AFX_MANAGE_STATE
inside an extension DLL? If the answer is YES, you're lost because
of the linker problem!!!!! At the moment, for me, the answer is NO:
I can do without AFX_MANAGE_STATE, I now use my C++ helper class
that calls AfxSetResourceHandle(). But what will happen when I ever
will have to use AFX_MANAGE_STATE?
For the rest of your mail message:
> "Forgot to say that" is downright abrasive: when you buy a car,
> do you think that the car company "forgot to say" that you
> shouldn't drive into trees, shouldn't drive into children on
> tricycles, and shouldn't drive in reverse down the highway? Even
> if the manual said all of that, do you think that the manufacturer
> also "forgot to say that" you shouldn't drive into the lake?
Well, Mike, one moment, please: the picture you draw here really
beats me at first but I think, it cannot stand a second attack.
Microsoft's docs about AFX extension DLL's are like a car
manufacturer's docs about programming the GPS (satellite navigation
system, normally only found in high end cars like the new BMW's).
One of the first things MS says about extension DLL's is: "When you
cross a module boundary, you have to care about module state
switching". This statement is as true or false as saying: "When you
cross a frontier you have to tell that to the car's GPS computer".
What the car manufacturer "forgot to say" was: "Do not care about
frontiers on this planet (MFC) - only if you move your car to
another planet (non-MFC), you have to reprogram the GPS computer!"
MS should move the discussion about module state switching to a very
special place in the docs where only people with mixed worlds come
across.
-----
Stay with me during the commercials so that you do not miss the next
issue of "The AFXEXT linking problem" soap opera! :-)
Best regards...
Matthias.
-----
P.S.:
I know that writing docs is non-trivial and generally, I appreciate
MS's docs very much. BTW, I have observed a difference between German
docs and American docs. The German docs tend to be highly
structured (1., 1.1, 1.1.1, 1.1.2, 1.1.3, etc...), whereas
American docs are like the instructions on an American lawn mower:
1. Put your foot here!
2. Pull the handle there!
3. The motor starts!
4. Off you go!
The German version would be:
1. The lawn mower
1.1 Parts of a lawn mower
1.1.1 The platform for the foot
1.1.2 The handle
1.1.3 About the significance of the combination of a foot and a
handle and what it means for starting the motor of the lawn
mower
Generally (in spite of being a German) I prefer American docs. They
are much easier to read and understand.
Whooo, I have a new idea now: I should re-state my problem the
American way: :-))
1. Write an _AFXEXT DLL
2. Care about module state switching
3. You end up in a linker problem
OK, OK, that would be too much - let's terminate this
mail message here :-)
Bye.
-------------------------------------------------------
Matthias Bohlen | Logotec Software GmbH
Phone: +49 228 64 80 520 | Chateauneufstr. 10
FAX: +49 228 64 80 525 | D-53347 Alfter, Germany
|
E-mail: mattes@logotec.com | CAD systems development
-------------------------------------------------------
Roger Austin -- R.Austin@ioh.ac.uk
Friday, May 10, 1996
Like all the best soap operas, this one has run and run
to the point where everyone has got so tangled up in the
intricacies of the plot that they have forgotten what started
the whole thing in the first place. Reading Matthias' most
recent posting, my restatement of his original problem would
be:
How do I prevent resource ID clashes when using MFC extension
DLLs ?
If you're building all the DLLs in question, the answer is
simple (not elegant, but simple) - give all your resources
unique IDs. You can do this by editing all your resource.h files,
changing (typically) the most significant digit of each ID to
ensure that the values are unique (you need to be a little careful
about permitted ID ranges as listed in the docs). Most importantly,
change the _APS_NEXT_**** variables which the resource editor
uses to assign new IDs so that they remain unique (at least for
the next 1000 or so resources). Its a pain, but it works, and once
you've done it you shouldn't need to worry about it again (until
you add a new DLL). There is also no runtime overhead.
I had to address this problem using VC 2.2, but I have no reason
to believe my solution wouldn't work for 4.0.
Having got that out of the way, how does the MFC framework load
resources? My understanding of this is based on the MFC 3 docs and
sources, so is probably rather out of date, but it goes a bit like
this:
First, it looks in the default module. Initially this will be the
EXE, but you can change it to something else using the appropriate
Afx... call. Don't forget to change it back, otherwise you'll
never load a resource from the EXE again. This is what Matthias'
solution does *every* time he calls a DLL function.
Next, it works through a linked list of CDynLinkLibrary objects,
each created by one of your DLLs when it was initialised in the
context of your application. From these, it gets the DLL module
handles and searches the DLLs in turn until it finds a resource
with the required ID (or not).
It would be nice if the framework could magically know which module
you wanted a resource loaded from, but it isn't always obvious.
For instance, my DLLs contain doc/view classes and associated
resources. At initialisation time, they create an appropriate
CDocTemplate object and register it with the app. The app is then
free to create new documents of that type, with nearly all of the
work (and resource loading) done by MFC code that knows nothing
about the DLL that exports the classes.
Of course, if there was a magic way, I would love to hear about
it :-)
--
Roger Austin
Mike Blaszczak -- mikeblas@msn.com
Sunday, May 12, 1996
----------
From: owner-mfc-l@netcom.com on behalf of Roger Austin
Sent: Friday, May 10, 1996 08:36
> How do I prevent resource ID clashes when using MFC extension
> DLLs ?
Be careful.
> changing (typically) the most significant digit of each ID to
> ensure that the values are unique (you need to be a little careful
That'll work.
>This is what Matthias'
> solution does *every* time he calls a DLL function.
And that's why Matthais' solution isn't very good. He's protected
every single function with activity that, with a little care, he could avoid
completely.
Next, it works through a linked list of CDynLinkLibrary objects,
each created by one of your DLLs when it was initialised in the
context of your application. From these, it gets the DLL module
handles and searches the DLLs in turn until it finds a resource
with the required ID (or not).
> It would be nice if the framework could magically know which module
> you wanted a resource loaded from, but it isn't always obvious.
It's isn't always _available_, either.
.B ekiM
TCHAR sz[] = _T("These words are my own: I don't speak for Microsoft.");
CraigTT_at_USNELMIS@ccmail01.PE-Nelson.COM
Tuesday, May 14, 1996
If all the DLLs are under your control, then it is a little work to a
moderate amount of work to assign and track unique resource IDs
depending on the number of DLLs. I typically work on projects with 30
to 50 DLLs so the bookeeping needs a good deal of formality.
However, the rub comes when you try to use third party extension DLLs.
You must then be sure to avoid IDs used by this purchased DLL. Then
if you buy more than one DLL, you not only have to avoid all their
IDs, but those DLLs can have resource clashes amongst themselves as
well.
Tim Craig
PE-Nelson
______________________________ Reply Separator _________________________________
Subject: RE: AFXEXT: Soap Opera Part 2
Author: mfc-l@netcom.com at SMTPLINK-PEN
Date: 5/12/96 8:21 PM
----------
From: owner-mfc-l@netcom.com on behalf of Roger Austin
Sent: Friday, May 10, 1996 08:36
> How do I prevent resource ID clashes when using MFC extension
> DLLs ?
Be careful.
> changing (typically) the most significant digit of each ID to
> ensure that the values are unique (you need to be a little careful
That'll work.
>This is what Matthias'
> solution does *every* time he calls a DLL function.
And that's why Matthais' solution isn't very good. He's protected
every single function with activity that, with a little care, he could avoid
completely.
Next, it works through a linked list of CDynLinkLibrary objects,
each created by one of your DLLs when it was initialised in the
context of your application. From these, it gets the DLL module
handles and searches the DLLs in turn until it finds a resource
with the required ID (or not).
> It would be nice if the framework could magically know which module
> you wanted a resource loaded from, but it isn't always obvious.
It's isn't always _available_, either.
.B ekiM
TCHAR sz[] = _T("These words are my own: I don't speak for Microsoft.");
| Вернуться в корень Архива
|