Link to home
Start Free TrialLog in
Avatar of alanolebeck
alanolebeckFlag for United States of America

asked on

Call MFC Dialog Class from WIN32 .exe Program (Mixed Program)

I have a WIN32 Application CPP program (not MFC) making calls to various dialogs and other functions also in WIN32 (not MFC).  Now in addition I want to call a function in a separate module that in turn uses the MFC Dialog class and DoModal.  I can get successful compile but the Debug version always returns a AfxGetResourceHandle assertion error and never shows the dialog box.  The release version shows the dialog box and then crashes.
I need to find some sample code that shows how to link MFC subprograms with WIN32 calling programs.  I have looked at a lot seemingly promising examples but they are just not close.  I have studied MSDN q173974 but this is for ATL and seems off.  I have tried many of the suggestions using various AFX calls with no luck.  I can send sample program easily.  Thanks,  Alan Lebeck
 
Avatar of jkr
jkr
Flag of Germany image

How are you calling the functions inside the (which I assume) MFC DLL? You are using

AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

there (i.e., at the beginning of every function that is called from a non-MFC app)?
Avatar of alanolebeck

ASKER

I have tried that to no success.  Here is the code in the MFC module.  My WIN32 calls OnFileCleanup

#include <afxwin.h>      
#include <afxdlgs.h>
#include <afxres.h>
#include <afxext.h>         // MFC extensions
//#include <afxres.rc>
#include "resource.h"  
//
void OnFileCleanup(HWND hwnd);
//
/////////////////////////////////////////////
// CCleanupDialog dialog

class CCleanupDialog : public CDialog  
{
// Construction
public:
      CCleanupDialog(      CWnd* pParent = NULL );

// Dialog Data
      //{{AFX_DATA(CCleanupDialog)
      enum { IDD = IDD_CLEANUP };
            // NOTE: the ClassWizard will add data members here
      //}}AFX_DATA

// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CCleanupDialog)
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL

// Implementation
 protected:

      // Generated message map functions
      //{{AFX_MSG(CCleanupDialog)
      virtual BOOL OnInitDialog();
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()

private:

};
/////////////////////////////////////////////////////////////////////////////
// CCleanupDialog dialog

CCleanupDialog::CCleanupDialog(      CWnd* pParent /*=NULL*/ )
      : CDialog(CCleanupDialog::IDD, pParent)
{

}

void CCleanupDialog::DoDataExchange(CDataExchange* pDX)
{
            CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CCleanupDialog, CDialog)
      //{{AFX_MSG_MAP(CCleanupDialog)
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCleanupDialog message handlers

BOOL CCleanupDialog::OnInitDialog()
{
      CString strMessage;
      strMessage.Format ( "OnInitDialog" );
      AfxMessageBox ( strMessage, MB_OK );
      return CDialog::OnInitDialog();  
}

void OnFileCleanup(HWND hwnd)
{
      CString strMessage;
      CWnd* Chwnd;
//      sRec = ((CFstestApp*) AfxGetApp())->getSealRecord();
      Chwnd=(CWnd*) hwnd;
//      strMessage.Format ( "Chwnd %d %d",hwnd, Chwnd );
//      AfxMessageBox ( strMessage, MB_OK );

      CCleanupDialog cleanupDlg ( NULL); // works same with (CWnd*) hwnd );

      strMessage.Format ( "DoModal" );
      AfxMessageBox ( strMessage, MB_OK );

      int retVal = cleanupDlg.DoModal();
      if ( retVal == IDOK      )
      {
            strMessage.Format ( "IDOK!" );
            AfxMessageBox ( strMessage, MB_OK );
      }
      else if ( retVal == IDCANCEL      )
      {
            strMessage.Format ( "IDCANCEL!" );
            AfxMessageBox ( strMessage, MB_OK );
      }
      else
      {
            return;
      }      
}
>>My WIN32 calls OnFileCleanup

