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.");
| Вернуться в корень Архива |