locked
A property sheet issue, when creating the sheet and pages in different modules RRS feed

  • Question

  • The codes use new to create some property pages in one DLL module, and pass out the pointers. In the exe module, it constructs a property sheet and then adds the property page pointers. The DLL module is implemented using ATL with MFC supported. The exe module is a MFC doc/view program.

     

    Here is the demo to duplicate the problem.

     

    In exe module, I put 4 buttons in toolbar. These are 4 different situations I tried.

    Code Snippet

    #import "..\PropPagesDll_test\Debug\PropPagesDll_test.tlb" no_namespace named_guids

    void CAppApp::OnBtn1()

    {

    // create the COM object

    ICPropPagesEnum_testPtr ptr;

    ptr.CreateInstance(CLSID_CPropPagesEnum_test);

    // in this exe module, create the property sheet

    CPropertySheet dlgPropertySheet(_T("Simple PropertySheet"));

    // in that DLL module, use new to create property pages, and pass out the pointers

    CPropertyPage * pPage1 = NULL;

    ptr->EnumPages((long*)&pPage1);

    dlgPropertySheet.AddPage(pPage1);

    CPropertyPage * pPage2 = NULL;

    ptr->EnumPages((long*)&pPage2);

    dlgPropertySheet.AddPage(pPage2);

    // show the property sheet.

    dlgPropertySheet.DoModal();

    delete pPage1;

    delete pPage2;

    }

    void CAppApp::OnBtn2()

    {

    //

    // the property sheet and pages are all created in the DLL

    //

    ICPropPagesEnum_testPtr ptr;

    ptr.CreateInstance(CLSID_CPropPagesEnum_test);

    ptr->InnerCall();

    }

    void CAppApp::OnBtn3()

    {

    //

    // if you comment out the AFX_MANAGE_STATE(AfxGetStaticModuleState());

    // and then click on this button, every thing seems OK.

    //

    ICPropPagesEnum_testPtr ptr;

    ptr.CreateInstance(CLSID_CPropPagesEnum_test);

    CPropertySheet dlgPropertySheet(_T("Simple PropertySheet"));

    HINSTANCE hOld = ::AfxGetResourceHandle();

    HINSTANCE hNew = ::LoadLibrary(_T("PropPagesDll_test.dll"));

    ::AfxSetResourceHandle(hNew);

    CPropertyPage * pPage1 = NULL;

    ptr->EnumPages((long*)&pPage1);

    dlgPropertySheet.AddPage(pPage1);

    CPropertyPage * pPage2 = NULL;

    ptr->EnumPages((long*)&pPage2);

    dlgPropertySheet.AddPage(pPage2);

    dlgPropertySheet.DoModal();

    delete pPage1;

    delete pPage2;

    ::AfxSetResourceHandle(hOld);

    }

    void CAppApp::OnBtn4()

    {

    //

    // pass in the property sheet pointer, add pages in that DLL module

    //

    ICPropPagesEnum_testPtr ptr;

    ptr.CreateInstance(CLSID_CPropPagesEnum_test);

    CPropertySheet dlgPropertySheet(_T("Simple PropertySheet"));

    ptr->AddPages((long)&dlgPropertySheet);

    dlgPropertySheet.DoModal(); //memory leak here

    }

     

     

    In the DLL module implemented by ATL with MFC supported. I define a COM object CPropPagesEnum_test. The object realizes interface ICPropPagesEnum_test .

     

    Code Snippet

    STDMETHODIMP CCPropPagesEnum_test::EnumPages(long* pNew)

    {

    // if you comment out this line, and then click on button 3

    // everything seems OK.

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    static int nCnt = 1;

    CPropertyPage * pPage = NULL;

    if( nCnt % 2 )

    pPage = new CPropPage01();

    else

    pPage = new CPropPage02();

    *pNew = (long)pPage;

    nCnt ++ ;

    return S_OK;

    }

    STDMETHODIMP CCPropPagesEnum_test::InnerCall(void)

    {

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    CPropertySheet dlgPropertySheet(_T("Simple PropertySheet"));

    CPropertyPage * pPage1 = NULL;

    EnumPages((long*)&pPage1);

    dlgPropertySheet.AddPage(pPage1);

    CPropertyPage * pPage2 = NULL;

    EnumPages((long*)&pPage2);

    dlgPropertySheet.AddPage(pPage2);

    dlgPropertySheet.DoModal();

    delete pPage1;

    delete pPage2;

    return S_OK;

    }

    STDMETHODIMP CCPropPagesEnum_test::AddPages(long addressSht)

    {

    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    CPropertySheet * pSheet = (CPropertySheet*)addressSht;

    CPropertyPage * pPage1 = NULL;

    EnumPages((long*)&pPage1);

    pSheet->AddPage(pPage1);

    CPropertyPage * pPage2 = NULL;

    EnumPages((long*)&pPage2);

    pSheet->AddPage(pPage2);

    return S_OK;

    }

     

     

    I think this problem is related with the Microsoft DLL resource handle issue. Does anyone here know how to work around this in an easy way?

     

    I have this demo project, but I do NOT know how to up load an attachment in this forum. If someone want to see the demo, contact me via email. I'll send you the project.

     

    Thanks !

    Tuesday, June 5, 2007 4:29 AM

