AFXEXT: complete Soap Opera
Matthias Bohlen -- MATTES@logotec.com Tuesday, April 23, 1996 Hello, two weeks ago, I started a thread here about linking problems with AFX extension DLL's. I have received responses from many people but the problem still remains. To put things absolutely clear, I re-state it with complete info (sorry for the extreme length of this mail message, but the MS guys seem to have a problem there). -------------- List of facts starts here ------------------------- A) I use Visual C++ 4.0. I write an EXE program with several MFC extension DLL's (_AFXEXT, _AFXDLL, _WINDLL defined). B) My problem is: I want to access resources inside the *.RES file that is linked to an extension DLL. C) Microsoft offers two solutions: C.1) call AfgxSetResourceHandle( hExtensionDLL ) (that works!) C.2) call AFX_MANAGE_STATE( AfxGetStaticModuleState() ) (that does NOT work!) D) In "DLLs: Building and Using an Extension DLL", Microsoft states that I have to think about the following to have a correct MFC extension DLL: " * It does not have a CWinApp-derived object. * It calls AfxInitExtensionModule in its DllMain function. The return value of this function should be checked. If a zero value is returned from AfxInitExtensionModule, return 0 from your DllMain function. * It will create a CDynLinkLibrary object during initialization if the extension DLL wishes to export CRuntimeClass objects or resources to the application. * For an example of a DLL that fulfills the basic requirements of an extension DLL, see the MFC Advanced Concepts sample DLLHUSK. In particular, look at the TESTDLL1.CPP and TESTDLL2.CPP files." Found out: to do this, you have to #include "afxdllx.h". E) In "Managing the State Data of MFC Modules", Microsoft states that: A module's state data is contained in a structure, and is always via a pointer to that structure. When the flow of execution enters a particular module (as shown in Figure 2), that module's state must be the "current" or "effective" state. Therefore, each thread object has a pointer to the effective state structure of that application. Keeping this pointer updated at all times is vital to managing the application's global state and maintaining the integrity of each module's state. Incorrect management of the global state can lead to unpredictable application behaviour." F) Statement E) told me that AFX_MANAGE_STATE would be absolutely necessary. Therefor, I chose to take Step C.2. G) Step C.2 caused linker errors about multiply defined symbols: 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 H) To track the problem down, I browsed a little in \MSDEV\MFC\SRC and found the only place where AfxGetStaticModuleState() is defined. This file is called DLLMODUL.CPP. The file also contains these interesting lines of code: ////////////////////////////////////////////////////////////////////// ////// // export DllMain for the DLL extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { ... } extern "C" BOOL WINAPI RawDllMain(HINSTANCE, DWORD dwReason, LPVOID); extern "C" BOOL (WINAPI* _pRawDllMain)(HINSTANCE, DWORD, LPVOID) = &RawDllMain; extern "C" BOOL WINAPI RawDllMain(HINSTANCE, DWORD dwReason, LPVOID) { ... } K) From Step "D", we know that I had to #include "afxdllx.h". This file contains the interesting code lines: extern "C" BOOL WINAPI ExtRawDllMain(HINSTANCE, DWORD dwReason, LPVOID); extern "C" BOOL (WINAPI* _pRawDllMain)(HINSTANCE, DWORD, LPVOID) = &ExtRawDllMain; L) From "H" and "K" we can see that we need not wonder about the name clashes: We have two pRawDLLMain's (from DLLMODUL.CPP and from AFXDLLX.H) and two DLLMain's (one from DLLMODUL.CPP, the other from my own DLL initialization module). The linker messages from Step "G" are logical. Check mate! ----------- End of fact list ------------------------------------ OK, what do we learn from this? I think, one of the following must be true: a) MS did not pay attention when writing the code (I cannot imagine that!). b) MS does not want me to call AFX_MANAGE_STATE inside an MFC extension DLL but forgot to say that. Possibility "b" is more likely because in "TN058: MFC Module State Implementation", Microsoft states: "Note that certain kinds of DLLs, specifically "MFC Extension" DLLs don't switch the module state in their RawDllMain (actually, they usually don't even have a RawDllMain). This is because they are intended to behave "as if" they were actually present in the application that uses them. They are very much a part of the application that is running and it is their intention to modify that application's global state." ---------- End of the story: I kept my DLLMain, threw out AFX_MANAGE_STATE and replaced it by the following C++ class: extern AFX_EXTENSION_MODULE LOGOCADDLL; class dll_instance_switcher { public: dll_instance_switcher() { m_hinst = AfxGetResourceHandle(); AfxSetResourceHandle (LOGOCADDLL.hResource); } ~dll_instance_switcher() { AfxSetResourceHandle (m_hinst); } private: HINSTANCE m_hinst; }; I put an object of this class into every exported function and I can find the resources OK. Does somebody want to comment? Bye... Matthias Bohlen ------------------------------------------------------- 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 -------------------------------------------------------
Mike Blaszczak -- mikeblas@msn.com Friday, April 26, 1996 ---------- From: owner-mfc-l@netcom.com on behalf of Matthias Bohlen Sent: Tuesday, April 23, 1996 09:39 > To put things absolutely clear, I re-state > it with complete info (sorry for the extreme length of this mail > message, but the MS guys seem to have a problem there). What problem do MS guys seem to have with long mail? I'm guessing that you're using VC++ 4.0 and MFC 4.0 but you don't explicitly say so. Are you saying that the way I ask for the specific versions of your tools all of the time is a problem? > B) My problem is: I want to access resources inside the *.RES file that > is linked to an extension DLL. You don't say what module wants to retrieve resources from what module or modules. If you want your EXE to access resources in your MFC extension DLLs, you should have no problem doing so if the DLLs are loaded. Just call the appropriate load routine, like CBitmap::LoadBitmap(). If you want your DLL to load resources from its own self, you should have no problem doing so. If you want your DLL to load reousrces from other DLLs, you'll need to call AfxSetResourceHandle(). If you're explicitly loading these DLLs, or if you have some resources in one DLL with the same ID as resources in another DLL, you have other problems and these rules might not apply to you. > F) Statement E) told me that AFX_MANAGE_STATE would be absolutely > necessary. Therefor, I chose to take Step C.2. 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. > H) To track the problem down, I browsed a little in \MSDEV\MFC\SRC You should browse a little more in MSDEV\MFC\SRC. It sounds like the original problem you had was that some DLL or EXE in your scenario was trying to load a resource or two and wasn't able to do so. If that happened to me, I would trace into the MFC function which did the loading. There' you'd learn about the function AfxFindResource() and probably be able to make some conclusions about what's really going on. > 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. "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? You can invent tens of dozens of different DLL architectures. It is impossible that Microsoft would have all the forsight to predict every one of them, the forsight to guess what problems you might run into, and the time and resources to document every combination of those situations. Don't you think? > I put an object of this class into every exported function and I can > find the resources OK. Exported function? So, your DLL offers a C-level interface? Your first statement: > A) I use Visual C++ 4.0. I write an EXE program with several > MFC extension DLL's (_AFXEXT, _AFXDLL, _WINDLL defined). seems ambiguous. Is your EXE an MFC program, or not? Is your EXE built with _AFXDLL or not? That you need this helper class suggests that you've not written an MFC _AFXDLL executable or that you're loading resources that belong to one DLL from another DLL, or that the resources aren't really in a proper extension DLL. Otherwise, you shouldn't need to play with AfxSetResourceHandle(). > Does somebody want to comment? See above. .B ekiM TCHAR sz[] = _T("I don't speak for Microsoft.");
Dan Kirby -- dkirby@accessone.com Monday, April 29, 1996 You're solution is a good one. Your discoveries are "by design". The module state for an extension DLL is the same as the .EXE since there is no CWinApp object in an Extension DLL. Your resources will be found if you have properly hooked up the CDynlinklibrary object and you don't have any conflicting resource IDs in other modules (.EXE and .DLLs). So normally, there isn't a need to call AfxSetResourceHandle(). But if you want to call AfxSetResourceHandle() to be explicit about which module has the resource that is perfectly fine. --dan ---------- From: Matthias Bohlen[SMTP:mattes@logotec.com] Sent: Tuesday, April 23, 1996 9:39 AM To: mfc-l@netcom.com Subject: AFXEXT: complete Soap Opera Hello, two weeks ago, I started a thread here about linking problems with AFX extension DLL's. I have received responses from many people but the problem still remains. To put things absolutely clear, I re-state it with complete info (sorry for the extreme length of this mail message, but the MS guys seem to have a problem there). -------------- List of facts starts here ------------------------- A) I use Visual C++ 4.0. I write an EXE program with several MFC extension DLL's (_AFXEXT, _AFXDLL, _WINDLL defined). B) My problem is: I want to access resources inside the *.RES file that is linked to an extension DLL. C) Microsoft offers two solutions: C.1) call AfgxSetResourceHandle( hExtensionDLL ) (that works!) C.2) call AFX_MANAGE_STATE( AfxGetStaticModuleState() ) (that does NOT work!) D) In "DLLs: Building and Using an Extension DLL", Microsoft states that I have to think about the following to have a correct MFC extension DLL: " * It does not have a CWinApp-derived object. * It calls AfxInitExtensionModule in its DllMain function. The return value of this function should be checked. If a zero value is returned from AfxInitExtensionModule, return 0 from your DllMain function. * It will create a CDynLinkLibrary object during initialization if the extension DLL wishes to export CRuntimeClass objects or resources to the application. * For an example of a DLL that fulfills the basic requirements of an extension DLL, see the MFC Advanced Concepts sample DLLHUSK. In particular, look at the TESTDLL1.CPP and TESTDLL2.CPP files." Found out: to do this, you have to #include "afxdllx.h". E) In "Managing the State Data of MFC Modules", Microsoft states that: A module's state data is contained in a structure, and is always via a pointer to that structure. When the flow of execution enters a particular module (as shown in Figure 2), that module's state must be the "current" or "effective" state. Therefore, each thread object has a pointer to the effective state structure of that application. Keeping this pointer updated at all times is vital to managing the application's global state and maintaining the integrity of each module's state. Incorrect management of the global state can lead to unpredictable application behaviour." F) Statement E) told me that AFX_MANAGE_STATE would be absolutely necessary. Therefor, I chose to take Step C.2. G) Step C.2 caused linker errors about multiply defined symbols: 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 H) To track the problem down, I browsed a little in \MSDEV\MFC\SRC and found the only place where AfxGetStaticModuleState() is defined. This file is called DLLMODUL.CPP. The file also contains these interesting lines of code: ////////////////////////////////////////////////////////////////////// ////// // export DllMain for the DLL extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { ... } extern "C" BOOL WINAPI RawDllMain(HINSTANCE, DWORD dwReason, LPVOID); extern "C" BOOL (WINAPI* _pRawDllMain)(HINSTANCE, DWORD, LPVOID) = &RawDllMain; extern "C" BOOL WINAPI RawDllMain(HINSTANCE, DWORD dwReason, LPVOID) { ... } K) From Step "D", we know that I had to #include "afxdllx.h". This file contains the interesting code lines: extern "C" BOOL WINAPI ExtRawDllMain(HINSTANCE, DWORD dwReason, LPVOID); extern "C" BOOL (WINAPI* _pRawDllMain)(HINSTANCE, DWORD, LPVOID) = &ExtRawDllMain; L) From "H" and "K" we can see that we need not wonder about the name clashes: We have two pRawDLLMain's (from DLLMODUL.CPP and from AFXDLLX.H) and two DLLMain's (one from DLLMODUL.CPP, the other from my own DLL initialization module). The linker messages from Step "G" are logical. Check mate! ----------- End of fact list ------------------------------------ OK, what do we learn from this? I think, one of the following must be true: a) MS did not pay attention when writing the code (I cannot imagine that!). b) MS does not want me to call AFX_MANAGE_STATE inside an MFC extension DLL but forgot to say that. Possibility "b" is more likely because in "TN058: MFC Module State Implementation", Microsoft states: "Note that certain kinds of DLLs, specifically "MFC Extension" DLLs don't switch the module state in their RawDllMain (actually, they usually don't even have a RawDllMain). This is because they are intended to behave "as if" they were actually present in the application that uses them. They are very much a part of the application that is running and it is their intention to modify that application's global state." ---------- End of the story: I kept my DLLMain, threw out AFX_MANAGE_STATE and replaced it by the following C++ class: extern AFX_EXTENSION_MODULE LOGOCADDLL; class dll_instance_switcher { public: dll_instance_switcher() { m_hinst = AfxGetResourceHandle(); AfxSetResourceHandle (LOGOCADDLL.hResource); } ~dll_instance_switcher() { AfxSetResourceHandle (m_hinst); } private: HINSTANCE m_hinst; }; I put an object of this class into every exported function and I can find the resources OK. Does somebody want to comment? Bye... Matthias Bohlen ------------------------------------------------------- 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 ------------------------------------------------------- begin 600 WINMAIL.DAT M>)\^(A@$`0:0" `$```````!``$``0>0!@`(````Y 0```````#H``$(@ <` M& ```$E032Y-:6-R;W-O9G0@36%I;"Y.;W1E`#$(`0V ! `"`````@`"``$$ MD 8`; (```(````,`````P``, ,````+``\.``````(!_P\!````/P`````` M``"!*Q^DOJ,0&9UN`-T!#U0"`````&UF8RUL0&YE=&-O;2YC;VT`4TU44 !M M9F,M;$!N971C;VTN8V]M```>``(P`0````4```!33510`````!X``S !```` M$0```&UF8RUL0&YE=&-O;2YC;VT``````P`5# $````#`/X/!@```!X``3 ! M````$P```"=M9F,M;$!N971C;VTN8V]M)P```@$+, $````6````4TU44#I- M1D,M3$!.151#3TTN0T]-`````P``.0`````+`$ Z`0````(!]@\!````! `` M``````,-`````P``, 0````+``\.`0````(!_P\!````0P````````"!*Q^D MOJ,0&9UN`-T!#U0"```!`&UA='1E``,P`0```!,` M``!M871T97- ;&]G;W1E8RYC;VT```,`%0P!`````P#^#P8````>``$P`0`` M`!4````G;6%T=&5S0&QO9V]T96,N8V]M)P`````"`0LP`0```!@```!33510 M.DU!5%1%4T!,3T=/5$5#+D-/30`#```Y``````L`0#H``````@'V#P$````$ M````````! (!^0\!````0P````````"!*Q^DOJ,0&9UN`-T!#U0"```!`&UA M='1E `!X,`0````4```!33510`````!X`'PP!````%0```&1K M:7)B>4!A8V-E ``@0`0`` M`&4```!93U5215-/3%5424].25-!1T]/1$].15E/55)$25-#3U9%4DE%4T%2 M12)"641%4TE'3B)42$5-3T153$535$%4149/4D%.15A414Y324].1$Q,25-4 M2$5304U%05-42$5%6$53``````(!"1 !````N@\``+8/``"W( ``3%I&=7<4 M/C/_``H!#P(5`J0#Y 7K`H,`4!,#5 (`8V@*P'-E=.XR!@`&PP*#,@/&!Q," M@S(S$P]F- /%`@!P ,Q0@"PH2\@P!8P! ($I9"& G%Z @ 8QS0(U$
(&+>:@60!4 +@"(" M12):'F%_'H07H!SP"' DT 0@`_!L90,@8B&R=6X>``:0(%IY"& @$< ?," 5 M\&^J< 20;" `:!W@:PF #"!U)B C4D-$>6[*; N :RQ@8G(*P" `KR9%`' > M`"HB9 (@)P5 KRIC`' @``6@;AIP:2: Q0N 9RAG($E$!" FL6YO)0(@]00@ M*"13+9(N:2+A 85,EH"60 2PD]RXR'; >0#4K@70EH&,S01-P M9GAI!F!T4BB%2"V1(4 H[3*20AT@*>9W`' %0#3?OS7K-](I82) "U O4&DM M<=,&X#;Q=V@O4&@@]A' OR,T+\ 0Y8,@@70(9 C4 0'3O2Q%+`E%O M3&YU)E-3CT@+Y$%&)'!85%C02Q$+4%<2`!S0,M!A)B!/*N%A\T ?02,S-D*7 M%"(,`4.6)$AE*3!O+#Y\='=U):!W">!K'9$=T#. 2;\A8@`@*X$=L"-0%Z!A M'@#_)1,ZQ"QB+X(JL0)@%H H\L\C4#[66K$B/"=S'F!AD'
@)-!I'S > M`"AQ\"(!'P!" #4B#P+L(JX"K0_2%!8C;Q8#8@T6/U(6$I(O\7H # "X!F M85-@*J V\4D!^QJ@'9%B'/-?<"[A(4 *P/MA@+2%T"H4ZD62#6S?O"X " M$#&0'/!R+/$ATB-2KR)!:Q$ %G=190_QS@">!H8#?A+G1J)R4#,I!S/GYWFB!,! `%0'(!9OT` MT'1TL6'"!" E$W><=YDM/GQ!.;!AD'41\"!6#00`=0= ); K*R T]"XP9G)W M!1 AH2;B)''_*K$)P".@9'01\!\Q?6$*A=A-1D-E?C&07UJT,X#'@B(BX8*1 M5TE.(N(-L=4^,61VGD(YL$UHL6I%_P0`6-!^(3>E`- HT00@*'@-:U%I#; C M0RHN4D7_!? ^("%!/0-N5@0@+&(TI/LB'G:M0X5!+U #8!SP`8!_$]L!'@ M8),<]H8@C'8N]C$YL#@59SB/-H J4"<76R+B.; H/0-@L')A$"$:*8\',H^F M6L!?34$`3D%'15]35$&L5$618#AA1Q(`4R&!_2]032$3E>(YDI)F+A 'D>,* MA9A33D]4DM.3-PJ%]D1\H0.@(C)26- VX ,0=Q[@+Y$MDE4DH9MR)PPB_S. MC1@A @= AP`Z!V!T *4/]Q]ZL&'. K M0"$P'@`I81&PWP60*W%F<2H`';!Z!) EH/^LM 0`J]LK@6@SJ*\A$S. _:Q% M,&@D*B&:$:J4"H6K!_^G>RD3!0!B@'YR+!-X@"R _WB +,LA( 40&J!N5K(! M!S'\:7J6`1U2>JL`;(D1 MJ'$C4F(CX/LO4"AA<9L0<4(",+[!KV&_(BTS@'31= 2 X&369*RP_R3!'@`( M4"3!!3!TL<&D(N'P2%532V9Q`Z *L981_R$P;6(7,"M@':!S\PJ%E4"#E1 B MX3$N0U!0+8/=R^4RS&.)$F9@(@J%P1#_*;(ZX5C0-^$N$'(C,X J)YTWX2,D ML0I B$$B83AP839@;'@N:"" /GQ%^YI$2-!N84"A,R#1EJ,BT-\AD!VP<@& MTI9$ 0=*'^=@ >6(65?VP=Q]#TAP"H*A4L)X'#_T\0]4MUE*[#9 MD6("+7$I,?^]8C!QW'(!D/&A): #@=.G]^JVP#B!X6<7,,00`R A=/\MDFLR MVD+3QMUR"< ZD"T!_]_QZ6%RUMB[R;*B%O+CQ-/_0
`(I3R\#?K[ 10.#R8R/E@&P=,!M@OR "@^, '!.2QP0,#6!6-!?7W!287>JAONZ(&)R>59E M"[8FL2+A@V# 250N3T)*#1\.+\L/-U9E7ZJ%0#$)4!"U_Q&/$I4,OQ.O5F4W MD*R +X)S#T%"$# V#[\6[Q(Z.[MTP2\!9!$JN?&ZDF /Q]$]R /_7 _K$@YL&NA<3"&\/^@$&GZ+A#C,&%RN##?L FB3]G NC!* M0*GD7%QT4$2<158M48#@+5!34H#P_^JVV\%PD,["QM*M\#WQ7@'_/-$[(#/" ME8^6E]OQ"[7 T'_+=JU3B2+9XC@A8@&[T4WX3T15B^#,<:N#B02Z(/^.8-H& MP[-\\=US: "MT/-!?U9EBE%H`7(!HA +L([V+^\Z'SLO/#\]3R\YJSH!O*8_ MJH=PI<)85F6[,JR!(D,QSB9"3T^[\(-105 S89"JA2A(@V"5$4Y#/0,@:)I@ MY2''\<_P1%?H3U)$SW!WD(#$(*WP@<_P3%!63TE$E_JQ1ZTO*FP<@#=1Y?X+DK0_59F?4&?0PL PI.TN?0P0H0V3 \!QZ9BE. M'T^W(#U'#YA4)O\ *H]#?_]'B,Y2TYHDB-JHWBSE2.+3_I!9,#T-TD9%97UIO1F%4 M[_]KCVPX3]]0[U'_4P]4$6J/?W*_;(]4HE9 : MMMEPE3)E?!2)(I)#,LB)+ MI)#_8!'\$L:4DK+LL 3P\2&E4O^8\-O W:&@-P2&R."NH;VQ^[Q!'$!7[+#0 M5(XQ'(,T\?_:48'BL7,TZB[4=T+'1H+3_%@N*A',LHXB?5FC0*"QOWX>Y"*L M\3 6VX<>_C,;O2N=WB5"DUI GE\>'OR-#Z8*UQ7SA'I)#JMN/1[]^@ MT["4$<#00Z[2]?'4<'HAF5PMB_AG\,[2U0!F_S P\6"Z,.6AB_F.;XOVF5S\ M3TM?\9=D8 +\4:R!L7/]\ (_*^&?T\_P@@+ZU:#8_^6%GF?M4#F'ZK8!0"UP MSW#^:7FD7% $8.5 NU&MTS!Q[YB!]U'3QF5S* 9!P5"E4F_QT-.BJ\;7A2&& MT-(L8O^6]*46(W#ZH0&R-H'Q@@(_OS;QEU#UHJ*#7+B[/&(*\?T%\6>E8<]1 MR- $8 'BTATV4 : Q#!B-A#W8B)B[V[ V_'B4(FB::\`+^'^T*,) )DY23: ML3 *Q?]M!KO*#4Q$6"9"T4[#E9?_LH-K T%/9P&_I8K7;\::F_[3" MB8)<)=UR>B%A(_[3T4+]\@%F;L"XXV 0X]&T)BL!_S=1^J&RU/.OWS+D80F1 MP[/^;36CN1/<@!3 !& +(.EQ_^LARA'ZQ;V?OJ7;\>U0FE#_\S+;L\,CLP2Y MT[YT\L'B8/^NL*-DP<_TOO]NCV_2EHS$=]0"-_#G0'D<0$.PIE!P__H!!&!] M5>0B_6#?P0KQGO_?+N)<)?U@,!/$$V*C8I0*>$,K*WNSJUY7A 30 M,D!MMG.*_3#14][N?>#4R&* WP;?__? !>/O MY/_8A/=0=HK1_]D^1%C>!6VV2W!V':CFV,'_E/#\(111^>+!8S1"U?/V(=\V M@+91P'$_A"9Q9B] ^?#_F&(NXIH38Q@O53?1WS,*H/N0X ?-1)UR:D !L K0 M)?$OG?.> 3_(<%UG?8 LT),0NQ%";_IHW\!NR*__OP#/`=_* M%N_]S7.NB>"L<6-?8*K!(W'5RY!'#%!(I$9H@@$<0 `K-#D@,C(X(( V-" X M," U%5"? .-7.$1"TU MT#,S-#
Ranjiv Sharma -- RanjivS@datastorm.com Wednesday, May 01, 1996 The only time you do not have to use your dll_instance_switcher class is if you do not have any resource ID's that are same in any of the extension DLL's that are being linked to the MFC App. This is really a problem, since the resource editor ends up using the same ID's for dialogs in different DLL's (starts with 101 for dialogs). Since it is a pain to keep track of unique resource ID's - especially if more than one person is working on a project, it is best to always use your dll_instance_switch class. The MFC solution may have been slightly more acceptable, if the resource chain always started with the Resource of the module in which the function was being executed, and then, if the resource was not found, to go through the other modules in the chain. The current implementation only leads to great confusion, when you have dialog boxes from other dll's coming up - or the wrong strings being loaded. In order to keep the same programming model for extension and non extension DLL's (which is what is desirable), you could define a macro something like this #ifdef _AFXEXT #define SWITCH_RESOURCE dll_instance_switcher #else //_USRDLL #define SWITCH_RESOURCE AFX_MANAGE_STATE(AfxGetStaticModuleState()) Now you can do the same thing in every exported function in your dll ExportedFuntion() { SWITCH_RESOURCE; .... } -Ranjiv (ranjivs@datastorm.com) ---------- From: owner-mfc-l[SMTP:owner-mfc-l@netcom.com] Sent: Monday, April 29, 1996 9:41 PM To: 'mfc-l@netcom.com'; 'mattes@logotec.com' Subject: RE: AFXEXT: complete Soap Opera You're solution is a good one. Your discoveries are "by design". The module state for an extension DLL is the same as the .EXE since there is no CWinApp object in an Extension DLL. Your resources will be found if you have properly hooked up the CDynlinklibrary object and you don't have any conflicting resource IDs in other modules (.EXE and .DLLs). So normally, there isn't a need to call AfxSetResourceHandle(). But if you want to call AfxSetResourceHandle() to be explicit about which module has the resource that is perfectly fine. --dan ---------- From: Matthias Bohlen[SMTP:mattes@logotec.com] Sent: Tuesday, April 23, 1996 9:39 AM To: mfc-l@netcom.com Subject: AFXEXT: complete Soap Opera Hello, two weeks ago, I started a thread here about linking problems with AFX extension DLL's. I have received responses from many people but the problem still remains. To put things absolutely clear, I re-state it with complete info (sorry for the extreme length of this mail message, but the MS guys seem to have a problem there). -------------- List of facts starts here ------------------------- A) I use Visual C++ 4.0. I write an EXE program with several MFC extension DLL's (_AFXEXT, _AFXDLL, _WINDLL defined). B) My problem is: I want to access resources inside the *.RES file that is linked to an extension DLL. C) Microsoft offers two solutions: C.1) call AfgxSetResourceHandle( hExtensionDLL ) (that works!) C.2) call AFX_MANAGE_STATE( AfxGetStaticModuleState() ) (that does NOT work!) D) In "DLLs: Building and Using an Extension DLL", Microsoft states that I have to think about the following to have a correct MFC extension DLL: " * It does not have a CWinApp-derived object. * It calls AfxInitExtensionModule in its DllMain function. The return value of this function should be checked. If a zero value is returned from AfxInitExtensionModule, return 0 from your DllMain function. * It will create a CDynLinkLibrary object during initialization if the extension DLL wishes to export CRuntimeClass objects or resources to the application. * For an example of a DLL that fulfills the basic requirements of an extension DLL, see the MFC Advanced Concepts sample DLLHUSK. In particular, look at the TESTDLL1.CPP and TESTDLL2.CPP files." Found out: to do this, you have to #include "afxdllx.h". E) In "Managing the State Data of MFC Modules", Microsoft states that: A module's state data is contained in a structure, and is always via a pointer to that structure. When the flow of execution enters a particular module (as shown in Figure 2), that module's state must be the "current" or "effective" state. Therefore, each thread object has a pointer to the effective state structure of that application. Keeping this pointer updated at all times is vital to managing the application's global state and maintaining the integrity of each module's state. Incorrect management of the global state can lead to unpredictable application behaviour." F) Statement E) told me that AFX_MANAGE_STATE would be absolutely necessary. Therefor, I chose to take Step C.2. G) Step C.2 caused linker errors about multiply defined symbols: 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 H) To track the problem down, I browsed a little in \MSDEV\MFC\SRC and found the only place where AfxGetStaticModuleState() is defined. This file is called DLLMODUL.CPP. The file also contains these interesting lines of code: ////////////////////////////////////////////////////////////////////// ////// // export DllMain for the DLL extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { ... } extern "C" BOOL WINAPI RawDllMain(HINSTANCE, DWORD dwReason, LPVOID); extern "C" BOOL (WINAPI* _pRawDllMain)(HINSTANCE, DWORD, LPVOID) = &RawDllMain; extern "C" BOOL WINAPI RawDllMain(HINSTANCE, DWORD dwReason, LPVOID) { ... } K) From Step "D", we know that I had to #include "afxdllx.h". This file contains the interesting code lines: extern "C" BOOL WINAPI ExtRawDllMain(HINSTANCE, DWORD dwReason, LPVOID); extern "C" BOOL (WINAPI* _pRawDllMain)(HINSTANCE, DWORD, LPVOID) = &ExtRawDllMain; L) From "H" and "K" we can see that we need not wonder about the name clashes: We have two pRawDLLMain's (from DLLMODUL.CPP and from AFXDLLX.H) and two DLLMain's (one from DLLMODUL.CPP, the other from my own DLL initialization module). The linker messages from Step "G" are logical. Check mate! ----------- End of fact list ------------------------------------ OK, what do we learn from this? I think, one of the following must be true: a) MS did not pay attention when writing the code (I cannot imagine that!). b) MS does not want me to call AFX_MANAGE_STATE inside an MFC extension DLL but forgot to say that. Possibility "b" is more likely because in "TN058: MFC Module State Implementation", Microsoft states: "Note that certain kinds of DLLs, specifically "MFC Extension" DLLs don't switch the module state in their RawDllMain (actually, they usually don't even have a RawDllMain). This is because they are intended to behave "as if" they were actually present in the application that uses them. They are very much a part of the application that is running and it is their intention to modify that application's global state." ---------- End of the story: I kept my DLLMain, threw out AFX_MANAGE_STATE and replaced it by the following C++ class: extern AFX_EXTENSION_MODULE LOGOCADDLL; class dll_instance_switcher { public: dll_instance_switcher() { m_hinst = AfxGetResourceHandle(); AfxSetResourceHandle (LOGOCADDLL.hResource); } ~dll_instance_switcher() { AfxSetResourceHandle (m_hinst); } private: HINSTANCE m_hinst; }; I put an object of this class into every exported function and I can find the resources OK. Does somebody want to comment? Bye... Matthias Bohlen ------------------------------------------------------- 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 -------------------------------------------------------
Mike Blaszczak -- mikeblas@msn.com Friday, May 03, 1996 ---------- From: owner-mfc-l@netcom.com on behalf of Ranjiv Sharma Sent: Wednesday, May 01, 1996 09:33 > The only time you do not have to use your dll_instance_switcher class is > if you do not have any resource ID's that are same in any of the > extension DLL's that are being linked to the MFC App. This is not true. It is _not_ necessary to manually manage the module state when the call is coming from another MFC AFXDLL-compiled module. I outlined these rules in my previous note on this subject. By insulating every entry point in your DLL with a class like the one in question, you'll can make a significant adverse effect on performance! .B ekiM TCHAR sz[] = _T("How's your Bud Ice? Doo be doo be do...");
| Вернуться в корень Архива |