Export (0) Print
Expand All
21 out of 35 rated this helpful - Rate this topic

Hosting ActiveX Controls in the .NET Compact Framework 2.0

.NET Compact Framework 1.0

Alex Feinman

February 2006

Applies to:
   Microsoft .NET Compact Framework version 2.0

Summary: Learn how to host an ActiveX control in an application that uses the .NET Compact Framework. A download code sample is provided. (19 printed pages)

Download Host_ActiveX_Controls.msi from the Microsoft Download Center.


Control Hosting
Things to Improve
About the Author


The Microsoft .NET Compact Framework version 2.0 introduces Component Object Model (COM) support—a feature that is conspicuously missing from version 1.0. The obvious need for it is probably best illustrated by the fact that Odyssey Software has released a CFCOM product (a tool used to overcome this limitation and allow calling COM interfaces and hosting ActiveX controls) almost before the .NET Compact Framework came out of beta.

Nevertheless having COM Interop support in the .NET Compact Framework is a great step forward. A growing number of operating system features in Windows CE is supported through COM interfaces. A short list of such features includes UPnP, Bluetooth and SDP, Imaging, Message Queuing (also known as MSMQ), MAPI, Pocket Outlook Object Model (POOM), and many other important operating system areas. Before the .NET Compact Framework version 2.0, the developer had to create a shim by using C++. Now it is possible to use most of the COM interfaces directly from the managed code, thus removing the need to write and distribute processor-specific unmanaged modules.

COM support in the .NET Compact Framework 2.0 is still somewhat limited compared to its desktop computer counterpart. For example, there is no support for external activation. You cannot write a standalone COM component in managed code; if it were possible, a COM component could have been instantiated inside a regular, unmanaged Win32 process. Because managed code requires the Common Language Runtime (CLR) to execute, such setup needs the Win32 process to host CLR. Because CLR hosting is not one of the features of the .NET Compact Framework 2.0, the whole external activation model is not available.

Another important feature that is missing from the .NET Compact Framework 2.0 is control hosting. The feature was not included in the final release of the framework due to the time and resource constrains. However, everything that is required to host a control is in the framework. Managed-to-COM calls (runtime callable wrapper [RCW]); COM-to-managed (COM callable wrapper [CCW]); connection points; callbacks; and access to control handles (finally!) —it's all there. This article will show you the steps that you need to take to host a control.

Control Hosting

The material in this article requires you to be familiar with Win32 and COM. While it is not absolutely necessary to read this material so that you can use the control-hosting framework described, it is recommended that you read it—if only to help you appreciate the underlying complexity.

Controls and Containers

ActiveX containers are one of the main concepts of control hosting. An ActiveX container is an entity used to supply the environment for an ActiveX control to run. The basic information about ActiveX controls and containers is becoming increasingly difficult to find. There does not seem to be a single example on MSDN or in a software development kit (SDK) where how to write a container or a control without using a tool, such as Active Template Library (ATL) or Microsoft Foundation Classes (MFC) Library, is demonstrated. Granted, ATL is great help—but not when you need to implement a basic container from the scratch. The literature list at the end of this article seems to be all that is available on MSDN about the subject.

ActiveX Container Interfaces

From a technical standpoint, an ActiveX container is a COM object that supports several mandatory and a few optional interfaces. Table 1 shows a list of interfaces that containers use. (This information is from the Introduction to ActiveX Control Containers.)

Table 1. Interfaces (and their support) that containers use

Interface Support Comments
IAdviseSink Optional This interface is required only when the container requires notifications such as data change notifications from controls with the IDataObject interface, view change notifications from controls that are not active and have the IViewObject2 interface, and other notifications from controls that act as standard embedded objects.
IClassFactory2 Optional This interface is not required, but supporting it is recommended.
IDispatch for ambient properties Required N/A
IErrorInfo Required This interface is mandatory if a container supports dual interfaces.
IOleClientSite Required N/A
IOleContainer Required This interface is implemented on the document or form object that holds the container sites. Controls use the IOleContainer interface to navigate to other controls in the same document or form.
IOleControlSite Required N/A
IOleInPlaceFrame Required N/A
IOleInPlaceSite Required N/A
IPropertyNotifySink Optional This interface is only needed for containers that have their own property-editing user interface (UI).
IOleWindow Required This interface is implemented in the container, is queried from the hosted control, and is used by the container.
IOleInPlaceUIWindow Required N/A
ISimpleFrameSite Optional This interface, in addition to support for

