Create an ActiveX control using ATL that you can use from Fox, Excel, VB6, VB.Net

Creating an ActiveX control is a good exercise in understanding how one works. It also helps to have full control over its source code for learning and testing purposes. A customer asked about migrating legacy ActiveX controls over to .Net.  Many controls can be used without any changes in .Net. (See Using ActiveX Controls with Windows Forms in Visual Studio .NET)


Here are some steps to create an ActiveX control using C++ ATL (Active Template Library) and a few wizards. We’ll add a method and an event, and if you’re really ambitious, we’ll add some GDIPlus calls to draw text and a picture.


Start Visual Studio 2005 (2003 works too, but the steps may be a little different). Now create a new project: choose File->New Project->C++->ATL->ATL Project. Call it “MyCtrl”


In the ATL Project Wizard’s Application Settings, choose “Attributed” and Server Type: DLL.  Then choose Finish.


Now we’ll add a control to the project: Choose Project->Add Class->ATL->ATL Control to start the ATL Control Wizard.

Enter the short name "TestCtrl".  Note that the ProgId is "MyCtrl.TestCtrl". In Options, choose Connection Points for event support, then Finish the wizard.


Now you can host the control in a client, even though it has no functionality yet. Hit F5 to go, allow it to build. When it asks for an Executable to run for debugging, point it to a host like “c:\program files\....\VFP9.EXE”, VB6.exe, or Excel.exe

Note that the output is MyCtrl.DLL. The extension doesn’t matter: if you want to use the traditional OCX extension, change Project->Properties->Configuration->Linker->General->Output file.


To use the control in VB6, right click on the toolbox and select Components->Controls. Add "MyCtrl 1.0 Type Library"


To use the control in Fox, create a new form, put on an ActiveX Control, then choose "CTestCtrl Object"

In VB.Net 2005, right click on the ToolBox Common Controls, choose "Add Toolbox Items", COM, "CTestCtrl Object"


Depending on which client you choose, you can have VS automatically start the client with F5 and load the control on a form by loading a prior project or executing some code. Choose Project->Properties, Configuration Properties->Debugging. Set the Command Arguments to point to some code to execute or a project.

For Fox, I have command arguments set to "-t t" which means to suppress the Log screen (run VFP9 /? to see other options) and run a program called "t" which contains these lines



MODIFY FORM t nowait




You can also view the control on a web page. Right click on TestCtrl.htm in Solution explorer and choose to view in a browser. Note all the security warnings!


Now that you see how easy it is to make a control that does nothing, let’s add some functionality.


Let’s add a method: In Class View (View->Class View), expand MyCtrl and right click on ITestCtrl, choose Add->New Method to start the "Add Method Wizard"

We'll add a method that takes a string parameter and returns an integer:  a signature like this:

            Function Foobar(bstrString as string) as Integer

In the wizard, set the Method Name to "Foobar"

Now we'll add the first parameter: it's an IN parameter, so check "In". Set the Parameter type to "BSTR" and the name to "bstrString", then choose "Add" to add that parameter.


All COM method calls return HRESULTs, with S_OK (zero) being success. Thus, to get a return value for a method call, we need to add another parameter that's passed by reference, and it's marked by "retval" in the dialog. IOW, if the method has N parameters, the COM method signature will have N+1, with the last parameter being the return value.


However, you'll see the "retval" checkbox disabled until you choose a parameter type that's by reference, such as "LONG *". The "*" indicates it's a pointer, so it's by reference. So Choose "LONG *" and then click on the newly enabled "retval" checkbox. Give it a name "nRetval", then choose "Add".


Now you have 2 parameters for the method.


Similarly, right click on ITestCtrl, choose Add->New Property called "MyString" (this actually adds 2 methods: a get and a set)


In TestCtrl.CPP, put some code in the Method Foobar. To open TestCtrl.CPP, click on CTestCtrl in Class View, and in the bottom pane of Class View, dbl-click on Foobar.

      ::MessageBox(0,bstrString,L"From Foobar",0);


      return S_OK;


Set a breakpoint on the MessageBox line and on the CTestCtrl::get_MyString and CTestCtrl::set_MyString methods.

Hit F5 to go, or test your control in a client. Call the Foobar method, play with the properties of the control. At breakpoints, examine the call stacks and expand the “this” pointer in the CTestCtrl object to see all the COM Interfaces CTestCtrl implements (I copied them from the debug window, pasted into a new VS C++ file, then used the VS Editor Column Select with Ctrl-Alt drag to select columns):



ATL::IPersistStreamInitImpl<CTestCtrl>    {...} ATL::IPersistStreamInit

