visual-c++commfcatlvisual-c++-2010

Using IDispatchImpl with an Unregistered Interface in an MFC+ATL EXE


I started the project as an MFC Application (for the GUI..), and later added support for ATL.

I then coded a simple ATL-COM object implementing a non registered dual interface using IDispatchImpl, with the 0xfff for Major and Minor, to tell ATL to load the TLB from the EXE.

I skip some details, but at the end, after some debugging I found that the CComTypeInfoHolder::GetTI implementation in atlcom.h was NOT trying to load the TLB from the EXE, but was searching it in the registry. Reason : a m_plibid variable was NOT corresponding to the DECLARE_LIBID macro use in my ATL::CAtlMfcModule declaration.

After some googling I found Bug: CAtlMfcModule::InitLibId() not called and added a call to InitLibId in my module CTOR.

Works fine, now.

Question: Is that a known bug? with a known fix? I am not confortable with my workaround of such an old bug. Is there another way of dealing with that?

UPDATE: additional information, as an answer states there is no bug...

IDispatchImpl Class:

By default, the IDispatchImpl class looks up the type information for T in the registry. To implement an unregistered interface, you can use the IDispatchImpl class without accessing the registry by using a predefined version number. If you create an IDispatchImpl object that has 0xFFFF as the value for wMajor and 0xFFFF as the value for wMinor, the IDispatchImpl class retrieves the type library from the .dll file instead of the registry.

Excerpt from CComTypeInfoHolder::GetTI Implementation in atlcom.h:

if (InlineIsEqualGUID( CAtlModule::m_libid, *m_plibid) &&
                       m_wMajor == 0xFFFF &&
                       m_wMinor == 0xFFFF ) {
    TCHAR szFilePath[MAX_PATH];
    DWORD dwFLen = ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(), szFilePath, MAX_PATH);
    [...]
    hRes = LoadTypeLib(pszFile, &pTypeLib);
} else {
    [...]
    hRes = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor, lcid, &pTypeLib);

So, it seems clear to me that there is an advertised behavior: use 0xffff for minor and major and ATL will try to load the typelib from module, not from registry, provided that your CAtlModule::m_libid is up todate. How is CAtlModule::m_libid expected to be be up to date? By using the DECLARE_LIBID macros. How does work that macro? by defining a static InitLibId function, which set up CAtlModule::m_libid.

The bug: when your module derives from ATL::CAtlMfcModule, the defined InitLibId function is NOT called (as ATL::CAtlMfcModule is not a class template)


Solution

  • You are correct, if you are using -1 for major/minor versions, then is is assumed that type information would be taken from the binary. This however does not work with MFC projects: DECLARE_LIBID only works up to CAtlMfcModule class but not its descendants.

    A quick fix might be like this, in atlbase.h:

    //class CAtlMfcModule :
    //  public ATL::CAtlModuleT<CAtlMfcModule>
    
    template <typename T>
    class CAtlMfcModuleT : 
        public ATL::CAtlModuleT<T>
    

    and then in your project:

    //class CMFCApplication1Module :
    //  public ATL::CAtlMfcModule
    
    class CMFCApplication1Module :
        public ATL::CAtlMfcModuleT<CMFCApplication1Module>
    

    If you post it on MS Connect as a bug, you can leave a link here for others to go upvote the bug.