Answers

  • I got help from my colleage David.

     

    Here is his reply:

    Hi,The Afx Manage State macros are used for several purposes, to manage MFC's resource lookup scheme, to allow assembly isolation (side-by-side), etc. The problem you have may be occurring because you are using CPropertyPage outside of the module in which you created it. If you look at the CPropertyPage implementation in dlgprop.cpp you'll see that AFX_MANAGE_STATE is not called in any of the methods, but at least some of the methods do look up resources. It may be that Microsoft did not intend for pointers to the CPropertyPage class to be passed between modules and that it is trying to access resources from the wrong DLL module, causing your problem. This advice given in this MFC knowledgebase article may help you too: http://support.microsoft.com/kb/140105

     

    And mine:

    David, you are right! Here is the call stack when I am hit by the issue. See the top one in the stack, "afxMapHWND" calls the AFX_MODULE_THREAD_STATE. It can be found in file wincore.cpp.

    > mfc80ud.dll!afxMapHWND(int bCreate=1) Line 282 C++
    mfc80ud.dll!CWnd::FromHandle(HWND__ * hWnd=0x00580f3c) Line 302 + 0x7 bytes C++
    mfc80ud.dll!CPropertySheet::GetActivePage() Line 884 + 0x1c bytes C++
    mfc80ud.dll!CPropertySheet:SurprisenKickIdle(unsigned int wp=0, long lp=0) Line 1290 + 0x8 bytes C++

    Wednesday, June 6, 2007 2:22 AM

All replies

  • I'd re-think your design. As you've seen, the resource-instance management is problematic. Also, your code will fail in 64-bit: your type punning between object pointers and longs is invalid, and the 64-bit compiler won't let you get away with it. If you were building /W4 /WX (which you should be), this code wouldn't compile in 32-bit either.
    Tuesday, June 5, 2007 5:27 AM
  • Thanks, Sdi. I totally agree with you about the 64-bit comment. Now I am using /w3, and I saw the warning too. For some reasons, the codes are already there. I quickly write this demo to duplicate the problem for easy diagnosis. My point here is the property sheet. I want to know, if we can do property sheet and pages this way.

     

    I found this problem when mitigating some memory leak issues when "AFX_MANAGE_STATE(AfxGetStaticModuleState());" was commented out. The leak reason is that MFC store something in module global variables when creating the dialogs. Only depend on correct module state, they can be cleaned up. When I put that macro back, this problem happens.

     

    PS:

    About the memory leak, most of them are "pDlgInfo->m_pItemInfo = new _AFX_OCC_DIALOG_INFO::ItemInfo[iItems];", you can see this line in file, C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\occmgr.cpp, line 195.

    Tuesday, June 5, 2007 6:05 AM
  • I got help from my colleage David.

     

    Here is his reply:

    Hi,The Afx Manage State macros are used for several purposes, to manage MFC's resource lookup scheme, to allow assembly isolation (side-by-side), etc. The problem you have may be occurring because you are using CPropertyPage outside of the module in which you created it. If you look at the CPropertyPage implementation in dlgprop.cpp you'll see that AFX_MANAGE_STATE is not called in any of the methods, but at least some of the methods do look up resources. It may be that Microsoft did not intend for pointers to the CPropertyPage class to be passed between modules and that it is trying to access resources from the wrong DLL module, causing your problem. This advice given in this MFC knowledgebase article may help you too: http://support.microsoft.com/kb/140105

     

    And mine:

    David, you are right! Here is the call stack when I am hit by the issue. See the top one in the stack, "afxMapHWND" calls the AFX_MODULE_THREAD_STATE. It can be found in file wincore.cpp.

    > mfc80ud.dll!afxMapHWND(int bCreate=1) Line 282 C++
    mfc80ud.dll!CWnd::FromHandle(HWND__ * hWnd=0x00580f3c) Line 302 + 0x7 bytes C++
    mfc80ud.dll!CPropertySheet::GetActivePage() Line 884 + 0x1c bytes C++
    mfc80ud.dll!CPropertySheet:SurprisenKickIdle(unsigned int wp=0, long lp=0) Line 1290 + 0x8 bytes C++

    Wednesday, June 6, 2007 2:22 AM