ATL::IOleControlImpl<CTestCtrl>     {...} ATL::IOleControlImpl<CTestCtrl>

ATL::IOleObjectImpl<CTestCtrl>      {...} ATL::IOleObjectImpl<CTestCtrl>

ATL::IOleInPlaceActiveObjectImpl<CTestCtrl>     {...} ATL::IOleInPlaceAct

ATL::IViewObjectExImpl<CTestCtrl>   {...} ATL::IViewObjectExImpl<CTes

ATL::IOleInPlaceObjectWindowlessImpl<CTestCtrl> {...} ATL::IOleInPlac

ATL::IPersistStorageImpl<CTestCtrl> {...} ATL::IPersistStorageImpl<CT

ATL::ISpecifyPropertyPagesImpl<CTestCtrl> {...} ATL::ISpecifyProper

ATL::IQuickActivateImpl<CTestCtrl>  {...} ATL::IQuickActivateImpl<CTe

ATL::IDataObjectImpl<CTestCtrl>     {...} ATL::IDataObjectImpl<CTestCtrl>


ATL::IConnectionPointContainerImpl<CTestCtrl>   {...} ATL::IConnectio

ATL::IPropertyNotifySinkCP<CTestCtrl,ATL::CComDynamicUnkArray>    {...}


ATL::CComObjectRootEx<ATL::CComSingleThreadModel>     {...} ATL::CComOb

ISupportErrorInfo {...} ISupportErrorInfo






Also, you'll see that without any implementation on the property get and set, setting and getting the property from the client does nothing.


Let's add some code to respond to mouse clicks on the control.  First we need to write a click handler. From Class View, click on CTestCtrl. Near the top of the Properties window (not the Class View window), click on the Messages icon. Choose WM_LBUTTONDOWN, add code. This creates an OnLButtonDown method for you to fill out. I want the code to raise an event in the client. That means we need to modify the ITestCtrlEvents interface, add the definition of a method that the client will optionally implement.


From Class View, click on the ITestCtrlEvents interface (which was added because we chose Connection Point support above) and add a method as we did for the ITestCtrl interface.

Let's call it MyEvent with a single BSTR parameter bstrEventString and no return value. This will be implemented by the client (Fox or Excel) and will be called from our control.


Now add some code to fire the event in the client in your OnLButtonDown handler in TestCtrl.cpp:


LRESULT CTestCtrl::OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)


      CComBSTR bstr=L"it was clicked";

      MyEvent(bstr); // Fire the event: call the client code, if it exists

      return 0;



Now hit F5 and click on the control to see the event get fired. In the Fox client, add code to the Olecontrol1.MyEvent snippet.

*** ActiveX Control Event ***

MESSAGEBOX(bstreventstring+" From Fox event code")




For more fun, let’s override the OnDraw method and add some GDIPlus calls.

Add these 2 lines after the other #includes as the top of testctrl.h:


#include "gdiplus.h"

using namespace Gdiplus;


Then rename the OnDraw method (in the same file: TestCtrl.h) to be OnDrawOld, and add this code. Be sure to point to a jpg file on your machine and note that backslash is an escape character and needs to be doubled.

(For production code, we’d call GdiplusShutdown and cache the Image.)

Notice how the ActiveX control determines the difference between design time and run time.




      BOOL fRunMode=0;


      static Gdiplus::GdiplusStartupInput gdiplusStartupInput;

      static ULONG_PTR gdiplusToken=0;

      if (gdiplusToken == 0) {

            Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput,NULL);// note: call GdiplusShutdown!!!


      Gdiplus::Graphics MyGraphics(di.hdcDraw);

      Gdiplus::Image MyImage(L"d:\\kids.jpg",0);// some jpg: backslash needs to be doubled. Cache this!

      RectF rect(



            (REAL)(di.prcBounds->right - di.prcBounds->left),

            (REAL)(di.prcBounds->bottom - di.prcBounds->top));


      if (!fRunMode)    // if design mode, draw some text


            Gdiplus::Font MyFont(L"Arial",16);

            Gdiplus::StringFormat sf;

            PointF pf(rect.X,rect.Y);

            SolidBrush MyBrush(Color(128,0,128,255));

            MyGraphics.DrawString(L"Design Time!",12,&MyFont,pf,&MyBrush);


      return S_OK;



Now we’ll have to link with the GDIPlus library, so go to Project->Properties, Configuration Properties->Linker->input->Additional Dependencies and add Gdiplus.lib.


Hit F5 and have fun!


Below is sample Fox client code that doesn’t use the Fox form designer, so it will not be in design mode:






          ADD OBJECT OC as olecontrol WITH ;





          PROCEDURE oc.MyEvent(bstreventstring as String)

                   MESSAGEBOX(bstreventstring+" From Fox event code")


                   ?this.foobar("Call the Foobar method")





