0

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.

juniper
  • 311
  • 5
  • 13
  • "... all MFC windows are COM components ..." - I doubt that. A CWnd is just a wrapper around the HWND handle. So do you have the source for the ChartFx-component or not? – dwo Nov 07 '11 at 22:03
  • > "… all MFC windows are COM components ..." - I doubt that. A CWnd is just a wrapper around the HWND handle. A.: You are right, this statement of mine was wrong. I had misinterpreted the following article by Paul DiLascia: http://msdn.microsoft.com/en-us/magazine/cc301408.aspx (Two articles on that page, search for 'programming COM' on this page to jump to the first sentence.) – juniper Nov 08 '11 at 19:05
  • I found some more information about these CWnds wrapping ActiveX controls: http://www.murrayc.com/learning/windows/useactivex.shtml http://msdn.microsoft.com/en-us/library/aa294388(v=VS.60).aspx '[..] Controls built with the MFC ActiveX control classes are self-registering because two entry points in the control's executable, DLLRegisterServer and DLLUnregisterServer, are automatically added when ControlWizard creates the control's files. [..]' – juniper Nov 08 '11 at 19:06
  • > So do you have the source for the ChartFx-component or not? A.: As I found now, it's only the MFC wrapper. After searching the web for the CLSID I found the component is an ocx which may come with Delphi: http://community.softwarefx.com/forums/t/6396.aspx So I will have a look whether we have got them on one of the other machines. – juniper Nov 08 '11 at 19:06
  • So this could be closed. I found the ChartFx component at one of our Delphi installations. This really was the missing piece and now it works. – juniper Nov 22 '11 at 18:23

1 Answers1

0

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.

snips-n-snails
  • 637
  • 5
  • 22