Question

I'm trying to create a ActiveX control that contains a System.Windows.Forms.Integration.ElementHost control to host WPF content in the control.

I was able to create the ActiveX control using MFC and I can also load an MFC CDialog in it. However, as soon as I try to create a managed control, it will crash with an AccessViolationException at

return CreateControl(info,dwStyle,&pt,&size,pParentWnd,nID);

this in in afxwinforms.inl:122.

My MainDialog.cpp file looks like this:

// MainDialog.cpp : implementation file
//

#include "stdafx.h"
#include "MFCAX.h"
#include "MainDialog.h"
#include "afxdialogex.h"


// CMainDialog dialog

IMPLEMENT_DYNAMIC(CMainDialog, CDialog)

CMainDialog::CMainDialog(CWnd* pParent /*=NULL*/)
    : CDialog(CMainDialog::IDD, pParent)
{

}

CMainDialog::~CMainDialog()
{
}

void CMainDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);

    //DDX_ManagedControl(pDX, IDC_BUTTON1, m_elementHost);
}

BOOL CMainDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    System::Windows::Forms::MessageBox::Show(gcnew System::String("init"));

    return m_elementHost.CreateManagedControl(WS_CHILD | WS_VISIBLE, IDC_BUTTON1, this);
}

BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
END_MESSAGE_MAP()


// CMainDialog message handlers

The message box shows up, but then the crash described above will follow.

I feel like there's only a small piece missing, but I don't really know where to search. Any help is appreciated.

Was it helpful?

Solution

I finally figured it out, so I'll post a quick overview of what is to do here for anyone having a similar problem:

First of all: Don't go the WPF in WinForms ElementHost route, it's much easier than that!

To load a WPF control and display it on the ActiveX control, use the following method:

HWND CMFCAXCtrl::GetUserControl1Hwnd(HWND parent, int x, int y, int width, int height)
{
   System::Windows::Interop::HwndSourceParameters^ sourceParams = gcnew System::Windows::Interop::HwndSourceParameters("MFCWPFApp");
   sourceParams->PositionX    = x;
   sourceParams->PositionY    = y;
   sourceParams->Height       = height;
   sourceParams->Width        = width;
   sourceParams->ParentWindow = System::IntPtr(parent);
   sourceParams->WindowStyle  = WS_VISIBLE | WS_CHILD;
   m_hwndSource = gcnew System::Windows::Interop::HwndSource(*sourceParams);

   EventReceiver^ recv = gcnew EventReceiver(this);

   m_wpfUC = gcnew WpfExample::TestControl();
   m_wpfUC->Clicked += gcnew System::EventHandler(recv, &EventReceiver::Handler);

   m_hwndSource->RootVisual = m_wpfUC;

   return (HWND) m_hwndSource->Handle.ToPointer();
}

You'll have to setup a HwndSource to paint the WPF control in. You can also wire up event handlers etc. to handle WPF events in C++ (see the Clicked event handler).

The corresponding members are defined as follows:

gcroot<System::Windows::Interop::HwndSource^> m_hwndSource;
gcroot<WpfExample::TestControl^> m_wpfUC;
HWND m_hwndWPF;

Now comes the tricky part: It seems that there is a problem when loading assemblies like this, any assemblies that are not installed in the GAC are not found. So you'll have to wire up your own assembly resolver. There you must make sure that an assembly is only loaded once, otherwise you will get strange errors and exceptions. A quick and dirty implementation could look like this:

ref class Resolved {
public:
    // just needed to store the assembly that was loaded.
    static  System::Reflection::Assembly^   assembly;
};

System::Reflection::Assembly^ ResolveHandler(System::Object^ Sender, System::ResolveEventArgs^ args)
{
    return Resolved::assembly;
}

and in the OnCreate method of the control:

Resolved::assembly = System::Reflection::Assembly::LoadFile(gcnew System::String("Path:\\To\\My\\assembly\\WpfExample.dll"));

System::AppDomain::CurrentDomain->AssemblyResolve += gcnew System::ResolveEventHandler(ResolveHandler); 

// To create the main dialog 
m_hwndWPF = GetUserControl1Hwnd(this->GetSafeHwnd(), 0, 0, 500, 500);

Now you're all set and you should be able to use WPF in any legacy project that supports ActiveX.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top