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

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


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 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




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
>
>




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