nested simple frames, is optional.

Because you are going to write a container to use on a mobile device and with limited support of the OLE Controls 96 (OC 96) specification, you are going to simplify the task by ignoring most of the optional and some of the mandatory features.

Self-Imposed Limitations

The container you are going to write will be limited to a single control at one time. If you need more than one ActiveX control hosted on the form, you will have to create two separate containers. This decision lets you immediately leave out the ISimpleFrameSite interface.

You can also leave out other interfaces. For example, you are not going to offer property editing; therefore, leave out the IPropertyNotifySink interface. The IClassFactory2 and IDispatch interfaces are obviously not necessary because CCW (part of the COM support in the .NET Compact Framework) takes care of class instantiation and IDispatch implementation. Currently, you don't want any notifications other than what comes through event interfaces, so you can leave out the IAdviseSink interface. And finally, supporting documents or multiple controls is not required, so you will implement the IOleContainer interface as a stub. You are also not going to implement the IErrorInfo interface on the container because you will receive the error information through the exception mechanism.

Tables 2 through 7 describe the methods that exist in the interfaces your container will support.

Table 2. The methods in the IOleClientSite interface

IOleClientSite methods Description Note
SaveObject Saves embedded object Ignore this method because the container does not support control persistence.
GetMoniker Requests an object's moniker This method is not supported. The implementation is self-contained and will not provide any COM interfaces to the outside clients.
GetContainer Requests pointer to object's container This method is supported.
ShowObject Asks container to display object This method is supported.
OnShowWindow Notifies container when object becomes visible or invisible Ignore this method.
RequestNewObjectLayout Asks container to resize display site This method is not supported.

Table 3. The methods in the IOleWindow interface

IOleWindow methods Description Comments
GetWindow Gets a window handle This method is supported.
ContextSensitiveHelp Controls enabling of context-sensitive help Ignore this method. The container does not implement help support.

Table 4. The methods in the IOleInPlaceSite interface

IOleInPlaceSite methods Description Comments
CanInPlaceActivate Determines if the container can activate the object in place This method returns true.
OnInPlaceActivate Notifies the container that one of its objects is being activated in place Use this method to activate the object.
OnUIActivate Notifies the container that the object is about to be activated in place and that the main menu will be replaced by a composite menu This method is supported.
GetWindowContext Enables the in-place object to retrieve the window interfaces that form the window object hierarchy and the position in the parent window where the object's in-place activation window should be placed This method is supported.
Scroll Specifies the number of pixels by which the container is to scroll the object This method is unsupported.
OnUIDeactivate Notifies the container to reinstall its user interface and take focus This method is supported.
OnInPlaceDeactivate Notifies the container that the object is no longer active in place This method is supported.
DiscardUndoState Instructs the container to discard its undo state Ignore this method. The container will not track the undo state.
DeactivateAndUndo Deactivates the object and reverts it to the undo state Ignore this method. The container will not track undo state
OnPosRectChange Informs the container that the object's extents have changed This method is supported.

Table 5. The methods in the IOleControlSite interface

IOleControlSite methods Description Comment
OnControlInfoChanged Informs the container that the control's CONTROLINFO structure has changed and that the container should call the control's IOleControl::GetControlInfo method for an update Ignore this method because the container doesn't have keyboard support. If it did, you would have grabbed the keyboard accelerator table from the CONTROLINFO structure.
LockInPlaceActive Indicates whether or not this control should remain in-place active, regardless of possible deactivation events This method is not supported.
GetExtendedControl Requests an IDispatch pointer to the extended control that the container uses to wrap the real control Ignore this method. It will always return NULL because you will not use dispatch interfaces to communicate with the container.
TransformCoords Converts between a POINTL structure expressed in HIMETRIC units (as is standard in Object Linking and Embedding [OLE]) and a POINTF structure expressed in units the container specifies Ignore this method. The container only supports MM_TEXT.
TranslateAccelerator Instructs the container to process a specified keystroke This method is supported.
OnFocus Indicates whether the embedded control in this control site has gained or lost the focus Ignore this method.
ShowPropertyFrame Instructs a container to display a property sheet for the control embedded in this site This method is not supported.

