ActiveX Control Tutorial

This article is somewhat like an extension to POLYGON tutorial provided in Microsoft’s online documentation. So I would not be explaining the basic steps involved to create the project and adding control to the project. I would only explain the steps where I diverted form POLYGON tutorial. To completely follow the example code of this article we will have to do the following steps.

  1. Name your project as ActiveXCtl
  2. Name the control as ShapeCtl.
  3. Since our control is circular and we will fill it with different kinds of fill patterns, we will add two custom properties to control, Radius and FillPattern. To do so follow the steps to add Sides property in POLYGON tutorial.
  4. POLYGON tutorial has only one event added to control. Following the same method we will add three methods to our control. Right click on CShapeCtl class in class view and add windows message handlers for WM_LBUTTONDOWN, WM_LBUTTONDBLCLK and WM_MOUSEMOVE. As is clear from these messages that our control will fire events for click, double click and mouse move on the control. The following functions will be added to CShapeCtl.h file
  5.    LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam,
                               LPARAM lParam, BOOL& bHandled);
       LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam,
                             LPARAM lParam, BOOL& bHandled);
       LRESULT OnMouseMove(UINT uMsg, WPARAM wParam,
                           LPARAM lParam, BOOL& bHandled);

    We will see the implementation a little later.

  6. In the second step of POLYGON tutorial where control is configured, from the Stock Properties tab add all the stock properties to control. It is not necessary that you have to add all of them. Since for my learning experience I wanted to see how all of these get implemented, I added all of these. Therefore to look your code similar to the example code, add all the stock properties.
  7. Follow step 6 of POLYGON tutorial to add property pages to control. We will add two pages and name these as ShapeProp and ApperanceProp. Add the controls to these pages as show in the figure
  8. Figure 1


    Figure 2

  9. We will not add any MFC support to our project because we won’t be using it.

Add the necessary controls and corresponding variables to the property page classes to manipulate the values corresponding to these controls. Since at the start of this article I told you that I didn’t add any MFC support to the project, therefore we will not be making use of CPropertPage or other MFC classes for these property page. We will use Win32 API calls to manipulate all the controls. I know it sucks after using the MFC for our applications. If you don’t want to go my way then you add the MFC support to this control and make use of all the control classes. Here is part of our code that initializes the controls.

LRESULT CAppearanceProp::OnInitDialog(UINT uMsg, WPARAM wParam,
                                      LPARAM lParam,
                                      BOOL& bHandled)
   // Let's set some ranges for controls.
   if ((m_hBorderWidthEdit = GetDlgItem (IDC_BORDERWIDTH_EDIT))
      != NULL)
      ::SendMessage (m_hBorderWidthEdit, UDM_SETRANGE, 0,
                     MAKELONG (10, 0));

   if ((m_hBorderWidthSpin = GetDlgItem (IDC_BORDERWIDTH_SPIN))
      != NULL)
      ::SendMessage (m_hBorderWidthSpin, UDM_SETRANGE, 0,
                     MAKELONG (10, 0));
   m_hAppearanceCombo    = GetDlgItem (IDC_APPEARANCE_COMBO);
   m_hFillStyleCombo     = GetDlgItem (IDC_FILLSTYLE_COMBO);
   m_hFillPatternCombo   = GetDlgItem (IDC_PATTERN_COMBO);
   m_hDrawStyleCombo     = GetDlgItem (IDC_DRAWSTYLE_COMBO);
   m_hEnableCheck        = GetDlgItem (IDC_ENABLE_CHECK);
   m_hBorderVisibleCheck = GetDlgItem (IDC_BORDERVISIBLE_CHECK);
   m_hTabStopCheck       = GetDlgItem (IDC_TABSTOP_CHECK);

All the control variables have been defines as HWND.

Control Implementation

Now we have done enough work to prepare skeleton structure for our control. Compile the project and test it by inserting it in ActiveX Control Test Container provided in the Tool menu of VC++ IDE. Ofcourse you will see nothing else but a rectangle with the label "ATL 2.0". Now we will fill the function bodies to draw the control the way we want it to be. Before I start discussing these functions, here is one thing that I did to the source code files. Object wizard puts most of the function bodies in header file. To separate the interface definitions and their implementation I moved all the function implementations to source files. Therefore if you see any difference between your files and the example code files, this could be the reason.

OnDraw ()

