CWnd as ActiveX control without .dll or .ocx file in C++?

3k Views Asked by At

Dear MFC/ActiveX/COM cracks, I have 'inherited' the source of an old MFC application (originally created with Visual Studio 6) which builds and runs so far in VS 2010, but has embedded some ActiveX controls as source code, apparently generated by the Visual Studio wizard (.h and .cpp files, see below); however not in an own subproject so that a .dll or .ocx file are generated. Here is the relevant part of the header file of one such control:

#if !defined(AFX_CHARTFX_H__F8A080E0_0647_11D4_92B0_0000E886CDCC__INCLUDED_)
#define AFX_CHARTFX_H__F8A080E0_0647_11D4_92B0_0000E886CDCC__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++

// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.

/////////////////////////////////////////////////////////////////////////////
// CChartfx wrapper class

class CChartfx : public CWnd
{
protected:
    DECLARE_DYNCREATE(CChartfx)
public:
    CLSID const& GetClsid()
    {
        static CLSID const clsid
            = { 0x8996b0a1, 0xd7be, 0x101b, { 0x86, 0x50, 0x0, 0xaa, 0x0, 0x3a, 0x55, 0x93 } };
        return clsid;
    }
    virtual BOOL Create(LPCTSTR lpszClassName,
        LPCTSTR lpszWindowName, DWORD dwStyle,
        const RECT& rect,
        CWnd* pParentWnd, UINT nID,
        CCreateContext* pContext = NULL)
    { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }

    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
        const RECT& rect, CWnd* pParentWnd, UINT nID,
        CFile* pPersist = NULL, BOOL bStorage = FALSE,
        BSTR bstrLicKey = NULL)
    { return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,
        pPersist, bStorage, bstrLicKey); }
    //rest of header file omitted

Note that this class inherits from CWnd and not some OCX class. But since all MFC windows are COM components (as I read somewhere) and this is generated code it should have worked some time ago. I also read that this may be really a migration gap which occurred somewhere before 2005. Also note the DECLARE_DYNCREATE, so I think this is late binding, using the IDispatch interface. So MFC will call a Create() function for us.

The above control is used via aggregation by an encompassing CDialog (also created with VS wizard):

//... analysedlg.h  - leading auto-generated stuff omitted
class CAnalyseDlg : public CDialog
{
  CChartfx m_chhrtfx;
  //... enum for resource ID, DoDataExchange, message map, other members…
}

The dialog, in turn, is embedded in a view class of the application (again, via a member variable) and created by invoking DoModal() in a menu item event handler.

So, when I click on the corresponding menu item, I get an m_hWnd NULL assertion and when hitting 'retry' in the popped up dialogue, the following stack (excerpt):

mfc100d.dll!COleControlContainer::FillListSitesOrWnds(_AFX_OCC_DIALOG_INFO * pOccDlgInfo)  line 925 + 0x23 Bytes    C++
mfc100d.dll!COccManager::CreateDlgControls(CWnd * pWndParent, const char * lpszResourceName, _AFX_OCC_DIALOG_INFO * pOccDlgInfo)  line 410  C++
mfc100d.dll!CDialog::HandleInitDialog(unsigned int __formal, unsigned int __formal)  line 715 + 0x22 Bytes  C++
mfc100d.dll!CWnd::OnWndMsg(unsigned int message, unsigned int wParam, long lParam, long * pResult)  line 2383 + 0x11 Bytes  C++
mfc100d.dll!CWnd::WindowProc(unsigned int message, unsigned int wParam, long lParam)  line 2087 + 0x20 Bytes    C++
mfc100d.dll!AfxCallWndProc(CWnd * pWnd, HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  line 257 + 0x1c Bytes  C++
mfc100d.dll!AfxWndProc(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  line 420    C++
mfc100d.dll!AfxWndProcBase(HWND__ * hWnd, unsigned int nMsg, unsigned int wParam, long lParam)  line 420 + 0x15 Bytes   C++
user32.dll!766162fa()   
[missing frames omitted by me]  
mfc100d.dll!CWnd::CreateDlgIndirect(const DLGTEMPLATE * lpDialogTemplate, CWnd * pParentWnd, HINSTANCE__ * hInst)  line 366 + 0x2a Bytes    C++
mfc100d.dll!CDialog::DoModal()  line 630 + 0x20 Bytes   C++

In the VS debug output there are the following lines:

CoCreateInstance of OLE control {8996B0A1-D7BE-101B-8650-00AA003A5593} failed.
>>> Result code: 0x80040154
>>> Is the control is properly registered?
Warning: Resource items and Win32 Z-order lists are out of sync. Tab order may be not defined well. 

So apparently the call to CoCreateInstance had already been done and silently failed without an assertion which would have been nice to have had. Does anybody know where this is?

My central question is whether it is correct that in this case normally MFC would take care for registering the control even if it is not in an .dll or .ocx project and that it must have worked like this in the past. I read somewhere that CreateDlgIndirect with a DialogTemplate is a way of creating ActiveX controls without needing a .dll or .ocx file. In the above stack, this is called, too, but not for the ActiveX control, but for the dialogue instead.

Does anyone know more about this issue and how to fix it? If I do have to manually register the controls, e.g. using regsvr32.exe or via source code, is there a way without .dll or .ocx files? Or do I have to repackage the ActiveX components in their own projects (what would be more component-based/modular anyway)?

I hope my problem description is precise enough and I would be very thankful for any answer. Kind regards.

1

There are 1 best solutions below

0
On

I've just run into this when using an old ActiveX control. Apparently it was apartment threaded and I was trying to call CoInitializeEx() with COINIT_MULTITHREADED.