Table 6. The methods in the IOleInPlaceUIWindow interface

IOleInPlaceUIWindow methods Description Comment
GetBorder Returns a RECT structure in which the object can put toolbars and similar controls while active in place This method is not supported. The implementation has no extra UI elements—just the control itself.
RequestBorderSpace Determines if there is available space for tools to be installed around the object's window frame while the object is active in place This method is not supported.
SetBorderSpace Allocates space for the requested border This method is not supported.
SetActiveObject Provides a direct channel of communication between the object and each of the frame and document windows This method is supported.

Table 7. The methods in the IOleInPlaceFrame interface

IOleInPlaceFrame methods Description Comment
InsertMenus Allows container to insert menus This method is not supported.
SetMenu Adds a composite menu to the window frame This method is not supported.
RemoveMenus Removes a container's menu elements This method is not supported.
SetStatusText Sets and displays status text about the in-place object in the container's frame window status line This method is not supported.
EnableModeless Enables or disables modeless dialog boxes This method is not supported.
TranslateAccelerator Translates keystrokes This method is not supported.

Interacting with Controls

Although the documentation states that it is possible to have a control that implements nothing but an IUnknown-derived interface, in reality there are some things that you can reasonably expect from a control. For example, because the primary goal in writing ActiveX controls is to make them usable from automation clients, such as Microsoft Internet Explorer and Microsoft Visual Basic (and their device counterparts: eMbedded Visual Basic and Internet Explorer Mobile), nearly every control on the market supports the IDispatch interface and dispatch event interfaces. If a control has some sort of UI, it also exposes IOleControl and IOleInPlaceObject (with the inherited IOleWindow interface).

Other controls also support the IPersistStream interface and other IPersistxxx interfaces. Those interfaces are used to stuff initial values of control properties and are mostly useful for designer support in Visual Basic. This article will not cover this topic because the designer support in Visual Studio works quite differently.


In the full framework, the control hosting functionality is implemented in the AxHost class. The AxHost class is very similar to the CAxHostWindow class—an ATL template class that defines a window that can host ActiveX controls. Similar to CAxHostWindow, AxHost in the full framework does much more than you need at this point. In this implementation, you are going to forego anything that deals with toolbars, property pages, frames, etc.

To make the implementation more manageable, you will introduce several functional parts that encapsulate certain logical subsets of the overall design. For example, the container support will be implemented in AxContainer class, as shown in Figure 1. The OleSite class will be responsible for site-related interfaces and interaction with the control's IObjectWithSite interface.

Click here for larger image

Figure 1. Control hosting framework architecture. Click the thumbnail for a larger image.

Declaring and Instantiating a Control

To host a control in the .NET Compact Framework, you need to complete two major tasks. It is necessary to create an import library with COM interface definitions and build a control class that is derived from the AxHost class.

Obtaining a COM Interface Definition

To host a control, it is necessary to implement control-specific interfaces (IDispatch or IUnknown) in the class derived from the AxHost class. For example, the Microsoft Windows Media Player control supports dispatch interfaces that allow getting and setting the properties and receiving events. It also supports IUnknown interfaces, such as IWMPCore and IWMPPlayer that can and should be mapped directly into .NET Compact Framework classes and interfaces. The AxHost class gives you access to the control's IUnknown interface by means of the GetOcx method. If you had a COM interface definition in your .NET Compact Framework code, you could simply cast the object type returned by the GetOcx method to the interface type, and the framework would have performed the QueryInterface method internally.

Creating the Interface Definition Manually

In some cases, you will have an .idl file that came with the control or the SDK. The definitions from such a file can be translated into the .NET Compact Framework manually. It is not a simple thing to do and mistakes are easy to make. You should consider this solution a last resort—when the other methods described in this article are not available.

For example, the IWMPCdrom interface is defined in the full Windows Media Player 10 as the following code example shows.

    helpstring("IWMPControls: Public interface."),