An OnDraw method is automatically added to your control class when you create your control with the ATL Object Wizard. POLYGON tutorial has put code for drawing simple polygon. We will add code to draw circular control, but a fancy one. Our control implements circle filled with fill patterns and you can change the font to one of your choice. Here is part of code in OnDraw function that fills the control with pattern you specify

switch (m_nFillPattern)
case fillBDiagonal:
   hBrush = CreateHatchBrush (HS_BDIAGONAL, colorFill);

case fillCrossHatch:
   hBrush = CreateHatchBrush (HS_CROSS, colorFill);
case fillDiagXHatch:
   hBrush = CreateHatchBrush (HS_DIAGCROSS, colorFill);
case fillFDiagonal:
   hBrush = CreateHatchBrush (HS_FDIAGONAL, colorFill);
case fillHorizontal:
   hBrush = CreateHatchBrush (HS_HORIZONTAL, colorFill);
case fillVertical:
   hBrush = CreateHatchBrush (HS_VERTICAL, colorFill);

The font property is implemented through IFontDisp interface. So we will make use of it to get, change and release the font object handle.

CComQIPtr<IFont, &IID_IFont> pFont (m_pFont);
if (pFont != NULL)
   pFont->get_hFont (&hFont);
   pFont->AddRefHfont (hFont);
   hOldFont = (HFONT)SelectObject (hdc, hFont);

And as a good Win32 programming practice, do delete al the GDI objects like HPEN, HBRUSH, HFONT, etc. after these have been used. And don’t forget to restore the font, pen and brush in the device context before you exit the function.

if (pFont != NULL)
   pFont->ReleaseHfont (hFont);

SelectObject (hdc, hOldFont);
SelectObject (hdc, hOldBrush);
DeleteObject (hBrush);
SelectObject (hdc, hOldPen);
DeleteObject (hPen);

Ambient Properties Change

If you want your control to respond to any change in the ambient properties of the container that is hosting the control, we will have to implement the OnAmbientPropertyChange method of IOleControl interface. Our control has already has IObjectControl in the inheritance list. If its not, then add it to the list of classes from which our control is derived from. And add corresponding entry in the BEGIN_COM_MAP. In the header file you need to add the following lines for this

class ATL_NO_VTABLE CShapeCtl :
   public IOleControlImpl<CShapeCtl>,


Add the following interface definition to CShapeCtl.h file.

// IOleControl
   STDMETHOD(OnAmbientPropertyChange)(DISPID dispid);

And in the source file we will handle the property we want to respond to. In the example code I have handled the change in Background color.

STDMETHODIMP CShapeCtl::OnAmbientPropertyChange (DISPID dispid)
   // It is up to the control to decide which ambient property it
   // wants to respond to.
   // NOTE: In this control only change to background color is
   // being acknowledged. If you want to respond to any other
   // ambient property, add the appropriate code at the appropriate
   // place in switch statement.

   switch (dispid)
      // Get the ambient back color and change the view of control.

      if (SUCCEEDED (GetAmbientBackColor (m_clrBackColor)))
         FireViewChange ();


Control Redraw

Whenever some control property changes you would like to redraw the control. Call FireViewChange method of CComControl class. The following code shows how the control handles the change in radius.

STDMETHODIMP CShapeCtl::put_Radius(short newVal)
   if (newVal <= 0)
      return (Error (_T ("Radius value can't be zero!")));
   else if (newVal > RectHt || newVal > RectWd)
      return (Error (_T ("Radius can't exceed control's extents!")));

   m_nRadius = newVal;

   // Change the control's view.

   FireViewChange ();

   return S_OK;

Event Firing

While creating control we added three methods to handle mouse button down and move events. For firing event for mouse click we check the location where the click took place. If its inside the control region, we fire ClickIn event otherwise we fire ClickOut event.

LRESULT CShapeCtl::OnLButtonDown(UINT uMsg, WPARAM wParam,
                                 LPARAM lParam, BOOL& bHandled)
   HRGN rgn;
   WORD xPos = LOWORD (lParam);
   WORD yPos = HIWORD (lParam);

   // Create the elliptic region to determine if the click point was
   // inside or outside the control.

   rgn = CreateEllipticRgn((CenterPt.x - m_nRadius),
                           (CenterPt.y - m_nRadius),
                           (CenterPt.x + m_nRadius),
                           (CenterPt.y + m_nRadius));
   // If the click was inside control, then fire ClickIn; otherwise,
   // fire ClickOut.
   if (PtInRegion (rgn, xPos, yPos))
      Fire_ClickIn (xPos, yPos);
      Fire_ClickOut (xPos, yPos);
   // Deleted the created region object.
   DeleteObject (rgn);
   return 0;

