I'm trying to show a dialog I've implemented inside an MFC extension DLL.
My app builds and compiles fine, but at runtime it's failing an assertion
during the initial call to the dialog's DoDataExchange method, where I've
mapped several controls using DDX_Control. The assertion is on line 42 of
dlgdata.cpp (VS2005 SP1) and it seems its because it can't find the control
with that ID. I've checked that the control with that ID is present on the
dialog. It's a regular modal dialog which I'm creating on the stack and then
calling DoModal.
Any "gotchas" I should know about showing dialogs defined in MFC extension
DLLs that might cause this problem?
Thanks,
Nick
"Nick Meyer" wrote:
My guess is that the it can't find the dialog resource.
Are you using AFX_MANAGE_STATE(AfxGetStaticModuleState())?
I never understood when you export a class in an MFC Extension DLL, do you
have to call AFX_MANAGE_STATE at the top of every method? This is a real
pain. And in this case where you export a class derived from CDialog, the
entry point is DoModal(), which is in CDialog, and is not overridden in the
derived class, so how do you call AFX_MANAGE_STATE in this case?
Thanks,
David
"David Ching" wrote:
I agree that it's a pain. I'm working with regular DLL's so the rules may
be different. The trick is to have the proper hInstance when the dialog
resource is parsed. You might be able to get away with an override to
OnInitDialog.
Steve
> I never understood when you export a class in an MFC Extension DLL, do you
> have to call AFX_MANAGE_STATE at the top of every method? This is a real
> pain. And in this case where you export a class derived from CDialog, the
> entry point is DoModal(), which is in CDialog, and is not overridden in the
> derived class, so how do you call AFX_MANAGE_STATE in this case?
>
Hi Stephen and David,
My understanding is that AFX_MANAGE_STATE is only necessary in _regular_ MFC
DLLs, not _extension_ DLLs. At any rate, I tried that, and I found it made
the linker complain about redefinition of _DllMain. Not sure why.
I did find the problem, though -- the application was actually calling a
function inside the DLL itself that was attempting to create and display the
dialog. I added the following:
HINSTANCE hInstOld = ::AfxGetResourceHandle();
::AfxSetResourceHandle (McfCoreDLL.hModule);
// Dialog stuff
::AfxSetResourceHandle (hInstOld);
to change the resource handle to that of the DLL, and it worked.
I'm not sure I understand why this was necessary though -- if I understand
the MSDN docs correctly, executables can use resources from extension DLLs
they've loaded without knowing in which module the resources are defined.
Tracing through the CDialog::DoModal code, I find
if (m_lpszTemplateName != NULL)
{
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
hDialogTemplate = LoadResource(hInst, hResource);
}
executed. I thought that AfxFindResourceHandle would return the handle of
the DLL, not the handle of the EXE, but it seems it's returning the handle of
the EXE.
Maybe someone at MSFT or more experienced with the inner workings of MFC
DLLs can shed some light on this.
Thanks,
Nick
You should never use AFX_MANAGE_STATE in a MFC Extension DLL. Its only
for MFC Regular DLLs.
--
Ajay
You dont ever use this macro in an Extension DLL.
--
Ajay
"Nick Meyer" wrote:
Nick,
I wonder if it's due to a non-unique resource ID. If m_lpszTemplateName is
really just a UINT, it's quite possible the wrong resource would be located.
Regards,
Steve
I dont know how your resoruces are setup but MFC extension DLLs, you
can chain resources together using CDynLinkLibrary. I dont understand
your problem though. If all your controls and dialog resides in one
DLL, you shouldnt have this problem as long as there is not ID
conflict. You must make sure that in extension DLLs *ALL* ids are
unique across all DLLs. You can ensure that by changing starting id in
resource.h.
Another method which I havent tried but is frequently mentioned in
this group is use of AfxSetResourceHandle. That may be a quick way to
get it working.
--
Ajay
That is expected. You shouldnt use this macro in your case.
> I did find the problem, though -- the application was actually calling a
> function inside the DLL itself that was attempting to create and display the
> dialog. I added the following:
>
> HINSTANCE hInstOld = ::AfxGetResourceHandle();
> ::AfxSetResourceHandle (McfCoreDLL.hModule);
> // Dialog stuff
> ::AfxSetResourceHandle (hInstOld);
>
> to change the resource handle to that of the DLL, and it worked.
That is what I mentioned in my other post. You can swap resouruce
handles at will or use CDynLinkLibrary(which I prefer).
> I'm not sure I understand why this was necessary though -- if I understand
> the MSDN docs correctly, executables can use resources from extension DLLs
> they've loaded without knowing in which module the resources are defined.
> Tracing through the CDialog::DoModal code, I find
> if (m_lpszTemplateName != NULL)
> {
> hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
> HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
> hDialogTemplate = LoadResource(hInst, hResource);
> }
>
> executed. I thought that AfxFindResourceHandle would return the handle of
> the DLL, not the handle of the EXE, but it seems it's returning the handle of
> the EXE.
If you want your resoruces to be found by MFC, you should use
CDynLinkLibrary. If you use that, you dont have to do anything special
to swap resource handles etc. It will just work as MFC will find all
the ID in the resource chain that you constructed. Resources can span
multiple modules as long as they are put together in a chain.
--
Ajay
Hi Steve,
Ding ding ding! That was exactly the problem. The executable had a dialog
resource with the same ID as the DLL. I had searched the project for other
resources with the same ID as the control that MFC choked on, but not the
dialog. I guess changing the resource handle kept MFC from finding the one
in the executable first. After reassigning the conflicting IDs, it works.
Thanks,
Nick
What you have is a timebomb in your project. If you add another
resource in the module, it will conflict again. You need to change the
available ids for that module by editing resource.h. Each Extension
DLL in your solution should have an allocation of distinct IDs
otherwise you will have this perpetual problem.
--
Ajay
Hmm, that sounds like a good enough reason to always create an Extension DLL
instead of a Regular DLL when exporting classes! I am trying to convert an
existing Win32 DLL to use MFC. Since it is a Win32 DLL, obviously it does
not export MFC classes yet, and I don't think it ever will. But it does
export a lot of normal C++ classes, and I can't see calling AFX_MANAGE_STATE
at the beginning of every method of those exported classes. But if you
don't have to in an Extension DLL, maybe I should just make it an Extension
DLL. Do you see any disadvantage to that?
Thanks,
David
In a regular DLL, you can get away without using this macro if you dont plan
to use any resources in that method. However its a good practice to do it
but its mainly done to point to the right module for resources. If your DLL
is meant to be used only by MFC client, make it a Extnesion DLL otherwise go
with Regular DLL. I always go with a Extension DLL unless I want it to be
Regular for reason mentioned above.
--
Ajay
>...
Another method which I havent tried but is frequently mentioned in
this group is use of AfxSetResourceHandle. That may be a quick way to
get it working.<
I have never understood AFX_MANAGE_STATE( ) but I use
AfxSetResourceHandle( ) absolutely everwhere, as I have lots of DLLs with
resources.
In fact in each DLL with resources I have an exported class, whose sole
responsibility is to load resources belonging to the DLL.
There's a member function for loading each dialogue of the form
(schematically)
1. remember the current resource handle found with GetResourceHandle();
2. SetResourcehandle() to the DLL's handle.
3. Construct the modal dialogue class
4. Call its DoModal()
5. Reset the resource handle you found on entry - with SetResourceHandle()
The class does this also had members for loading strings, bitmaps, and
icons: wrappers around the API functions which manage the handles.
(It works fine and is very stable, but the only thing I haven't worked out
is how the various MFC language DLLs can be handled.)
Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mozartists/mailinglist.htm
On Fri, 13 Feb 2009 08:56:54 -0800, Nick Meyer <Nick...@discussions.microsoft.com>
wrote:
Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
On Fri, 13 Feb 2009 09:19:02 -0800, Stephen Myers <Stephe...@discussions.microsoft.com>
wrote:
You cant get away from AFX_MANAGE_STATE if you have resources in a Regular
DLL. For an Extension DLL, it has no meaning. As far as AfxResourceHandle is
concerned, I never used it only because I used CDynLinkLibrary, which allows
resource chaining and it works well, especially if you have resources spread
over many many modules.
--
Ajay
I simplify things by exporting a non-MFC class that "represents" my dialog.
I add a DoModal (or ShowDialog) method to this class.
In the .cpp file, I #include the header file that contains the real dialog
declaration. And in the implementation of my DoModal, I call
AFX_MANAGE_STATE, then create the dialog object and call DoModal on it.
This way, the user of the DLL only sees the header
file that has no MFC in it, so they aren't exposed to it (this is what
allows ATL and console applications to use the DLL to show the dialog).
This works well for shared dialogs and also allows me to add other methods
to the exported class, like setting up inital values that I will
pass to the real dialog class. This has worked quite well for me in the
vast majority of situations and I've had no worries about sharing
resource ID's that come up with extension DLLs.
"David Ching" <d...@remove-this.dcsoft.com> wrote in message
news:590BC17E-56DD-4806...@microsoft.com...
Yes, it sounds like this is a good solution for you. My DLL contains many
exported classes, none of which have anything to do with resources, so
thanks to Ajay for saying I don't need to call AFX_MANAGE_STATE even if it
is a Regular MFC DLL.
-- David
I was incomplete though. You need to make sure that you may need it
*if* you want to get to app state rather than your module's. For
example, AfxGetMainWnd will return null in your Regular DLL unless you
use AFX_MANAGE_STATE(of the app).
--
Ajay
Thanks Ajay, that's clear enough. Not what I wanted to hear, but ....
-- David