When you try it in a VB.Net application, try this code:

    Private Sub AxCTestCtrl1_MyEvent(ByVal sender As System.Object, ByVal e As AxMyCtrl._ITestCtrlEvents_MyEventEvent) Handles AxCTestCtrl1.MyEvent


    End Sub



See also ATL Tutorial

You can also create a .Net Usercontrol as an ActiveX control: see:  Create a .Net UserControl that calls a web service that acts as an ActiveX control to use in Excel, VB6, Foxpro




Comments (21)
  1. Deval Bhamare says:


    This is a helpful, fully explained and easy to understand document for a rookie like me in this filed. Thank you Calvin for your help. With the help of this I am felling like Activex Control with ATL is a very simple task, wich was almost next to impossible to me a few hours ago. I wonder why other documents are not as simple and down to earth as this one.

    Thanks a lot again.



  2. jmb says:

    great tutorial, helped me a lot!

    thank you!!


  3. Sunny says:

    Excellent tutorial, saved my day with pouring over MS(IT) document

  4. RGP says:

    Hi Calvin,

     I am new to ATL/COM .

     This tutorial helped me to understand how easy to build a control.

    Can you tell me how I can create an Image Viewer ( For Windows and Web based applications) using ATL/COM

    Thanks and Regards


  5. RGP says:

    Hi Calvin,

     One more query ;

    Can you tell how I can test the control using Active x Control Test container.( I couldnt see the control while opening thru Edit-> Insert NewControl).

    Thanks and Regards


  6. Rory Butler says:

    Hi Calvin

    I’m trying to make my pwn Frame activex control – to act as a container for buttons etc. How do I make my control a container in its own right?



  7. Rory Butler says:

    Well, darn… is it so simple as making the property ‘ControlContainer’ true???


  8. Steve says:

    I’m getting stuck on the event handling part  (I’m working in Excel).  The OnLButtonDown method gets called when the control is clicked, as expected; however, I can’t figure out how/where to implement the MyEvent method in Excel.  When I choose "view code" in design mode, it only shows GotFocus and LostFocus methods in the VB editor window.  I tried to write code like this:

    Private Sub CTestCtrl1_MyEvent(theString)


    End Sub

    But this does nothing.  Any ideas?  I’m sure this is a trivial goof on my part.

  9. saikatch says:

    Hi Calvin,

    I have a requirement. I need to use a .Net Winform user control from a ATL. In turns I want to host user control as an ActiveX control. How can I achieve this. Kindly help me with a sample code.

    Thanks you very much.


  10. Calvin_Hsia says:

    HI Saikat,

    These 3 links are samples that will help you

    Use WPF and inline XAML in your Fox, Excel or VB6 applications

    A Visual Basic COM object is simple to create, call and debug from Excel

    Create a .Net UserControl that calls a web service that acts as an ActiveX control to use in Excel, VB6, Foxpro

  11. saikatch says:

    Hi Calvin,

    Thanks for your reply. See, I need to create the ActiveX control using ATL (VC++) only. In that control I need to host my .NET user control. So, when the client application (written in most likely in VC++) will host the ATL activeX control, the user should be able to see and access the .NET control. I am very new to VC++. Kindly help me in this.

    Thanks in advance.


  12. Tarno Nona says:

    Hallo Calvin,

    I’m new to ATL. I try your sample using VS 2008, it’s built without error and work in test container. But when I try it in VFP9 SP2 it’s able to be put on form but the form cannot be saved with the following error:

    OLE error code 0x80004005: Unspecified Error. Please help me


    Tarno Nona

  13. Tarno Nona says:

    Hi Calvin,

    I read your article on, and when I try using your suggested code:


    ox.addobject("oc","olecontrol","the control’s progid")  


    it’s working. Oh btw, my os is XP SP2.


    Tarno Nona

  14. How fast is interop code? If you’re in one kind of code and your calling another, what is the cost of

  15. ghazanfar says:

    Plz, tell me a way.

    I have made a control in ATL(VC++). I want to know in my control whether any dll entered by user is registered or not.

    thanks in advance

  16. Angad says:

    Hi Calvin,

    I want to know about the Task Sequencer in VC++ using ATL and COM.

    Will you please reply me ??

  17. Jack says:

    I am trying to use this example with VB.NET and I keep getting the error: "Foobar is not a member of AxMyCtrl.AxCTestCtrl". What am I doing wrong? I added the Foobar method in VC++ like it says, but there is no information on how to call it from VB. Please help!

Comments are closed.

Skip to main content