Use CreateEllipticRgn API call with the radius of control and then use PtInRegion API call to check if the click was inside this region or not. Don’t forget to delete the created region object.

Control Persistence

Here is the most important part of COM objects. What do we do with the control we have created? How should I save the properties of this control? How will the container, which uses this control, reload the control in the state it was saved? And you know this is the part that is not explained in most of the examples published in books. And this was the part which took me couple of weeks to get the things straight. There are different types of persistence interfaces provided in COM to accomplish this task. But the interfaces which are of main interest to ActiveX controls are IPersistStream, IPersistStorage, and IPersistPropertyBag. A container implements only one type of persistence interface. But if you want to make your control useable by any kind of container, implement each one of these interfaces. Why do we have to implement each one on the interface? And this was the same question I asked myself when I was experimenting with this control and trying to insert it in different kinds on containers. Here are couple of hints to decide which interface will be used by the container.

  • VB 6.0 needs only IPersistStreamInit or IPersistStream. And when VB6.0 saves the control in a form, it uses IPersistPropertyBag interface to save and load the control.
  • IE 4.0 will only use IPersistProprtyBag to load the control.
  • VisualInterDev uses IPersistPropertyBag to save the state of the control
  • MS Office applications use IPersistStorage interface to save and load the control
  • When you insert the control in VC++ dialog box, it uses IPersistStreamInit interface to save and load the Control State.

If you look in the resource file of the Container App project, you will see the following code

// Dialog Info

    IDC_SHAPECTL, 0x376, 129, 0
    0x0000, 0x0000, 0x0060, 0x8080, 0x0000, 0x0080, 0x0080, 0xcdcd,
    0xcdcd, 0xcdcd, 0xcdcd, 0x00ff, 0x0000, 0xcdcd, 0xcdcd, 0xffff,
    0xffff, 0xcdcd, 0xcdcd, 0x0000, 0x0000, 0x0008, 0x0000, 0xffff,
    0xffff, 0x0002, 0x0000, 0x0018, 0x8000, 0xcdcd, 0xcdcd, 0x0002,
    0x0000, 0x0022, 0x0000, 0x004e, 0x0061, 0x0076, 0x0065, 0x0065,
    0x006e, 0x0027, 0x0073, 0x0020, 0x0043, 0x006f, 0x006e, 0x0074,
    0x0072, 0x006f, 0x006c, 0x0000, 0x0001, 0x0600, 0x02bc, 0xd4c0,
    0x0001, 0x530e, 0x7263, 0x7069, 0x2074, 0x544d, 0x4220, 0x6c6f,

This is how IPersistStreamInit interface saved the control sate. Next time you load the dialog box, the Control State will be loaded from this stream.

All the persistence interfaces have same methods, but with different argument types, to save and load the control state. For implementation of these three persistence interfaces, make sure that we have these in the inheritance list and the PROP_MAP_ENTRY. If not, then add these.

class ATL_NO_VTABLE CShapeCtl : 
public IPersistStreamInitImpl<CShapeCtl>,
public IPersistStorageImpl<CShapeCtl>,
public IPersistPropertyBagImpl<CShapeCtl>,

In the property map add following entries if not present.

   COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)

And add the following interface definitions to CShapeCtl.h.

// IPersistStreamImpl
STDMETHOD(Load)(IStream *pStream);
STDMETHOD(Save)(IStream *pStream, BOOL fClearDirty);

// IPersistStorageImpl
STDMETHOD(Load)(IStorage *Storage);
STDMETHOD(Save)(IStorage *pStorage, BOOL fSameAsLoad);

// IPersistPropertBag
STDMETHOD(Load)(IPropertyBag *pPropBag, IErrorLog* pErrorLog);
STDMETHOD(Save)(IPropertyBag *pPropBag, BOOL fClearDirty,
                BOOL fSaveAllProperties);

