Challenging issue on Regular DLLs creating and passing MFC
Kyle Herbert -- kyleh@ckeng.com Friday, September 27, 1996 Environment: (Win95 || WinNT3.5) && VC++4.0 Here's the scoop: I'd like to create a regular DLL with an exported function that creates and returns MFC objects to the calling application. More information: I'm trying to build a number of DLLs, each containing a distinct set of property page derived objects. Following a user's command, I want the main application to load the appropriate regular DLL (if it exists) and call the exported function to add property page derived objects from the DLL to a property sheet in the main application. I do not want to use an MFC extension DLL (unless I can load it at execution time through some mechanism like LoadLibrary) because the DLL may not exist and therefore can't be bound at link time. What I've tried: >From the DLL - extern "C" __declspec(dllexport) BOOL SetModulePages(CPropertySheet& sheet) { CUnivP1* pUnivP1; // These are property page derived objects CUnivP2* pUnivP2; // compiled into this DLL // Do not call delete on the property page pointers created below. // Let them dangle and allow the app property sheet to handle them. pUnivP1 = new CUnivP1(); // new did not return NULL sheet.AddPage(pUnivP1); pUnivP2 = new CUnivP2(); // new did not return NULL sheet.AddPage(pUnivP2); return TRUE; } >From the main application - BOOL CAllSht::OpenProgram(char theDLL[]) { HINSTANCE hInstance; td_SetModulePages SetModulePages; // typedefinition for regular DLL callback function CString str; if((hInstance = LoadLibrary(theDLL)) == NULL) // Note: library loaded successfully { str.LoadString(IDS_NODLL); AfxMessageBox(str); // Abort open here } if((SetModulePages = (td_SetModulePages)GetProcAddress(hInstance, "SetModulePages")) == NULL) // Note: address for procedure was found successfully { str.LoadString(IDS_NOPROCADDR); AfxMessageBox(str); // Abort program here } // A call is made to the DLL function to add its property pages to // the property sheet this application provides. (*SetModulePages)(m_sheet); // Looks good up to this point m_sheet.Create(this, WS_CHILD | WS_VISIBLE); // DEBUG ASSERTION FAILURE HERE // DLGPROP.CPP // ASSERT(pTemplate != NULL) // For fun I tried a DoModal in place of the Create with the same results. return TRUE; } Thoughts: It seems reasonable that because the DLL is not an MFC extension DLL, the resources included therein (ie. the dialogs around which the property pages are built) are not available for the main application to use. It's also possible that the dynamically created pages are located in DLL memory space and inaccessible to the main application - though, as I understand it, a regular DLL is loaded into the application's memory space. Question: Has anyone been down this road before? If you've followed this train of thought before in your own developement and come up with a means of accomplishing the task I've laid out, please let me know. It seems like such a simple thing! Any advice would be appreciated. --Kyle //===================================================== Kyle Herbert CK Engineering http://www.ckeng.com kyleh@ckeng.com "my mind is aglow, with whirling, transient nodes of thought, careening through a cosmic vapor of invention"
David Little -- dlittle@communique.net Monday, September 30, 1996 [Mini-digest: 3 responses] You must call this: AFX_MANAGE_STATE(AfxGetStaticModuleState( )); to make your DLL use the resources in the DLL. Otherwise, a DLL will attempt to use the resources in the calling app.... ---------- From: Kyle Herbert[SMTP:kyleh@ckeng.com] Sent: Friday, September 27, 1996 9:49 AM To: mfc-l@netcom.com Subject: Challenging issue on Regular DLLs creating and passing MFC objects Environment: (Win95 || WinNT3.5) && VC++4.0 Here's the scoop: I'd like to create a regular DLL with an exported function that creates and returns MFC objects to the calling application. More information: I'm trying to build a number of DLLs, each containing a distinct set of property page derived objects. Following a user's command, I want the main application to load the appropriate regular DLL (if it exists) and call the exported function to add property page derived objects from the DLL to a property sheet in the main application. I do not want to use an MFC extension DLL (unless I can load it at execution time through some mechanism like LoadLibrary) because the DLL may not exist and therefore can't be bound at link time. What I've tried: >From the DLL - extern "C" __declspec(dllexport) BOOL SetModulePages(CPropertySheet& sheet) { CUnivP1* pUnivP1; // These are property page derived objects CUnivP2* pUnivP2; // compiled into this DLL // Do not call delete on the property page pointers created below. // Let them dangle and allow the app property sheet to handle them. pUnivP1 = new CUnivP1(); // new did not return NULL sheet.AddPage(pUnivP1); pUnivP2 = new CUnivP2(); // new did not return NULL sheet.AddPage(pUnivP2); return TRUE; } >From the main application - BOOL CAllSht::OpenProgram(char theDLL[]) { HINSTANCE hInstance; td_SetModulePages SetModulePages; // typedefinition for regular DLL callback function CString str; if((hInstance = LoadLibrary(theDLL)) == NULL) // Note: library loaded successfully { str.LoadString(IDS_NODLL); AfxMessageBox(str); // Abort open here } if((SetModulePages = (td_SetModulePages)GetProcAddress(hInstance, "SetModulePages")) == NULL) // Note: address for procedure was found successfully { str.LoadString(IDS_NOPROCADDR); AfxMessageBox(str); // Abort program here } // A call is made to the DLL function to add its property pages to // the property sheet this application provides. (*SetModulePages)(m_sheet); // Looks good up to this point m_sheet.Create(this, WS_CHILD | WS_VISIBLE); // DEBUG ASSERTION FAILURE HERE // DLGPROP.CPP // ASSERT(pTemplate != NULL) // For fun I tried a DoModal in place of the Create with the same results. return TRUE; } Thoughts: It seems reasonable that because the DLL is not an MFC extension DLL, the resources included therein (ie. the dialogs around which the property pages are built) are not available for the main application to use. It's also possible that the dynamically created pages are located in DLL memory space and inaccessible to the main application - though, as I understand it, a regular DLL is loaded into the application's memory space. Question: Has anyone been down this road before? If you've followed this train of thought before in your own developement and come up with a means of accomplishing the task I've laid out, please let me know. It seems like such a simple thing! Any advice would be appreciated. --Kyle //===================================================== Kyle Herbert CK Engineering http://www.ckeng.com kyleh@ckeng.com "my mind is aglow, with whirling, transient nodes of thought, careening through a cosmic vapor of invention" -----From: ppbillc@srv2.sj.ablecom.net Kyle, Save the HInstance of the DLL that is being loaded. Before you load the propertysheet template you must swap to this dll's instance. Here is a class and macro set I use for this /* * Resource instance handling. */ class UTILITY_CLASS CResInstance { protected: HINSTANCE m_hOldRes; public: CResInstance(HINSTANCE hRes) { m_hOldRes = AfxGetResourceHandle(); AfxSetResourceHandle(hRes); } ~CResInstance() { if (m_hOldRes) AfxSetResourceHandle(m_hOldRes); } }; #define START_RES_BLOCK(hInst) {CResInstance MyResObj(hInst); #define END_RES_BLOCK } So you would HINSTANCE hMyDll = AfxLoadLibrary("MyDll"); START_RES_BLOCK(hMyDll) Do Your PropertySheet Stuff END_RES_BLOCK Hope this helps Bill -----From: Stuart DowningYou create a "regular DLL" when the DLL's clients aren't necessarily MFC applications. These DLLs statically link MFC support. AFXDLL's (a.k.a. MFC extension DLL's) dynamically link with MFCx0.dll and the client application MUST be an MFC application. For more detail on this, see Tech Note 33. It appears from your message that your client app is an MFC app. There's nothing to prevent you from loading an MFC extension DLL via LoadLibrary (you should actually use AfxLoadLibrary and AfxFreeLibrary - see the help on these functions to see why). We are doing this in our application. It sounds to me like you WANT to use an extension DLL. Extension DLL resources are available to the main app via their IDs, no matter which DLL they came from. MFC keeps a list of the extension DLLs that are loaded and searches them to find the requested resource. In fact, you may want to partition the "ID space" amongst the resources in your extension DLLs to make sure they don't collide. There also are ways to load a resource via its ID from a particular DLL. The assertion you are getting is because CPropertyPage::PreProcessPageTemplate can't find the resource for your dialog. Make your DLLs extension DLLs and your problems (well, some of them at least :) will be solved. ------------------------ Stuart Downing Creative Solutions, Inc. stuartd@izzy.net
John -- John.Weeder@abii.com Wednesday, October 02, 1996 Rather than develop a custom solution, I suggest using the solution which is already in place: OLE! For each set of property pages, create a new OLE class using CCmdTarget. CCmdTarget implements IDispatch and IUnknown... so you will need to add an ISpecifyPropertyPages interface. However, ISpecifyPropertyPages only has one member so this is trivial. Each property pages is represent by an instance of IPropertyPage. OLE even contains built-in function to display the property pages - OleCreatePropertyFrame. MFC also has some built in support for property pages, however it is designed to work with OLE controls. I'm not certain if it can be used independently. The following macros are used to declare property pages for OLE controls: DECLARE_PROPPAGEIDS BEGIN_PROPPAGEIDS END_PROPPAGEIDS Also see COlePropertyPage. -- John >---------- >From: David Little >Sent: Monday, September 30, 1996 7:45 AM >To: 'mfc-l@netcom.com' >Subject: RE: Challenging issue on Regular DLLs creating and passing >MFC objects > >[Mini-digest: 3 responses] > >You must call this: > >AFX_MANAGE_STATE(AfxGetStaticModuleState( )); > >to make your DLL use the resources in the DLL. Otherwise, a DLL will >attempt to use the resources in the calling app.... > > >---------- >From: Kyle Herbert[SMTP:kyleh@ckeng.com] >Sent: Friday, September 27, 1996 9:49 AM >To: mfc-l@netcom.com >Subject: Challenging issue on Regular DLLs creating and passing MFC >objects > >Environment: (Win95 || WinNT3.5) && VC++4.0 > >Here's the scoop: I'd like to create a regular DLL with an exported >function that creates and returns MFC objects to the calling >application. > >More information: I'm trying to build a number of DLLs, each >containing a >distinct set of property page derived objects. Following a user's >command, >I want the main application to load the appropriate regular DLL (if it >exists) and call the exported function to add property page derived >objects >from the DLL to a property sheet in the main application. I do not >want to >use an MFC extension DLL (unless I can load it at execution time >through >some mechanism like LoadLibrary) because the DLL may not exist and >therefore >can't be bound at link time. > >What I've tried: > >>From the DLL - > >extern "C" __declspec(dllexport) BOOL SetModulePages(CPropertySheet& >sheet) >{ > CUnivP1* pUnivP1; // These are property page derived objects > CUnivP2* pUnivP2; // compiled into this DLL > > // Do not call delete on the property page pointers created below. > // Let them dangle and allow the app property sheet to handle them. > > pUnivP1 = new CUnivP1(); // new did not return NULL > sheet.AddPage(pUnivP1); > > pUnivP2 = new CUnivP2(); // new did not return NULL > sheet.AddPage(pUnivP2); > > return TRUE; >} > >>From the main application - > >BOOL CAllSht::OpenProgram(char theDLL[]) >{ > HINSTANCE hInstance; > td_SetModulePages SetModulePages; // typedefinition for regular DLL >callback function > CString str; > > if((hInstance = LoadLibrary(theDLL)) == NULL) // Note: library loaded >successfully > { > str.LoadString(IDS_NODLL); > AfxMessageBox(str); > // Abort open here > } > > if((SetModulePages = (td_SetModulePages)GetProcAddress(hInstance, >"SetModulePages")) == NULL) // Note: address for procedure was found >successfully > { > str.LoadString(IDS_NOPROCADDR); > AfxMessageBox(str); > // Abort program here > } > > // A call is made to the DLL function to add its property pages >to > // the property sheet this application provides. > > (*SetModulePages)(m_sheet); // Looks good up to this point > > m_sheet.Create(this, WS_CHILD | WS_VISIBLE); // DEBUG ASSERTION >FAILURE HERE > // DLGPROP.CPP > // >ASSERT(pTemplate >!= NULL) > > // For fun I tried a DoModal in place of the Create with the >same >results. > > return TRUE; >} > >Thoughts: It seems reasonable that because the DLL is not an MFC >extension >DLL, the resources included therein (ie. the dialogs around which the >property pages are built) are not available for the main application to >use. >It's also possible that the dynamically created pages are located in >DLL >memory space and inaccessible to the main application - though, as I >understand it, a regular DLL is loaded into the application's memory >space. > >Question: Has anyone been down this road before? If you've followed >this >train of thought before in your own developement and come up with a >means of >accomplishing the task I've laid out, please let me know. It seems >like >such a simple thing! Any advice would be appreciated. > >--Kyle > >//===================================================== > >Kyle Herbert >CK Engineering >http://www.ckeng.com >kyleh@ckeng.com > >"my mind is aglow, with whirling, transient nodes of >thought, careening through a cosmic vapor of invention" > > >-----From: ppbillc@srv2.sj.ablecom.net > >Kyle, > > Save the HInstance of the DLL that is being loaded. >Before you load the propertysheet template you must swap to >this dll's instance. > >Here is a class and macro set I use for this >/* > * Resource instance handling. > */ >class UTILITY_CLASS CResInstance >{ >protected: > HINSTANCE m_hOldRes; >public: > CResInstance(HINSTANCE hRes) > { > m_hOldRes = AfxGetResourceHandle(); > AfxSetResourceHandle(hRes); > } > ~CResInstance() > { > if (m_hOldRes) > AfxSetResourceHandle(m_hOldRes); > } >}; > >#define START_RES_BLOCK(hInst) {CResInstance MyResObj(hInst); >#define END_RES_BLOCK } > > > So you would > > HINSTANCE hMyDll = AfxLoadLibrary("MyDll"); > >START_RES_BLOCK(hMyDll) > > Do Your PropertySheet Stuff > >END_RES_BLOCK > >Hope this helps > >Bill > >-----From: Stuart Downing> >You create a "regular DLL" when the DLL's clients aren't necessarily >MFC applications. >These DLLs statically link MFC support. >AFXDLL's (a.k.a. MFC extension DLL's) dynamically link with MFCx0.dll >and the >client application MUST be an MFC application. >For more detail on this, see Tech Note 33. > >It appears from your message that your client app is an MFC app. >There's nothing to prevent you from loading an MFC extension DLL via >LoadLibrary >(you should actually use AfxLoadLibrary and AfxFreeLibrary - see the >help on these >functions to see why). We are doing this in our application. >It sounds to me like you WANT to use an extension DLL. > >Extension DLL resources are available to the main app via their IDs, no >matter which >DLL they came from. MFC keeps a list of the extension DLLs that are >loaded and searches >them to find the requested resource. In fact, you may want to >partition the "ID space" amongst the >resources in your extension DLLs to make sure they don't collide. >There also are ways to load a resource via its ID from a particular >DLL. > >The assertion you are getting is because >CPropertyPage::PreProcessPageTemplate >can't find the resource for your dialog. Make your DLLs extension DLLs >and your problems >(well, some of them at least :) will be solved. >------------------------ >Stuart Downing >Creative Solutions, Inc. >stuartd@izzy.net > >
| Вернуться в корень Архива |