interface IWMPControls : IDispatch
        HRESULT isAvailable( [in] BSTR bstrItem, [out, retval] VARIANT_BOOL *pIsAvailable );
        HRESULT play();
        HRESULT stop();
        HRESULT pause();
        HRESULT fastForward();
        HRESULT fastReverse();
        HRESULT currentPosition( [out, retval] double * pdCurrentPosition );
        HRESULT currentPosition( [in] double dCurrentPosition );
        HRESULT currentPositionString( [out, retval] BSTR * pbstrCurrentPosition );
        HRESULT next();
        HRESULT previous();
        HRESULT currentItem( [out, retval] IWMPMedia **ppIWMPMedia);
        HRESULT currentItem( [in] IWMPMedia *pIWMPMedia );
        HRESULT currentMarker( [out, retval] long *plMarker);
        HRESULT currentMarker( [in] long lMarker);
        HRESULT playItem( [in] IWMPMedia *pIWMPMedia );

This same interface (IWMPCdrom) could be defined in the .NET Compact Framework code as shown in the following code example.

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IWMPControls
        IWMPMedia currentItem { get; set; }
        int currentMarker { get; set; }
        double currentPosition { get; set; }
        string currentPositionString { get; }

        void fastForward();
        void fastReverse();
        bool get_isAvailable(string bstrItem);
        void next();
        void pause();
        void play();
        void playItem(IWMPMedia pIWMPMedia);
        void previous();
        void stop();

As stated previously, this is not the method you want to follow unless you have to. An example of a situation when you have to define the interface explicitly is when automated import using the TLBIMP tool produces undesirable results or when the interface platform invoke definition does not work for your purposes for some other reason.

For instance, the release of the .NET Compact Framework 2.0 does not support marshalling of arrays of interface pointers back to the caller (that is, ISomeInterface[]). This type of parameter is rather common in IEnumxxx::Next method implementations that you can find in such COM interfaces as IEnumVariant, IEnumConnectionPoint, and others. Yet, if you were to look in the object browser, you would see that for the System.Runtime.Interop.ComTypes.IEnumConnectionPoints class, the Next method is defined like the following.

IEnumConnectionPoints.Next(int celt, IConnectionPoint[] rgelt, IntPtr pceltFetched)

This code, unfortunately, will not work for the reason mentioned previously (besides, having the last parameter defined as IntPtr is also quite inconvenient). Instead, you need to redefine the interface and make this method look like the following.

void Next (UInt32 cConnections, out IConnectionPoint rgpcn, out UInt32 lpcFetched )

While technically not an equivalent of the original definition, this code works nicely as long as cConnections is set to 1. Obviously, setting it to anything greater than one will cause a buffer overflow and crash your application.

Using this approach, you define your own version of the interface. For this example, it's called IMyEnumConnectionPoint. Notice that a call to the System.Runtime.InteropServices.ComTypes.IConnectionPointContainer.EnumConnectionPoints() method obtains the instance of the original IEnumConnectionPoint interface. How do you get the IMyEnumConnectionPoint interface instead? You simply cast the instance of IEnumConnectionPoint to the IMyEnumConnectionPoint interface. Because both are declared as ComImport, such typecast will not use CLR runtime type check, but they instead result in the call to QueryInterface, which succeeds because both interfaces have the same GUIDs.

Importing the Interface Definition Using TLBIMP.exe

To extract an interface definition automatically, you should use the TLBIMP tool. TLBIMP is shipped as a part of .NET Framework SDK. It is also invoked automatically when a COM library (.dll, .ocx, .tlb) reference is added to a .NET Compact Framework project. When you are working with device projects, the ActiveX control usually comes packaged in a .cab file. Of course, such a control would be compiled for the ARM CPU and could not run on the desktop computer; fortunately, it does not have to. The type library is platform-independent even when contained in a .dll file —almost (it is different on 64-bit platforms). So the reference to the ARM ActiveX DLL can be added to the project in the same way as if it were a desktop computer control. Of course, it will not be possible to instantiate it or do anything else with it, but that is a different story.

As an example, take a look at the Shockwave Flash player control by Macromedia (Adobe). You can download this player for free. The installer contains a .cab file that is called ActiveX.cab, which is copied to the C:\Program Files\Microsoft ActiveSync\Macromedia\Flash Player 6 directory by default. If you double-click the .cab file, you will see two files. The one called 000flash.001 is the ActiveX itself. Extract this file, rename it as Flash.dll, and then add it as a reference to your project. It will show in the list as ShockwaveFlashObjects, as shown in Figure 2.

Figure 2. Shockwave Flash import library in the Visual Studio project references

If you look at the class browser, you will see that you now have a new namespace and class and interface definitions, as shown in Figure 3.

Figure 3. Shockwave Flash import library in the Visual Studio class browser

Working Around Access to the CAB or Type Library

Sometimes you don't have the luxury of being able to access the control CAB. For example, the popular request to host a Windows Media Player control hits its first obstacle when the developer realizes that the Windows Media Player version 10 Mobile does not have a redistributable packaging. It is only available as a part of device ROM. Fortunately in case of Windows Media Player, the desktop computer's and the device's type libraries are identical, but the difference is that many methods on the Mobile Windows Media Player returns E_NOTIMPL. Fortunately, the fact that the device version does not provide complete implementations for some methods has no impact on the type library; therefore, you can import the type library from the desktop player (which is located in C:\Windows\System32\wmpcore.dll) and use it with the device Media Player. You still need to carefully read the Windows Media Player SDK documentation to see which methods and properties you can actually use.

When you have a control for which the type library is unavailable, check if a desktop computer version of the same control with identical or similar interface exists.

Unfortunately, there are occasions when the previously mentioned approach does not work. For example, if you try to use the type library for the desktop computer's Web browser control (SHDOCVW.DLL) to host the Pocket PC Web browser control, you will be disappointed; they are not the same. Luckily, there is a way around this situation. If you check the Windows Mobile SDK, You will see a file called webvw.idl that contains all of the interface definitions for the Web browser control—this is the good news. The bad news is that its size is 50 KB and that it contains about 1500 lines of code. This control is not feasible to manually convert to C#. But there is more good news—there is a painless method of creating a type library from it.

To create a type library from the webvw.idl file

  1. Open a Visual Studio command prompt. (If you are running the 64-bit platform, ensure it is a 32-bit command prompt.)
  2. Browse to the SDK Include directory (by default, it is located in C:\Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\PocketPC2003\Include).
  3. Run the following command:
    midl /D UNDER_CE webwv.idl

    The MIDL compiler compiles the .idl file and generates another file, webvw.tlb. This file can be, you guessed it, imported into the Visual Studio project.

This clever approach has a limitation. The .idl file must have a library section with a coclass defined in it. Sometimes, you do not have that. In this case, you need to make one up as shown in the following code example.

import "OAIdl.Idl";
import "objidl.idl";
// Import any other .idl files you need
    helpstring("Test library"),
library TestLib
        helpstring("Test class")
    coclass Test
        [default] interface IEnumConnectionPoints;
          interface IConnectionPoint;
          // List any other interfaces that you are interested in

Build a file as shown in the code example. Add import directives and interface references as needed. If an interface is included and its method references another interface, that interface will be included automatically. In the previous example, adding IEnumConnectionPoints causes the IConnectionPoint, IConnectionPointContainer, and a few other interfaces to be included.

Generating a Complete Control Class Definition by Using AxImp.exe

It would be nice to achieve the ultimate convenience—handle ActiveX controls the way Visual Studio does in the desktop computer form projects. All you would need to do is add a control to the toolbar by browsing for it or by selecting it from the list of registered components. While the Visual Studio designer actively resists any attempt to add an ActiveX control to the device project, you can get around this limitation. Similar to the previously described method of using the TLBIMP tool as a command-line equivalent for adding the reference to a type library, you can use the AXIMP tool (which is also a part of the .NET Framework SDK) that creates the same set of libraries as those that the Visual Studio IDE creates, when you place an ActiveX control on a form. Just as the TLBIMP tool has a richer set of features than the integrated development environment (IDE) (in terms of importing a type library), the AXIMP tool can do more. The biggest importance to your project is its ability to produce the Interop code in its source format. If you run the following command line, AxImp.exe C:\WINDOWS\system32\wmp.dll /source, it produces three files:

  • AxWMPLib.cs
  • AxWMPLib.dll
  • WMPLib.dll