Now we will have to provide the interface implementation in CShapeCtl.cpp file. I wouldn’t be going into details of implementation of each interface. I have tried to explain each and every detail of these interfaces in the CShapeCtl.cpp file While implementing these interfaces make sure that data is loaded in the same order in which it was saved. And you use the same stream and storage names. IPersistPropertyBag persistence has some problems. The default ATL implementation persist data in property bag if its of VARIANT type. So we will need some method to save user-defined data types. Therefore the simplest way would be to convert custom data types to one of the types defined in VARIANT type. And then save it in the property bag. And while loading reconvert it to user defined data type. For more explanation see "Professional ATL COM Programming", Wrox Press Ltd, by Dr. Richard Grimes. The code used in the example has been taken from this book.

void CShapeCtl::Insert (BSTR *pStream, void *pData, DWORD size)
   CComBSTR bstr;
   bstr.Attach (*pStream);

   LPBYTE ptr = static_cast<LPBYTE>(pData);
   TCHAR strTemp[3] = {0};

   for (DWORD j = 0; j < size; ++j)
      BYTE ch = *ptr;
      strTemp[1] = (ch & 0xf) > 9 ? '7'+ (ch & 0xf) : '0' +
                   (ch & 0xf);
      ch = ch >> 4;

      strTemp[0] = (ch & 0xf) > 9 ? '7'+ (ch & 0xf) : '0' +
                   (ch & 0xf);

      bstr += strTemp;

   *pStream = bstr.Detach ();

void CShapeCtl::Extract (LPCWSTR *pStream, void *pData, DWORD size)
   LPCWSTR str = *pStream;
   LPBYTE ptr = static_cast<LPBYTE>(pData);

   for (DWORD j = 0; j < size; ++j)
      BYTE ch = (str[0] >= L'A') ? str[0] - L'7' : str[0] - L'0';
      ch *= 16;
      ch += (str[1] >= L'A') ? str[1] - L'7' : str[1] - L'0';
      *ptr = ch;
      str += 2;

   *pStream = str;

This code converts your data type to string type and save as VARIANT and while reloading convert from string to user defined type. For more explanation see the comments in the code.

Control Category and Safety

If you insert this control in a web page and load the page, a warning message warning about unsafe ActiveX control gets displayed. It only gets displayed if you click OK. So we should mark our controls safe or unsafe so that when o container wants to load it, it knows about it. There are two ways of doing this. First way is to include the following category map in your control. I didn’t use this method. Instead I added the component category creation code in ActiveXCtl.cpp. During the registration of control, category gets created and registered in the registry. And when control is unregistered, this category is removed form the system registry. This way you can define your own control category types. Look in DllRegisterServer and DllUnregisterServer function bodies for implementation.


Second way is to Implement IObjectSafety interface. I have implemented this interface too. There was no need to implement both methods, but I did it for sake of getting experience of implementation of these methods. Add the following interface definition in the inheritance list, property map.

class ATL_NO_VTABLE CShapeCtl :

   public IObjectSafetyImpl<CShapeCtl,


Add these interface definitions to CShapeCtl.h file

// IObjectSafety
         (REFIID riid, DWORD *pdwSupportedOptions,
          DWORD *pdwEnabledOptions);
         (REFIID riid, DWORD dwOptionSetMask,
          DWORD dwEnabledOptions);

Look in code for implementation details of the interface.

Testing Control

Figure 3

 I have included ContainerApp which is a simple dialog based MFC application. In the dialog box right click and choose Insert Activex control option. Select ShapeCtl control. Right click on the control and manipulate the control properties from the property pages we added.

To include this control in web page use any of the editor or use the ActiveXPage.html file included in the ComApps folder.


  1. Professional ATL COM Programming, Wrox Press Ltd, by Dr. Richard Grimes.
  2. Active Template Library, IDG Books Worldwide Inc, by Tom Armstrong
  3. The Essence of COM with ActiveX, Prentice Hall, by David S. Platt
  4. Inside OLE second Ed., Microsoft Press, by Kraig Brockscmidt
  5. POLYGON tutuorial supplied with VC++ 6.0
  6. Microsft knowledgebase articles


