Dynamic creation and placement of ActiveX controls



Environment: VC6 SP4, NT4

Contents

Why do we need the ability of dynamic ActiveX control creation and placement ?

Most of the applications that use ActiveX controls, do so by embedding them at design time. In this article I have demonstrated how simple it is to create and place Active X controls at run time. I have used CWnd::CreateControl method of MFC.

Dynamic placement of ActiveX controls throws open the possibility for an application window to acquire different 'look and feel' at run time. You could think of an software application, which would be nothing more than a simple MFC created frame work, capable of loading into itself ActiveX controls written by Visual Basic developers. Such a paradigm offers the scope for customization as well.

Think of an imaginary software that should have the capability of displaying different image files like .jpg, gif, .bmp, etc. Further, individual image viewer ActiveX controls for each of the formats are also made available to us. How should you proceed? A simple approach could be to embed an instance of each of the controls on the main window, and then make visible any one of the control as per file format requirements.

Think of a software design, where in you provide a basic executable that contains a frame work and nothing else. The framework does nothing more than providing a menu, toolbar and some client space. You will let the framework application dynamicaly instantiate and load ActiveX controls into it's client space. The ActiveX controls in this case are the image viewers. While distributing my multi format image viewer product, I would also distribute the individual ActiveX controls, responsible for displaying each of the file formats (.jpg , .gif, .mpg, ..etc.)

Further, my installation package would create a registry list, containing the ActiveX control CLSID/ProgId responsible for each of the file format.

When an user, selects an image file , my product will determine the progid/clsid of the ActiveX control from the registry and instantiate that control within the application frame work, and the display the image file. The requirement from the ActiveX Control's side should be to implement a well published method/property through which I can pass on the file name, that is to be displayed by the ActiveX Control.

In this article I have created a very basic MFC dialog application, that loads an ActiveX control when supplied with a prog id.
top

Create a simple MFC dialog based application

Create a new MFC Dialog based EXE application. Make sure that the "Enable ActiveX Controls" option is checked.

top

Dynamic ActiveX Control creation

The class CWnd provides a method CreateControl that makes ActiveX control creation very much like a normal window. Add a private member variable (name it m_ControlWrapper), of type CWnd, in the project's main dialog class.

  class CYourApplicationDialog
    {
    .
    .
    .
  private:
    CWnd m_ControlWrapper;
    .
    .
    };

The member m_ControlWrapper will be the wrapper for our dynamically instantiated ActiveX control.Provide a menu bar to the dialog window.

Provide a handler for the "Insert ActiveX control" menu item.

void CYourApplicationDialog::OnFileInsertcontrol() 
  {
  // TODO: Add your command handler code here

  //
  // You may prompt  the user for the progid of the 
  // ActiveX control. For the sake of simplicity, I 
  // have hard coded the prog id. The downloadable 
  // source code provides a dialog box to key in the 
  // desired progid

  CString strControlProgid;
  strControlProgid="mscal.calendar.7"; //calendar control


  //
  // Because we are not a MDI, we make sure to destroy
  // any earlier instances.
  //
  OnDestroycontrol();


  //
  // We will make the ActiveX control occupy the entire
  // client area
  //
  RECT rc;
  ::GetClientRect(m_hWnd,&rc);


  //
  //create the ActiveX control with the given prog id
  //

  BOOL bStat=FALSE;
  bStat=
  m_ControlWrapper.CreateControl (
                          strControlProgid,
                          "",
                          WS_VISIBLE, 
                          rc,
                          this,
                          5000,
                          NULL,
                          FALSE,
                          NULL);

  if (bStat == FALSE)
     {
     ::MessageBox (m_hWnd,"Error!!",
                   "Could not place control",
                   MB_OK);
     return;
     }

  ::EnableMenuItem(
              ::GetMenu(m_hWnd),
              ID_DESTROYCONTROL,
              MF_ENABLED | MF_BYCOMMAND);


  //
  //display the prog id in the window's title bar
  //
  ::SetWindowText (m_hWnd,strControlProgid );

  }
top

Destroying the ActiveX Control

To safely unload the control we wil have to invoke CWnd::DestroyWindow. The code for control destruction is as follows:

void CYourApplicationDialog::OnDestroycontrol() 
  {
  // TODO: Add your command handler code here

  //
  //Use the method CWnd::DestroyWindow() 
  //
  m_ControlWrapper.DestroyWindow();

  ::EnableMenuItem(
              ::GetMenu(m_hWnd),
              ID_DESTROYCONTROL,
              MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);

  ::SetWindowText (m_hWnd,"ActiveX Container");
  }
top

Resizing the ActiveX Control

We will dynamicaly maintain the size of our control equal to that of the client area of the main window. To achieve this, the method IOleObject::SetExtent should be used. But unlike Win32 functions, the dimensions need to be in twips.

