views:

76

answers:

2

21st July: Updated, see bottom

In VC++ 2005 I have 2 projects. Firstly, a MFC DLL project (not an extension DLL) which has a simple dialog:

TestDlg.h

#pragma once
#include "afxwin.h"
#include "resource.h"
// CTestDlg dialog
namespace Dialogs
{
    class __declspec(dllexport) CTestDlg : public CDialog
    {
        DECLARE_DYNAMIC(CTestDlg )

    public:
        CTestDlg (CWnd* pParent = NULL);   // standard constructor
        virtual ~CTestDlg ();

    // Dialog Data
        enum { IDD = IDD_TEST_DLG };
    }
}

Then I have a Win32 console app, with MFC libraries, that does:

TestApp.cpp

#include "stdafx.h"
#include "TestApp.h"
#include <TestDlg.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: MFC initialization failed\n"));
        nRetCode = 1;
    }
    else
    {

        Dialogs::CTestDlg dlg;
        dlg.DoModal();
    }
    return nRetCode;
}

It builds and runs up, but no dialog appears. Stepping into DoModal()...

dlgcore.cpp

INT_PTR CDialog::DoModal()
{
    // can be constructed with a resource template or InitModalIndirect
    ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||
        m_lpDialogTemplate != NULL);

    // load resource as necessary
    LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
    HGLOBAL hDialogTemplate = m_hDialogTemplate;
    HINSTANCE hInst = AfxGetResourceHandle();
    if (m_lpszTemplateName != NULL)
    {
        hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
        HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
        hDialogTemplate = LoadResource(hInst, hResource);
    }
    if (hDialogTemplate != NULL)
        lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);

    // return -1 in case of failure to load the dialog template resource
    if (lpDialogTemplate == NULL)
        return -1;

    ... more stuff

For whatever reason it seems it can't load the resource, returning -1 at the end of the copied section. I've looked at a few articles on CodeGuru, etc, and not seen anything obvious. Is my class not being exported/imported right? Or is it a resource problem? Or is the problem that I'm trying to display it from a console (MFC) app?

21st July Update I created an overridden DoModal as so:

INT_PTR CTestDlg::DoModal()
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
    return CDialog::DoModal();
}

This seems to work although should I be overriding a different method to get the functionality more generic?

+2  A: 

I'm not sure if this construction can actually work. If possible, export just a function which opens the dialog inside the DLL.

But perhaps the use of the AFX_MANAGE_STATE-macro can help you.

dwo
Several places I read you _can_ export a CDialog-based class from a DLL. But your suggestion is not bad, a method like `Dialogs::CTestDlg *Dialogs::ShowTestDlg()`?
John
Yes, something like that, but without returning a pointer to the dialog class. In that case you need to know the class again!
dwo
The class itself seems to work fine though, only the resources. Exclusively using wrapper methods would be a big pain as data has to be set and returned from the dialog.
John
I haven't worked with MFC for a long time but I suspect this is it. I think the problem is that you have separate pools of resource/ID-to-class mappings between the app and the DLL and neither can access each other's even though they're all MFC. The one that always used to bite me I think is that the HWND-to-class mappings were per-thread (or something like that).
Rup
Is there any reason against using a MFC extension DLL?
dwo
@dwo not so much, but would it actually help? I've not seen anything saying I should need to.
John
The extension dll manages to get into the resource chain of the application, so your dialog would find its resource. But I just saw in your update, that my tip with the AFX_MANAGE_STATE also did the trick!
dwo
+2  A: 

As you've noted, the problem is that MFC is not fining the resource, since the module context is set to your main EXE rather than the DLL containing the dialog resource.

Manually calling AFX_MANAGE_STATE to ensure the DLL context is established is one way to work this, but it's not transparent. The ideal way is to compile your DLL as an extension DLL, so that MFC can take care of loading the resource from a list of extension DLLs and managing memory between the DLLs.

You may be able to short-cut creating the extension DLL and simply create your own CDynLinkLibrary instance, which adds your DLL to the main resource list. I have not tried this, preferring instead to take the extension dll _AFXDLL route, so this may or may not work.

The MSDN article on Extension DLLs may help you determine if they are suitable in your case, and what advantages/drawbacks they bring.

mdma