And how does your app get a pointer to the dialog to do that?
Here is a part of the WIN32 code making the call:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
      int wmId, wmEvent;
      PAINTSTRUCT ps;
      HDC hdc;
      TCHAR szHello[MAX_LOADSTRING];
      LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

      switch (message)
      {
            case WM_COMMAND:
                  wmId    = LOWORD(wParam);
                  wmEvent = HIWORD(wParam);
                  // Parse the menu selections:
                  switch (wmId)
                  {
                        case IDM_ABOUT:
                           DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
                           break;
                        case IDM_EXIT:
                           DestroyWindow(hWnd);
                           break;
                        case ID_CRASH:
                              OnFileCleanup( hWnd);
                              break;
                        default:
                           return DefWindowProc(hWnd, message, wParam, lParam);
                  }
                  break;
            case WM_PAINT:
                  hdc = BeginPaint(hWnd, &ps);
                  // TODO: Add any drawing code here...
                  RECT rt;
                  GetClientRect(hWnd, &rt);
                  DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
                  EndPaint(hWnd, &ps);
                  break;
            case WM_DESTROY:
                  PostQuitMessage(0);
                  break;
            default:
                  return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}
Here is the standard WinMain implementation of Microsoft, e. g. used for a dialog-based MFC application:

----------------------------------------------------------
/////////////////////////////////////////////////////////////////////////////
// Standard WinMain implementation
//  Can be replaced as long as 'AfxWinInit' is called first

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
      LPTSTR lpCmdLine, int nCmdShow)
{
      ASSERT(hPrevInstance == NULL);

      int nReturnCode = -1;
      CWinThread* pThread = AfxGetThread();
      CWinApp* pApp = AfxGetApp();

      // AFX internal initialization
      if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
            goto InitFailure;

      // App global initializations (rare)
      if (pApp != NULL && !pApp->InitApplication())
            goto InitFailure;

      // Perform specific initializations
      if (!pThread->InitInstance())
      {
            if (pThread->m_pMainWnd != NULL)
            {
                  TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
                  pThread->m_pMainWnd->DestroyWindow();
            }
            nReturnCode = pThread->ExitInstance();
            goto InitFailure;
      }
      nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
      // Check for missing AfxLockTempMap calls
      if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
      {
            TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
                  AfxGetModuleThreadState()->m_nTempMapLock);
      }
      AfxLockTempMaps();
      AfxUnlockTempMaps(-1);
#endif

      AfxWinTerm();
      return nReturnCode;
}
---------------------------------------------------------------------

The dialog call was made in the InitInstance member function of a CWinApp derived class (normally created by wizard).

----------------------------------------------------------------------
#include "stdafx.h"
#include "testdlg.h"
#include "testdlgDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

BEGIN_MESSAGE_MAP(CTestdlgApp, CWinApp)
      //{{AFX_MSG_MAP(CTestdlgApp)
      //}}AFX_MSG
      ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

CTestdlgApp::CTestdlgApp()
{
}

CTestdlgApp theApp;

BOOL CTestdlgApp::InitInstance()
{
      AfxEnableControlContainer();

#ifdef _AFXDLL
      Enable3dControls();                  
#else
      Enable3dControlsStatic();      
#endif

      CTestdlgDlg dlg;
      m_pMainWnd = &dlg;
      int nResponse = dlg.DoModal();
      if (nResponse == IDOK)
      {
      }
      else if (nResponse == IDCANCEL)
      {
      }

      return FALSE;
}

----------------------------------------------------------------

As far as I can see, the only important thing is the call to AfxWinInit. All other is framework stuff you can adopt but don't need to.