(1 twip ~ 1/1440 inch).

We will override the OnSize message handler of the main window. Here we will obtain the new client rectangle extents, convert the dimensions into twips, and then invoke the IOleObject::SetExtent method.

Use the CWnd method GetControlUnknown() to get the IUnknown interface of the ActiveX control COM object. Using the IUnknown, query for the interface IOleObject In this example, I have used the _com_ptr_t smart interface pointer class. VC++ provides such wrappers for all standard OLE interfaces. Given an interface IMyInterface, the following single statement

_COM_SMARTPTR_TYPEDEF(IMyInterface,
                      __uuidof(IMyInterface));

will provide a smart interface pointer wrapper with the name IMyInterfacePtr. Do not forget to include the header file comdef.h .
void CYourApplicationDialog::OnSize(UINT nType,
                                         int cx, 
                                         int cy) 
  {
  CDialog::OnSize(nType, cx, cy);

  // TODO: Add your message handler code here
  //
  //update the control's dimensions, 
  //

  //
  //make sure that we have a valid embedded control
  //
  if (m_ControlWrapper.GetControlUnknown() == NULL) return;

  IOleObjectPtr pOleObj(m_ControlWrapper.GetControlUnknown ());
  if (pOleObj != NULL) 
     {

     RECT rc;
     ::GetClientRect (m_hWnd, &rc);

     CSize size(rc.right , rc.bottom );
     CClientDC dc(NULL);
     //
     //convert from pixel to twips.
     //
     dc.DPtoHIMETRIC(&size);


     pOleObj->SetExtent (1,(SIZEL*)&size);
     pOleObj->Update();//this does not seem to be working
     ::RedrawWindow(m_hWnd,NULL,NULL,RDW_ALLCHILDREN | 
                                     RDW_UPDATENOW | 
                                     RDW_INVALIDATE);
     }

  }

top

Downloads

The following binaries have been provided with this article:
  • Exe_client.exe:This is the main application, whose screen shots have been provided above
  • Sample1.ocx:Sample OCX created using Visual Basic. (progid=CodeGuru.Sample1)
  • Sample2.ocx:Sample OCX created using Visual Basic.(progid=CodeGuru.Sample2)

Download demo project - 91 Kb
Download source - 187 Kb

top


