15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


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.");





| Вернуться в корень Архива |