Regards, Alex
Alex, Thanks for the comments.  I have put together some code to try.  What did you use for (!pThread->InitInstance()), that is the InitInstance function for the CWinThread function?  I would like to be able to pull in my existing WndProc(---- I listed above and call the dialog (written in a separate module) from there.  Is that possible?  Thanks.
>>>> What did you use for (!pThread->InitInstance())

InitInstance is a virtual function of class CWinThread. The user application class (derived from CWinApp derived from CWinThread) needs  to override the InitInstance as you see it in the sample code above.

You don't need to adopt that code - i think yo don't want to create a new framework . Instead you could create your dialog here and call DoModal. CDialog derived classes have their own message loop, so you could quit your prog after DoModal (as it is done in MFC framework with dialog-based applications as well).

BTW, if you need to call an MFC dialog anyway, why not simply create a MFC dialog application and move your code to InitInstance ? It seems easier for me than the way you are trying to go.

Regards, Alex
 
I just checked on of my earlier projects where an MFC DLL is used in exactly that way, and all that was necessary was in fact only adding

AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

Have you tried to 'AfxSetResourceHandle()' with the instance handle of your DLL?

BTW, what assertion in 'AfxGetResourceHandle()' are you getting exactly (line number etc.)?
thanks again.  To Alex, Do you have a complete sample code?  From what you indicated this was a working example.  Could you send me the code?  alan.o.lebeck@msti-online.com
As to restructuring what I am trying to do, as you might suspect there are 1000's of lines of code in my application in C with some complicated GUI's and C++ and in FORTRAN .dll's (yes) that I am trying to bring together including a lot of valuable MFC code.  
To jkr i did try this but I get the same essential error.  Note what I have in the example is not a .dll but just a routine in a separate file compiled and linked with the main program.  I think the error it is the error on line 21 or 22 that always shows up.  Thanks.
>>>> Do you have a complete sample code?

Do you have a version of Visual Studio? If yes, you find the sample code above in APPMODUL.CPP and WINMAIN.CPP. It's the MS implementation of the MFC application framework.

These files you should find below the installation path of Visual Studio  in MFC\SRC.

The problem when invoking a MFC dialog is that you need to init AFX (means MFC and framework), That could be done by a call to AfxWinInit(). The bigger problem might b that MFC dialogs expect to have access to an object of CWinApp by calling AfxGetApp() (you commented one of these calls). So your dialog might not work if you don't create an application object.

I would recommend to try AfxWinInit() only. If that doesn't work we might look further:

void OnFileCleanup(HWND hwnd)
{
     static bool bAfxInit = true;   // we need to init only once

     if (bAfxInit)
     {
           bAfxInit = false;
           // you need to get these arguments from your WinMain
           // store them to global variables or make them static class members of a helper class
           AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
           AfxEnableControlContainer();

//NOTE, if you use MFC in a DLL you need to set _AFXDLL in your preprocessor symbols
#ifdef _AFXDLL
            Enable3dControls();              
#else
            Enable3dControlsStatic();    
#endif

     }

     CString strMessage;
     CWnd* Chwnd;
//     sRec = ((CFstestApp*) AfxGetApp())->getSealRecord();
     Chwnd=(CWnd*) hwnd;
//     strMessage.Format ( "Chwnd %d %d",hwnd, Chwnd );
//     AfxMessageBox ( strMessage, MB_OK );

     CCleanupDialog cleanupDlg ( NULL); // works same with (CWnd*) hwnd );

     strMessage.Format ( "DoModal" );
     AfxMessageBox ( strMessage, MB_OK );

     int retVal = cleanupDlg.DoModal();
     if ( retVal == IDOK     )
     {
          strMessage.Format ( "IDOK!" );
          AfxMessageBox ( strMessage, MB_OK );
     }
     else if ( retVal == IDCANCEL     )
     {
          strMessage.Format ( "IDCANCEL!" );
          AfxMessageBox ( strMessage, MB_OK );
     }
     else
     {
          return;
     }    
}


>>>> as you might suspect there are 1000's of lines of code in my application in C
>>>> with some complicated GUI's and C++ and in FORTRAN .dll's (yes) that I am
>>>> trying to bring together including a lot of valuable MFC code.  

You should know, that CMyApp::InitInstance, is nearly equivalent to your current WinMain beside that AFX was setup properly and MFC can be used. So, if you would move your current WinMain code to InitInstance above to the statements where the dialog was created and DoModal was called, your prog should run as ever. You would have to remove the dialog and modal call cause you invoke it in your windows proc. That should work cause AFX and MFC was initialized properly.

Regards, Alex
 


   

ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thank you Alex and jkr.  Alex I tried your latest idea with bAfxInit and I was able on debug to get past the line 22 error.  In fact the program got past the initialization and the dialog box showed up.  Then an error occured on the line                   
if (!AfxGetThread()->PumpMessage())  access violation in the WINCORE.  So, it looks like I still need a little more here,  but I see some hope.  I am done for today.  Thanks
>>>> if (!AfxGetThread()->PumpMessage())

Yes, the CWinThread object is missing.  It is responsible for the dialog's message loop. You will have to supply a CWinApp derived class instance 'theApp' that could do the job.  

>>>> but I see some hope.

Yes, we will make it.

Regards, Alex
>>So, it looks like I still need a little more here,  but I see some hope

Again: What are your compiler settings? There is nothing more to do than 'AFX_MANAGE_STATE(AfxGetStaticModuleState( ));' in each function called by Win32 apps. The product I am talking about ships all over the world, I would have been told if there were any problems.
jkr, Thanks for you input.  I did copy your compiler settings and tried that.  This is not a DLL.  When I try the above I get the error at line 22.  AFX_MANAGE_STATE will not run for me unless I do            AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
           AfxEnableControlContainer(); as suggested first.
Thanks.
>>This is not a DLL

Hm, tha changes everything and makes the whole thing quite unusual. Why don't you put that in a DLL then? If you want a MFC executable, you should create one in the 1st place, this will save you from a ton of problems. Apart from that, have you tried ''AfxSetResourceHandle()' with the hInstance passed to your 'WinMain()'?
Alex and jkr,
Here is a file that works, but is a little quirky.  My WIN32 just calls OnFileCleanup(HWND hwnd) as I described before.  The dialog takes place under CTestdlgApp::InitInstance.  Return from OnFileCleanup returns before the dialog is closed!  So, could still be improved.  Thanks for all of your help.

#include <afxwin.h>      
#include <afxdisp.h>  
//#include <afxres.rc>
#include "resource.h"
//
extern HINSTANCE gghInstance;
extern HINSTANCE ghPrevInstance;
extern LPSTR     glpCmdLine;
extern int       gnCmdShow;
//
void OnFileCleanup(HWND hwnd);  
//
/////////////////////////////////////////////
// CCleanupDialog dialog
class CCleanupDialog : public CDialog  
{
// Construction
public:
      CCleanupDialog(      CWnd* pParent = NULL );
// Dialog Data
      //{{AFX_DATA(CCleanupDialog)
      enum { IDD = IDD_CLEANUP };
            // NOTE: the ClassWizard will add data members here
      //}}AFX_DATA
// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CCleanupDialog)
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL
// Implementation
 protected:
      // Generated message map functions
      //{{AFX_MSG(CCleanupDialog)
      virtual BOOL OnInitDialog();
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
private:

};
/////////////////////////////////////////////////////////////////////////////
class CTestdlgApp : public CWinThread
{
      DECLARE_DYNCREATE(CTestdlgApp)
private:
      CTestdlgApp();           // protected constructor used by dynamic creation
// Attributes
public:

// Operations
public:
// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CTestdlgApp)
      public:
      virtual BOOL InitInstance();
      virtual int ExitInstance();
      //}}AFX_VIRTUAL
// Implementation
protected:
      virtual ~CTestdlgApp();
      // Generated message map functions
      //{{AFX_MSG(CTestdlgApp)
            // NOTE - the ClassWizard will add and remove member functions here.
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};
//
// CCleanupDialog dialog
CCleanupDialog::CCleanupDialog(      CWnd* pParent /*=NULL*/ )
      : CDialog(CCleanupDialog::IDD, pParent)
{

}
void CCleanupDialog::DoDataExchange(CDataExchange* pDX)
{
            CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CCleanupDialog, CDialog)
      //{{AFX_MSG_MAP(CCleanupDialog)
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCleanupDialog message handlers

BOOL CCleanupDialog::OnInitDialog()
{
//      CString strMessage;
//      strMessage.Format ( "OnInitDialog" );
//      AfxMessageBox ( strMessage, MB_OK );
      return CDialog::OnInitDialog();  
}

void OnFileCleanup(HWND hwnd)
{
      static bool bAfxInit = true;    // we need to init only once
//
     if (bAfxInit)
     {
           bAfxInit = false;
// you need to get these arguments from your WinMain
// store them to global variables or make them static class members of a helper class
           AfxWinInit(gghInstance, ghPrevInstance, glpCmdLine, gnCmdShow);
       }
      AfxBeginThread( RUNTIME_CLASS(CTestdlgApp) ,THREAD_PRIORITY_NORMAL, 0, 0, NULL );
      return;
}
//
// CTestdlgApp
IMPLEMENT_DYNCREATE(CTestdlgApp, CWinThread)

CTestdlgApp::CTestdlgApp()
{
}

CTestdlgApp::~CTestdlgApp()
{
}

BOOL CTestdlgApp::InitInstance()
{
/*#ifdef _AFXDLL    // Will not compile
     Enable3dControls();              
#else
     Enable3dControlsStatic();    
#endif
*/
     CCleanupDialog dlg;
     m_pMainWnd = &dlg;
     int nResponse = dlg.DoModal();
     if (nResponse == IDOK)
     {
     }
     else if (nResponse == IDCANCEL)
     {
     }
     return TRUE;
}

int CTestdlgApp::ExitInstance()
{
// TODO:  perform any per-thread cleanup here
      return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CTestdlgApp, CWinThread)
      //{{AFX_MSG_MAP(CTestdlgApp)
            // NOTE - the ClassWizard will add and remove mapping macros here.
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTestdlgApp message handlers


SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Alex and jkr,
Thank you again for your suggestions.  Here is a simpler version of what will work, and now it behaves like a modal dialog!  The calling program remains the same.  Thanks.  

#include <afxwin.h>      
#include <afxdisp.h>
//#include <afxres.rc>
#include "resource.h"
//
extern HINSTANCE ghInstance;
extern HINSTANCE ghPrevInstance;
extern LPSTR     glpCmdLine;
extern int       gnCmdShow;
//
void OnFileCleanup(HWND hwnd);
UINT TryDirect(LPVOID p1);  
//
/////////////////////////////////////////////
// CCleanupDialog dialog
class CCleanupDialog : public CDialog  
{
// Construction
public:
      CCleanupDialog(      CWnd* pParent = NULL );
// Dialog Data
      //{{AFX_DATA(CCleanupDialog)
      enum { IDD = IDD_CLEANUP };
            // NOTE: the ClassWizard will add data members here
      //}}AFX_DATA
// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CCleanupDialog)
      protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //}}AFX_VIRTUAL
// Implementation
 protected:
      // Generated message map functions
      //{{AFX_MSG(CCleanupDialog)
      virtual BOOL OnInitDialog();
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
private:

};
/////////////////////////////////////////////////////////////////////////////
//
// CCleanupDialog dialog
CCleanupDialog::CCleanupDialog(      CWnd* pParent /*=NULL*/ )
      : CDialog(CCleanupDialog::IDD, pParent)
{

}
void CCleanupDialog::DoDataExchange(CDataExchange* pDX)
{
            CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CCleanupDialog, CDialog)
      //{{AFX_MSG_MAP(CCleanupDialog)
      //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCleanupDialog message handlers

BOOL CCleanupDialog::OnInitDialog()
{
//      CString strMessage;
//      strMessage.Format ( "OnInitDialog" );
//      AfxMessageBox ( strMessage, MB_OK );
      return CDialog::OnInitDialog();  
}

void OnFileCleanup(HWND hwnd)
{
      CWinThread * pThread;  
      HANDLE hHandle=0;
      CString strMessage;
      static bool bAfxInit = true;    // we need to init only once
//
     if (bAfxInit)
     {
           bAfxInit = false;
// you need to get these arguments from your WinMain
// store them to global variables or make them static class members of a helper class
           AfxWinInit(ghInstance, ghPrevInstance, glpCmdLine, gnCmdShow);
       }
    pThread = AfxBeginThread(TryDirect,NULL);
    pThread->m_bAutoDelete = FALSE;     // CWinThread object will not be destroyed
     WaitForSingleObject(pThread->m_hThread,INFINITE); // wait for thread to terminate
    delete pThread;
      strMessage.Format ( "End OnFileCleanup" );
      AfxMessageBox ( strMessage, MB_OK );

               
      return;
}
//
UINT TryDirect(LPVOID p1)
{
/*#ifdef _AFXDLL    // Will not compile
     Enable3dControls();              
#else
     Enable3dControlsStatic();    
#endif
*/
     CCleanupDialog dlg;
//     m_pMainWnd = &dlg;
     int nResponse = dlg.DoModal();
     if (nResponse == IDOK)
     {
     }
     else if (nResponse == IDCANCEL)
     {
     }

     return 0;
}