Comments

  • good job

    Posted by Bahar Ali on 09/27/2012 12:09am

    v useful article, thanks for sharing...

    Reply
  • VB OCX crash problem

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

    Originally posted by: Bryan Welton

    Hi,

    Found this article very useful. However, I have hit a problem. If I create an OCX in VB which itself uses mscomctl.ocx, I get a crash occurring when I close the container.

    Anyone else observed this?

    --
    Bryan Welton
    Software Engineer
    ORIMOS Ltd

    Reply
  • Couldn't put ActiveX in a child window from a MFC doc/view app

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

    Originally posted by: Lucian

    int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    
    {
    if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

    // TODO: Add your command handler code here
    m_pCtrl = new CPolyCtl(); // the ActiveX wrapper
    bool ret = m_pCtrl->Create("Hi",WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,1234);
    if (!ret)
    AfxMessageBox("Create failed !");

    return 0;
    }

    void CChildFrame::OnEditPaste() // on some event
    {
    m_pCtrl->ShowWindow(SW_SHOW);
    m_pCtrl->UpdateWindow();
    }

    Reply
  • Is there any code of .ocx file which will add Gif Images to forms..

    Posted by Legacy on 01/30/2003 12:00am

    Originally posted by: G. Satyanarayanane

    Dear Friends,
    I would like to know how to create OCX file which enables us to add the .GIF file to the projects. It should be done in VB-6. Is it possible?? If so and if you R having any examples pls post or send email to me.

    Thanks
    Satyanarayanane.

    Reply
  • What about serialization?

    Posted by Legacy on 10/09/2002 12:00am

    Originally posted by: Alexei

    I have been developing a project with dynamic creation of ActiveX controls. Unfortunately, wrapper class for my ActiveX control doesn't have Serialize(...) member, and CWnd impelemtation does nothing except calling CObect::Serialize(...) method. So the question is how to make serialization with MFC?

    Reply
  • The Easy Way of Eventhandling

    Posted by Legacy on 10/02/2002 12:00am

    Originally posted by: Markus Schiefele

    Thinking about my last comment, I noticed that it might be difficult to get the DISPID of the event you want to handle. So I found a more simple way of event-handling. Before you delete the control from the dialog template and remove the DDX_CONTROL entry from the MESSAGE_MAP, simply let the wizard create event-handling routines for all the events you want to handle. So the wizard will create an EVENT_SINK_MAP and will do nearly all the work for you. The only thing that remains is to change the ID of the control in the ON_EVENT macros. If you want to add more than one control on runtime, you also have to replace the ON_EVENT macros by ON_EVENT_RANGE macros. Please don't forget to add an additional VTS_I4 paramater at the beginning of the parameter list of the ON_EVENT_RANGE macros, and also an ULONG parameter to get the ID in the event-handling routines.
    I think you will never have it more easily.

    Reply
  • The Easy Way and Event Handling

    Posted by Legacy on 09/30/2002 12:00am

    Originally posted by: Markus Schiefele

    There is a much more easier way to create ActiveX-controls on runtime. Add the desired control to the dialog-template, then ctrl-double-click it to make the class wizard add a wrapper-class for it. Remove the control from the dialog-template and remove the DDX_CONTROL entry from the AFX_DATA_MAP (you will find it in the DoDataExchange Method of your dialog class). Now you can use the instanciated wrapper class the wizard created. you can use the Create (it has the same Parameters as CreateControl from CWnd)Method to display it and the DestroyWindow Method to delete it. If you want to give your control an ID of 5000 and the instance of the wrapper is m_ctlMyControl it coul be:
    
    

    m_ctlMyControl.Create(NULL, WS_VISIBLE, rControlRect, this, 5000);

    Where RECT rControlRect contains the rect in which to display the control.

    Now about eventhandling. This a little bit more work. First add the DECLARE_EVENTSINK_MAP() macro to the header of your dialog class (best at the end). In the cpp of your class you must add an event-sink-map next. If your class is CMyDlg it might have the following form:

    BEGIN_EVENTSINK_MAP(CMyDlg, CDialog)
    ON_EVENT_RANGE(CMyDlg, 5000, 5009, 1, OnEvent1, VTS_I4 VTS_I4 VTS_I4)
    ON_EVENT_RANGE(CMyDlg, 5000, 5009, 2, OnEvent2, VTS_I4 VTS_I4 VTS_I4)
    END_EVENTSINK_MAP()

    The ON_EVENT_RANGE macro has the following parameters: first the name of your dialog class, the next two describe the range of the ID of your control. The ID was fixed in the Create Method (here the ID should have a value between 5000 and 5009, i.e. you can create ten controls at runtime. If you want to create only one control at runtime you can use the simpler ON_EVENT macro). The next parameter is the dispatch-id of Event you want to handle. Then comes the name of the Method to handdle the event. Last but not least follows the parameterlist. The first parameter must always be a VTS_I4 parameter because it gets the ID of the control, which fired the event. The following parameters are the parameters of the event. This EVENT_SINK_MAP would be good if your control has two events, both with two VTS_I4 parameters (like clicked_in and clicked_out of the Polygon control from the ATL-Tutorial in MSDN-help).
    Now add the Methods to handle the events in the Message-Map of the header of your dialog class (maybe below OnQueryDragIcon()). For the Event 1 in our example this might look like:

    afx_msg BOOL OnEvent1(UINT id, long x, long y);

    Please note that the method has to return a BOOL. The implementation could be:

    BOOL CMyDlg::OnEvent1(UINT id, long x, long y)
    {
    if (id == 5000)
    {
    MessageBox("Control 5000 fired Event1");
    //Something else with x an y
    }
    return TRUE;
    }

    Thats it.


    Reply
  • Did anyone got an answer on how to handle events?

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

    Originally posted by: Miguel Chavez

    Any Ideas on how to handle event or another way of dynamically create ActiveX controls.
    Thank you

    Miguel

    Reply
  • Dynamic creation and placement of ActiveX controls

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

    Originally posted by: Sunil Gade

    This is a wonderful article.
    May I take this opportunity to ask you the following questions?

    Que 1: How do I handle the events in the client of such an Active x control, if created it dynamically?
    Que 2: How do I create one Active X control from another Active X control. How do I make them communicate through events?

    Looking forward to get the valuable guidance from you ( if possible, I would appreciate your sending the sample source code)

    Yours Sincerely,
    Sunil Gade

    Reply
  • ActiveX vs DLL for dynamic creation

    Posted by Legacy on 02/20/2002 12:00am

    Originally posted by: Narayan Bhagavatula

    I want to dynamically create a control from another ActiveX Control (e.g MSFlexGrid), should I create the other control with a simple DLL or make my own custom ActiveX control which is dynamically created when desired ? Which is more coding, flexible etc. etc. ?

    thanks,

    Narayan Bhagavatula

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • On-demand Event Event Date: July 22, 2014 In this WhatWorks analysis, John Pescatore examines a use case where end users had local administrative rights on their PCs and it had gotten out of hand for this Fortune 500 Energy and Utilities company. The compelling event that prompted the company to reexamine this situation was the migration to Windows 7. In Windows XP, a custom tool that allowed users one of three levels of administrative rights to their workstations would need to be replaced during the Windows …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds