Share via


afxwin1.inl ASSERT error in AfxGetResourceHandle()

Question

Wednesday, January 9, 2008 4:58 PM

My Visual C++ 05 MFC project serves as a framework that loads a variable series of MFC extension DLLs that do most of the work of the project.  Although the DLLs vary widely in function and complexity, they have many common features and derive from a common base class.  All of the DLLs export a configure() function.  In most cases, the configuration is accomplished with a standard MFC dialog and associated class derived from CDialog. 

 

The framework program and all DLL operations work properly when compiled in debug mode.  When compiled in release mode, running under XP, the framework continues to operate properly, but all DLLs produce an assertion error on the DoModal() call to the configuration dialog.  Presumably a call to any other DLL dialog will also show an error.  The call stack shows:

mfc80d.dll!CDialog:oModal() Line 535 + 0x5 bytes (dlgcore.cpp)

mfc80d.dll!AfxGetResourceHandle() Line 24 + 0x1e bytes (afxwin1.inl)

in afxwin1.inl, the assertion that fails is ASSERT( afxCurrentResourceHandle != NULL )

 I do a call to _heapchk() immediately before the DoModal() call that always returns _HEAPOK.  Compiling with or without optimization has no effect. 

Clearly the DLL resource handles are being properly initialized in the debug compilation but not in the release compilation. 

Searching the various forums shows that others have had similar issues, but I have found no clear discussion or solution.  Any suggestions?

Thanks,

SPZ

All replies (8)

Thursday, January 10, 2008 8:58 AM

Can you paste some code and show the flow bettween dlls like - who declares the dialog? who calls?

-
Venkatesh


Friday, January 11, 2008 4:35 PM

I will try to assemble some sample code.  This will take a little while.  Thanks for your attention.  I will notify you when I have some code posted.

SPZ


Friday, January 11, 2008 6:59 PM

Here is a selection of code that covers the issue.  First I have some additional comments/questions.

NOTE:  Since this it is a release compilation that is failing, some things are not clear to me:
 How is an assertion being called in a release build?
 How is the debugger tracking code in a release build?
 Why is mfc80d.dll being linked in a release build?

NOTE:  I have tried loading debug compilations of the DLLs into a release build of the framework program.  The behavior is the same as the full release build.  That is, an ASSERT error.

Here are some code snippets with discussion.

A very simple DLL is called Pause.  CPause is a class in the DLL that is derived from the base class CTask, which, in turn, is derived from the abstract base class IRTask.  IRTask exports general DLL functionality.  All main DLL classes are derived in this way, though some have additional ancestor classes between them and CTask.  Note that the DLLs are refered to as "tasks" within the project.  Note that Pause.cpp,  stdafx.cpp and stdafx.h are unchanged since they were produced by the project wizard (in VC++ 6.0).

class IRTask 
{

public:
.
.
.                                                
    //check_before_destroy: called before eliminating a task           
    virtual     bool check_before_destroy( void )= 0;
                                                                                               
    //check_before_execute: called before executing a task                                       
    virtual bool check_before_execute( void )= 0;
       
    //configure: called to configure, reconfigure or view the configuration of a task in a TD
    virtual     bool configure( bool AllowChanges )= 0;
                                                                                               
    //configure_executed: called to review the configuration of a task in the Archive           
    virtual    bool configure_executed( void )= 0;
                                                                                               
    //execute: called to run a task in the CTD                                                   
    virtual bool execute( void )= 0;
    .
    .
    .
   
};  //  IRTask() 

class AFX_EXT_CLASS CTask : public IRTask 
{
   
    public:

        //  Default constructor/destructor
                    CTask( void );
                    CTask( bool Maxi );
                    CTask( const CTask &Task );
        .
        .
        .
        virtual bool check_before_remove( void );
        virtual bool check_before_execute( void );
        virtual     bool configure( bool AllowChanges );
        virtual     bool configure_executed( void );
        virtual bool execute( void );
        .
        .
        .
};  //  CTask() 
 
class CPause : public CTask 
{

    public:

        //  Constructor/Destructor 
        CPause( void );
        CPause( bool Maxi );
        CPause( const CPause &Task );
        // Simulate a virtual copy constructor
        IRTask    *clone();
         ~CPause( void  );
        void release( void );
        .
        .
        .
        //  Public operations
        bool check_before_destroy( void );
        bool check_before_execute( void );
        bool configure( bool AllowChanges );
        bool configure_executed( void     );
        bool execute( void );
        .
        .
        .
};  //  CPause

extern "C" AFX_EXT_CLASS IRTask *get_one( UINT constructor_selector );

CPause::configure() is very simple.  It is where the error appears.  Note that in most cases, the dialog object is not declared as a pointer.  Making it a pointer, here, was just another stab at getting things working.

bool CPause::configure( bool AllowChanges )
{

    CPauseSetup *lp_SetupDlg = new CPauseSetup;

    //  Configure the setup dialog
    lp_SetupDlg->m_Comments = m_Comments;
    lp_SetupDlg->m_Prompt    = m_Prompt;
    lp_SetupDlg->m_TaskName = m_TaskName;
    lp_SetupDlg->mp_System = mp_System;

    lp_SetupDlg->set_read_write( AllowChanges );
    .
    .
    .
    if( lp_SetupDlg->DoModal() == IDOK ) // ASSERT here in release build - no error in debug build
        .
        .
        .
};  //  Configure()