The example code has been compiled with VC+ 6.0 (SP-1) on Windows NT 4.0 (SP-4). If you compile the code, you will get 9 warnings. These warnings are there because I didn’t implement the dispatch interfaces for font, picture and mouse icon property of the control. I didn’t do this to keep the implementation of control as simple as I could. See Microsoft Knowledgebase Article ID : Q166472 for details on their implementation.



  • PopUpMenu on AcitveX Control

    Posted by to_senthil123 on 08/31/2005 06:54am

    hi all, I need a small help from u. Like i m n process of developing a server page. In that i have embeded an icon. but i have the icon as an Activex control. so that i can drop a file on that control to upload that file.Also i m enabling a popup menu for that icon so as to select many options from that pop up menu. As per the genral protocol on right click on the icon i call a JS page and on selecting an option from the menu, it must forward to a JSP page. the problem is that when i resize the IE window to (say 25% of its full size) and right click on that ICON ,...i m getting the [Activex control] icon infront of the popup menu distrupting the contents of the popup menu. so please give me a solution for that... thns n regards, Senthil

  • Create ActiveX control based on Dialog box.

    Posted by Legacy on 04/05/2003 12:00am

    Originally posted by: Essam

    Dear sir.
    I would like to know how to create Activex which based on dialog box.
    This way will make creating new activeX from stadred control easer.
    I try to create the dialog box in the function
    OnCreate(LPCREATESTRUCT lpCreateStruct)
    but this make many problem which make the application hang.
    can any one help me solve this problem.
    Thank you.

  • How to debug an Activex control?

    Posted by Legacy on 04/02/2003 12:00am

    Originally posted by: Jerrychan

    I cann't step into the code of my activex control in the test application,so I cann't debug it easily.Now I debug it by adding messagebox in the acitvex code,but it's really tiring.what's the easier way?

  • ActiveX control

    Posted by Legacy on 12/27/2002 12:00am

    Originally posted by: Umakanth

    How can draw on the ActiveX Control in design-time ?

  • Why can I not see the OCX icon in the executable ActiveX test container window?

    Posted by Legacy on 11/06/2002 12:00am

    Originally posted by: Shehenar Quayum

    I have created a project by using ActiveX control Wizard by using VisualC++( version 6)
    I have opened ActiveX control Test Container from a ActiveX project.
    I have inserted a new control called clock control( from edit of ActiveX control Test Container window).
    I built my project, and execute TestCon32.exe.
    But unfortunately I can not see the OCX icon in the executable ActiveX test container window. Does any body knows the reason?
    The control Menu of ActiveX test container is completely disabled, i.e. I can not select anything from executable window of control menu.
    Please let me know if you know the answer. I will appreciate your help.
    My email add:

  • How to place an ActiveX control in C++

    Posted by Legacy on 06/27/2002 12:00am

    Originally posted by: jayaram

    How to place an ActiveX control(any standard control) in C++ client with out using MFC?
    if any body has a solution please send the answer to my mail.

  • how to invoke ActiveX control using c++

    Posted by Legacy on 06/11/2002 12:00am

    Originally posted by: gopi

    any one help me on ActiveX controls
    my problem is..
    how do i invoke ActiveX control using c++ and display in
    the window.
    i donot want use MFC.
    Is there any way to get window handle of ActiveX control window after creating it with CoCreateInstance() method.?
    Is there any way to create and display ActiveX control without MFC?

    if u find any solution to above problem please mail me back


  • Tree Control in ActiveX Control

    Posted by Legacy on 11/05/2001 12:00am

    Originally posted by: kamran

    I have a broblem in Activex Control, I want to connect an Dialog Box that contain Tree Control and List Control to an ActiveX Control creation, means that when ActiveX Control create that Dialog Box(Dialog Box contain Tree control and List Control) will be connected to that ActiveX Control and will be drawn in that activeX Control.
    i also have MSchart(activex control) it also will be embeded in above ActiveX Control.
    please guide me with detail and tell me topics in MSDN and if source code is available in any site.

  • Property page is out of sync with ActiveX-es

    Posted by Legacy on 08/31/2000 12:00am

    Originally posted by: Desmon Tansuma

    A really good example of ATL ActiveX. Good extension of MSDN Polygon example. Thanks a bunch.

    Property page is out of sync if I am placing more than one ShapeCtrl Class in a container. I am using VC+ 6.0. For example:

    Placing a ShapeCtrl, and changing its properties using property page.

    Placing another ShapeCtrl. Property page of the new ActiveX will be out of sync from the ActiveX. It will have values from the other ActiveX. (maybe some issue with persistence)

    But, thanks again for the code. Good work.

  • MFC Programmer's SourceBook : ATL, COM, ActiveX

    Posted by Legacy on 05/04/2000 12:00am

    Originally posted by: David Barnett

    This page cannot be printed out even in landscape mode due to the width of the page. On the screen, the table's minimum width is rather large - larger than the background graphic.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date