There is also a .pdb file, but it's not important right now. AxWmpLib.cs is the source of the Interop library, AxWMPLib.dll. Looking inside it, you can see that it defines a AxWindowsMediaPlayer class derived from System.Windows.Forms.AxHost. The 1663 lines of code give you an idea about how unpleasant the task of manually writing this code would be.

Why do you want the source? If you look at the AxWMPLib.dll in the ILDAsm tool, you can see that it is compiled against the desktop computer versions of MSCORLIB and System.Windows.Forms. Obviously, you cannot use the AXIMP generated version of AxWMPLib.dll. But, you can compile the source file against the .NET Compact Framework libraries and get an assembly that you can use on the device.

When attempting to compile the source code of the Interop library, you will notice that it throws quite a few errors because of the device versions of MSCORLIB and System.Windows.Forms. Luckily, you can ignore all of these missing references and replace them with stubs. You can find these stubs in the AxImpSupport.cs module of the download code sample to this article.

The great things about using the AXIMP tool to generate the wrapper are that it is quick, simple, and creates a control class with full event and designer support. Of course, the other two methods can produce the code with full designer support too, but they will require significantly more work.

Some developers prefer compiling such classes into a separate library. Because it does not require any additional code to be written, even someone not familiar with C# can build this control library. After the control library is built, you can easily use it in a Visual Basic .NET project, as demonstrated in the download code sample for this article.

Designer Support

Because the AxHost class is derived from the Control class, the Visual Studio designer immediately picks up the AxHost class and shows it as available to insert into your project's forms. Same is true for the classes that are derived from the AxHost class. All of the controls you host will be automatically available in the designer, as shown in Figure 4.

Figure 4. Wrapped ActiveX controls that appear in the Visual Studio Toolbox

Moreover, because generated ActiveX control classes expose dispatch properties and event interfaces by means of .NET Compact Framework properties and events, the designer makes those available as well. You can further customize the control appearance in the designer by means of a control manifest, but it is beyond the scope of this article.

Figure 5 shows the designer's Properties pane for a hosted ShockWave Flash control.

Figure 5. Hosted ActiveX control events

Figure 6. Hosted ActiveX control properties

One unexpected consequence of the AxHost class being derived from the Control class is that the AxHost class also shows in designer's Toolbox. Because it is useless to developers as a standalone control, you need to suppress it by adding a control manifest file to the project with the following content.

<?xml version="1.0" encoding="utf-16"?>
<Classes xmlns="http://schemas.microsoft.com/VisualStudio/2004/03/SmartDevices/XMTA.xsd">
    <Class Name="System.Windows.Forms.AxHost">

Things to Improve

There are always things that can be improved. We, at OpenNETCF.org, are planning to add or enhance some of the AxHost class features in the future.

The following is a list of missing features:

  • Keyboard support
    This code has not been tested with the devices that use keyboards. I'm fairly sure that there are some issues with accelerators, focus and key press handling.
  • Windowless activation
    At the moment, this type of activation is not supported. Fortunately, most controls that support windowless activation also support windowed activation.
  • Button-like and label-like controls
    The current version of the AxHost class most likely does not correctly handle the OLEMISC flags. With controls like Shockwave player and WMP, it is not really necessary, but I expect that some controls would really require it.
  • Activation and deactivation
    Most of the controls developers are interested in have a simple lifecycle: create, activate, display, deactivate, destroy. Some controls might have more complex requirements, such as multiple activations or deactivations.

If you found this code useful, feel free to send comments and problem reports to alexf@opennetcf.org.


You've read about the process of creating a managed host for ActiveX controls by using the .NET Compact Framework 2.0. The article covered the general facts about control hosting, walks you through problems and solutions on the way to a practically usable ActiveX host object, and shows you how to use it to host controls in your own applications.

The managed control-hosting framework described here is offered by OpenNETCF.org.


This article uses information from the following references:

About the Author

Alex Feinman is a Software Engineer at Corrigo Incorporated, a Northern California technology company and a Microsoft Certified Solution Provider. Alex is also a member of the OpenNETCF.org Advisory Board.

OpenNETCF.org is a non-profit organization that the members of the OpenNETCF Advisory Board started as an independent source for the .NET Compact Framework development information working under the spirit of the open-source movement.

Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
© 2014 Microsoft. All rights reserved.