The call to get_one() constructs the DLL’s main class using a selected constructor and returns a pointer upcast to *IRTask.

IRTask    *get_one( UINT constructor_selector )
{

    CPause    *Representative;

    switch( constructor_selector )
    {

        case 0:
            Representative = new CPause();
            break;
        case 1:
        case 2:
        case 3:
            Representative = new CPause( true );
            break;

    }  //  switch 

    return( ( IRTask * )Representative );

}  //  get_one() 

The framework actually constructs and destructs each DLL several times, and in several ways, during initializiation and throughout program execution.  No errors occur in either debug or release builds on initialization. This example shows the DLLs being loaded into the framework library.  This is the last access to the DLLs during program initialization.

Note that the DLLs apply the extension *.vld to the debug compilation and *.vlr to the release build. 

//##ModelId=37811F5803AB
bool CLibMan::EnumerateDLLs()
{

    CVisionApp* pApp = (CVisionApp*)AfxGetApp();
    .
    .
    .
#ifdef _DEBUG
    tpath += "\*.vld";
#else
    tpath += "\*.vlr";
#endif
   
    CFileFind finder;  
    BOOL bWorking = finder.FindFile(tpath);  
   
    CLibRecord* pRec;
   
    while (bWorking)
    {     
        bWorking = finder.FindNextFile();
       
        pRec = new CLibRecord;
        pRec->set_DLLPath(finder.GetFilePath());
       
        //task creation using mini-constructor=====================================
        HINSTANCE hInstance;
        FUNTKPTR pGetRepFunction;
       
        // vipro dll load into process
        hInstance = ::AfxLoadLibrary(finder.GetFilePath());
        if (hInstance == 0 ) {
            .
            .
            .
            MessageBox(NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
            // Free the buffer.
            LocalFree( lpMsgBuf );
           
        }
        else {

            VERIFY(pGetRepFunction = (FUNTKPTR)::GetProcAddress(hInstance, get_one"));
            pRec->set_DLLHandle(hInstance);
            pRec->set_CreateFunction(pGetRepFunction);
           
            //we can create the task now:
            IRTask* pLibTask = pRec->CreateMiniTask();
            .
            .
            .
            //dispose of the task
            //1/20/2000 error: delete pLibTask;
            pLibTask->check_before_destroy();
            pLibTask->release();
           
            //create menu entries
           
        }
    } //while
   
    return true;
}

The call to CreateMiniTask() is as follows.  It returns a pointer to the IRTask derived class using one of the three constructors.

IRTask* CLibRecord::CreateMiniTask()
{
    if (m_pCreatTk != NULL) {
        return (*m_pCreatTk)(0);
    }
    else {
        return NULL;
    }

}

At this point, there is no difficulty.  However, when the user first accesses any of the DLLs in the framework, the first call is almost always to configure(), as in this example.  Any such call produces the error.

bool CTDEditor::AddTask_Last(IRTask *pTk, bool configureNow)
{
    .
    .
    .
   
    if (configureNow ) {
       
        if (!pTk->configure(TRUE) ) {
           
            //the user canceled, remove link with previous
            if ( pPrev != 0 ) {
                pPrev->set_next(NULL);
            }
           
            return false;
        }
    }
    .
    .
    .
}

The code sample is reduced as I can make it to show a full example.  Please let me know in advance if more information is needed.  Thanks for any suggestions or assistance.

SPZ

PS.  One last question.  This is the first time I've used code blocks.  Why is the background blue in some instances?


Wednesday, April 9, 2008 10:11 PM | 2 votes

I had similar problem and figured out I just did not call in some functions in my dll

AFX_MANAGE_STATE(AfxGetStaticModuleState());

This resolved the problem for me.


Wednesday, April 16, 2008 4:23 PM

Thank you so much.  I had the same assertion error getting thrown when I tried to load a menu resource.  I had to dig down to find out that this was the Afx method causing the problem, but once I found that, a quick google search brought up this thread, and your suggestion works perfectly.


Monday, December 7, 2009 3:37 PM

I had similar problem and figured out I just did not call in some functions in my dll

AFX_MANAGE_STATE(AfxGetStaticModuleState());

This resolved the problem for me.

I was also seeing this assert. I added the above code to my project and got an error saying that

mfcs90ud.lib(dllmodul.obj) : error LNK2005: _DllMain@12 already defined in Mydll.obj

...any advice?


Thursday, November 22, 2012 12:21 PM

Hi,

did you solve the problem?

Thanks.


Thursday, March 27, 2014 2:23 PM

but it does't work under vs2010. i have an exe program which call the dll with a mfc dialog. the dll is compiled under vc6, and exe program is produced by vs2010. if exe is compiled under vc6, they work well together. when exe compiled under vs2010 ,it throw an assert with " afxwin1.inl in line 19", although i added "AFX_MANAGE_STATE(AfxGetStaticModuleState());" or " AfxSetResourceHandle(theApp.m_hInstance)". Can you give me some suggestions? thanks.