ActiveX control (OCX) in a WPF window

This is an effort to explain and generalize the MSDN article, Walkthrough: Hosting an ActiveX Control in WPF, under the WPF and Windows Forms Interoperation category.

Putting an ActiveX, a.k.a, OCX control, to a WPF window is somewhat similar to an OCX in a Visual C# Windows Form, but there are some differences.

Unlike a Windows Form, a WPF window cannot embed an OCX directly, because arbitrary OCX does not follow the WPF rules in sharing the screen real estate.

You must create a System.Windows.Forms.Integration.WindowsFormsHost object as a host, put your OCX as a child to the host, then add the host as a child to a content container, such as a Grid object, in a WPF window.

However, the WindowsFormsHost host object only takes System.Windows.Forms.Control objects as a child. To make your OCX a System.Windows.Forms.Control, you will need to create a dummy Windows Forms Control Library project as hoster project, and let Visual Studio create this .NET ActiveX wrapper from OCX by using the OCX, i.e., putting an OCX in the custom control in that project. Then you can find generated the .NET ActiveX wrapper dll.

Detailed steps:

  1. OCX project: This is the project featuring high performance, low level, omni power ….(add whatever words here). In another word, you can do whatever your WPF stuff couldn’t do here.
    • I create a new project from Visual C++ | MFC | MFC ActiveX Control. You can use any other choices, for example, another language. The type information is conveyed externally through the COM system only. There is no direct link between this project and the rest in your Visual Studio solution. You many still want to set dependency between the projects for the building order in the same solution. In my case, I use Class Wizard to add a few methods and properties to the _D<OCX> interface and implement them in the corresponding <OCX>Ctrl.cpp. Building this project will also registers the OCX to the Windows COM registry, and that’s enough.
  2. WFCL hoster project: The only purpose of this project is to get a .NET wrapper class for your OCX that is a subclass of System.Windows.Forms.Control.
    • Create a new project from Visual C# | Windows | Windows Forms Control Library.
    • As a good habit, rename UserControl1.cs to a better name (I use the project name, since this is the only important file in the project anyway)
    • Add a reference to the OCX in COM category (not to the OCX project, you cannot add to C# project a reference to C++ project). You should see something reference like <OCX>Lib in your Solution Explorer. If you check the file system, you should find Interop.<OCX>Lib.dll. This .NET assembly has the .NET representation of your OCX type information. Unfortunately, the types have nothing to do with System.Windows.Forms.Control yet.
    • Bring the design view of your WFCL control (empty right now), in the Toolbox, right click | Choose Items…, then choose COM Components and add the OCX to the Toolbox.
    • Drag the OCX in the Toolbox to your WFCL control. You should see Ax<OCX>Lib is added as a reference to your hoster project and you can find corresponding obj\AxInterop.<OCX>Lib.dll.  That’s the thing we really want. If you check the reference, you should find Ax<OCX>Lib.Ax<OCX>, which is derived from System.Windows.Forms.Control through AxHost.
    • As a good habit, set the OCX control’s Dock property to Fill in your WFCL control, so in the future you can always see it there.
    • Build this project, and you will get bin\Debug\AxInterop.<OCX>Lib.dll, or bin\Release\AxInterop.<OCX>Lib.dll copied from obj\.
  3. WPF project: This is the end user of the OCX. The OCX is added to a content container in your WPF window dynamically by C# code.
    • Add reference to WindowsFormsIntegration (sort by Component Name in .NET category to find it). Its WindowsFormsHost is the glue between WPF and Windows Forms.
    • Add reference to System.Windows.Forms (sort by Component Name in .NET category to find it). Its Control class is the base class of your OCX wrapper class.
    • Add reference to AxInterop.<OCX>Lib.dll, by Browsing to WFCL\bin\Debug (or WFCL\bin\Release, they are the same since they only have the type information). This has the .NET wrapper class of your OCX.
    • Design your WPF window to have a content container that would be occupied by the OCX, for example a Grid.
    • In your WPF window’s Load event handler, add the code to put the OCX to the container. Since I have different OCXs, I write a generic AddAxObject() method for all kinds of OCXs, see code below.
  1     public partial class MainWindow : Window
  2     {
  3         // add an ActiveX object to the container with a WindowsFormsHost hoster
  4         private Ax addAxObject<Ax>(Grid container) 
  5                 where Ax : System.Windows.Forms.Control, new()
  6         {
  7             //Create the ActiveX/OCX control
  8             Ax ax = new Ax();
  9             //Create the interop host control.
 10             System.Windows.Forms.Integration.WindowsFormsHost hoster 
 11                     = new System.Windows.Forms.Integration.WindowsFormsHost();
 12             //Assign the ActiveX control as the host control’s child.
 13             hoster.Child = (System.Windows.Forms.Control)ax;
 14             //Add the interop host control to Grid control’s collection of child controls
 15             container.Children.Add(hoster);
 16             return ax;
 17         }
 18
 19         public AxCameraCaptureLib.AxCameraCapture m_axCamera;
 20         public AxModel3DLib.AxModel3D m_axModel3d;
 21
 22         private void Window_Loaded(object sender, RoutedEventArgs e)
 23         {
 24             m_axCamera = addAxObject<AxCameraCaptureLib.AxCameraCapture>(gridLiveCamera);
 25             m_axModel3d = addAxObject<AxModel3DLib.AxModel3D>(gridModel3D);
 26             m_axCamera.Start();              // now you can call OCX’s methods.
 27             m_axModel3d.LoadFile("test.stl");
 28             …
 29         }
 30     }

 

Changing OCX Implementation 

If you change the implementation of the OCX without changing its IDL, you may see error messages when the linker tries to delete and write to the ocx DLL. This is because the OCX is loaded by Visual Studio if some design window housing the OCX is opened. Simply closing these design windows does not unload the OCX. To be able to build the OCX, you will have to close all the involved design views in Visual Studio (otherwise when the solution restarted these views will be opened automatically), then close Visual Studio and restart it.

Changing OCX Interface

If you change your OCX’s interface (i.e., the IDL file, no matter through Class Wizard or not), in addition to do the work above in changing OCX implementation, you will need to manually update the Ax<OCX>Lib and <OCX>Lib references. This is because an OCX’s interface is considered to be relatively stable. Visual Studio does not monitor the COM registry entries. If you change the IDL, the .NET wrapper must reflect the change. What you need to do is:

  1. OCX project:
    • Rebuild and/to register your new OCX interface to COM system.
  2. WFCL hoster project:
    • Delete the OCX from your custom control in design view
    • Delete the Ax<OCX>Lib and <OCX>Lib references from the project
    • Delete the OCX from Toolbox
    • Add OCX as COM reference to the project
    • Add OCX as COM Component to Toolbox
    • Drag the OCX to the custom control in design view
  3. WPF project: No change is required (since the referenced Ax<OCX>Lib’s Interop.Ax<OCX>Lib.dll filename does not change).

Due to the work involved in WFCL hoster project, you should try to design the OCX interface as stable as possible. You will not like to do the steps again and again, in the middle you can often see Visual Studio show nasty error messages when you try to see the design views but it could not load the OCX.

 

About these ads

9 Responses to “ActiveX control (OCX) in a WPF window”

  1. I don’t have to do all the readding to generate new Ax.dll’s, just create a custom build and call AxImp.exe each time the ocx-file changes date.

  2. I am trying to do all of the above steps, but when I run the project in VS2008 it prompt following exception in WFCL Project in Control Designer code file. The runtime exception is “COMException Class not registered (Exception from HRESULT: 0×80040154 (REGDB_E_CLASSNOTREG))”

    The code goes here

    //
    // UserControl1
    //
    this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.Controls.Add(this.axMyControlTest1);
    this.Name = “UserControl1″;
    this.Size = new System.Drawing.Size(516, 346);
    ((System.ComponentModel.ISupportInitialize)(this.axMyControlTest1)).EndInit();
    this.ResumeLayout(false);

    I am using Windows7 64 bit edition.

    Please help.

    • It probably means that the OCX produced in the OCX project is not registered to the COM system correctly. You can use OleView to check if your OCX is registered. In Windows 7 64-bit, an application can run in native x64 or WOW64 (32-bit compatibility mode). You also need to make sure both your OCX and WFCL pull the same part of registry for COM registration. Hope this helps.

  3. Thank you so much! Worked like a charm!

  4. It is truly a nice and useful piece of info. I am satisfied that
    you just shared this helpful info with us.
    Please stay us up to date like this. Thank you for sharing.

  5. Hai, I got struck in steps. After finished WFCL steps,the ocx is added in the container part of the tool box. Can you please direct me? Thanx.

Trackbacks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 35 other followers

%d bloggers like this: