Using a Custom ActiveX Control in MFC by shours

The ActiveX is a container with a well-determined functionality.

Everybody has heard or seen an ActiveX control. They are everywhere. You could see them in everyday programs, in your office suite or when you are browsing the Internet. An ActiveX can be a media player, a button or a picture. It can simulate custom components (e.g. electrical) or it can be a text editor. You could even see a ActiveX that implement a Web browser.

An ActiveX cannot although execute himself. Therefore you will need a program who can instantiate and play the ActiveX. From his birth the ActiveX is responsible for his own actions. But of course he is capable to communicate with the mainframe that created him. The communication takes place using methods. The ActiveX have one interface on witch the application can Invoke methods that produce the execution of a function at ActiveX level.

While you’re reading this, for sure, it came on your mind: “Why writing a ActiveX? Why not writing common classes?”

The main idea about developing ActiveX controls is that after the inner code is linked and compiled the result is language independent, it can be called and instantiated in many applications without recompiling the last ones. Another issues is that after writing some ActiveX controls and developing an application who plays them, the whole programming part is over, the developing of the project concentrates now on scripting and configurations.

The ActiveX is a powerful object that can realize from simple thing like computing a+b to entering the backdoor of your system and finding all your passwords. ActiveX technology it became mature over the years and is capable to do things easily with a consistent support and no other stuff. You can write an ActiveX control that can be loaded in a Web Browser, or be tested in ActiveX Control Test Container or you could write an application that use custom ActiveX controls.

This article is presenting one issue that its not always have been in the spotlight: creating an application that uses custom ActiveX controls.

Creating the application:

First of all we need to create an application that use MFC. So, to read this article more easily, we’ll choose MFC AppWizard (exe):

I will name the application “Evol”. Next I will choose “Single Document” with “Document/View architecture support”. I have chosen “Single Document” to develop more easily the application.

No support for database.

In the step 3 I will include Container support and ActiveX Controls.

We will include a toolbar, a status bar and the toolbar will have a normal look.

 

For the step 5 of 6 we will not have any changes.

And finally the 6th step will bring a window with the filenames. We will not operate any changes here.

The classes made are: CAboutDlg, CEvolApp, CEvolCntrItem, CEvolDoc, CEvolView, CMainFrame.

Now we will have to make some changes and adding to the code.

For the class CEvolDoc, the one who is manipulating the document itself, we will some functionality. Here you will develop the routine that is loading the ActiveX. In the public section of the header add:

          ICatInformationPtr m_pCatInfo;

CArray< CATID, CATID& > m_aImplementedCategories;

CArray< CATID, CATID& > m_aRequiredCategories;

CList< CLSID, CLSID& > m_lControls;

CEvolCntrItem* pItem;

Next you will have to add some code the function OnNewDocument():

          BOOL tDone;

HRESULT hResult;

IEnumGUIDPtr pEnum;

CLSID clsid;

LPOLESTR pszName;

CString strName;

ULONG nImplementedCategories,iCategory;

CATID* pcatidImpl;

ULONG nRequiredCategories;

CATID* pcatidReq,catid;

POSITION posControl;

tDone = FALSE;

posControl=m_lControls.GetHeadPosition();

// clsid=m_lControls.GetNext(posControl);

if(!posControl)

{

hResult =m_pCatInfo.CreateInstance( CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER );

if( FAILED( hResult ) )

{

TRACE( “Failed to create category managern” );

return FALSE;

}

catid = CATID_Control;

m_aImplementedCategories.Add( catid );

nImplementedCategories = m_aImplementedCategories.GetSize();

if( nImplementedCategories == 0 )

{

nImplementedCategories = ULONG( -1 );

pcatidImpl = NULL;

}

else

{

pcatidImpl = (CATID*)_alloca( nImplementedCategories*sizeof( CATID ) );

for(iCategory = 0; iCategory < nImplementedCategories; iCategory++ )

{

pcatidImpl[iCategory] = m_aImplementedCategories[iCategory];

}

}

nRequiredCategories = m_aRequiredCategories.GetSize();

if( nRequiredCategories == 0 )

{

pcatidReq = NULL;

}

else

{

pcatidReq = (CATID*)_alloca( nRequiredCategories*sizeof( CATID ) );

for( iCategory = 0; iCategory < nRequiredCategories; iCategory++ )

{

pcatidReq[iCategory] = m_aRequiredCategories[iCategory];

}

}

hResult = m_pCatInfo->EnumClassesOfCategories( nImplementedCategories, pcatidImpl, nRequiredCategories, pcatidReq, &pEnum );

if( FAILED( hResult ) )

{

return FALSE;

}

tDone = FALSE;

while( !tDone )

{

hResult = pEnum->Next( 1, &clsid, NULL );

if( hResult == S_OK )

{

pszName = NULL;

hResult = OleRegGetUserType( clsid, USERCLASSTYPE_FULL, &pszName );

if( SUCCEEDED( hResult ) )

{

strName = pszName;

CoTaskMemFree( pszName );

pszName = NULL;

if(strstr(strName, “EvolActiveX”))

posControl = m_lControls.AddTail( clsid );

}

}

else

{

tDone = TRUE;

}

}

}

if(tDone)

{

pItem=NULL;

Load();

}

 

Next add a member function to EvolDoc: void Load().

The implementation is:

 

          CRect rect;

HRESULT hResult;

IEnumGUIDPtr pEnum;

CLSID clsid;

LPOLESTR pszName;

CString strName;

POSITION pos=0;

pos=m_lControls.GetHeadPosition();

while(pos)

{

clsid=m_lControls.GetNext(pos);

hResult = OleRegGetUserType( clsid, USERCLASSTYPE_FULL, &pszName );

if( SUCCEEDED( hResult ) )

{

strName = pszName;

CoTaskMemFree( pszName );

pszName = NULL;

}

else

{

pszName = NULL;

}

if(strName==”EvolActiveXExample1 Control”)

{

pItem = new CEvolCntrItem(this);

rect.left = 150; rect.right = 200; rect.top = 150; rect.bottom = 200;

pItem->m_rect = rect;

pItem->CreateNewItem(clsid);

if(pItem->m_lpObject != NULL)

pItem->InvalidateItem();

break;

}

}

Some of the structures called in the above are not define in the namespace of this project or in the StdAfx.h, so that’s way you will have to mannualy include them. In the StdAfx.h include the following headers:

            #include <comdef.h>

#include <comcat.h>

#include <afxtempl.h>

Next we will add the ActiveX position in the document. This must be a member variable of the class CEvolCntrItem:

public:

CRet m_rect; // position within the document

NOTE: Before running the application make sure you already registered the ActiveX. I have developed for this application a quite simple container that draw a ellipse and fill the exterior of the ellipse with some color. If you will build the application at this point, there will appear a window which contains the ActiveX.

Although you cannot move or resize the ActiveX at this moment. To resize or move we will need to change some features in OnDraw function in the class CEvolView. Comment the implicit code and add the new code pDoc->pItem[0]->DoVerb(OLEIVERB_SHOW, this); so your new OnDraw look like:

            void CEvolView::OnDraw(CDC* pDC)

{

CEvolDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

// TODO: add draw code for native data here

// TODO: also draw all OLE items in the document

// Draw the selection at an arbitrary position. This code should be

// removed once your real drawing code is implemented. This position

// corresponds exactly to the rectangle returned by CEvolCntrItem,

// to give the effect of in-place editing.

// TODO: remove this code when final draw code is complete.

/* if (m_pSelection == NULL)

{

POSITION pos = pDoc->GetStartPosition();

m_pSelection = (CEvolCntrItem*)pDoc->GetNextClientItem(pos);

}

if (m_pSelection != NULL)

m_pSelection->Draw(pDC, CRect(10, 10, 210, 210));*/

pDoc->pItem->DoVerb(OLEIVERB_SHOW, this);

}

We have managed the first part. We got to the interesting part: calling a method on the ActiveX interface. The ActiveX that I created have a method called SetColor: void SetColor(short sColor); It is the first method on the interface so that’s why will be called as InvokeMethod0.

Let’s concentrate now on the functionality of the server. You will need to construct now the function that call the 1st method on the ActiveX interface. At the level of CEvolDoc a new function will appear:

void InvokeMethod0(short sColor);

The function will be implemented like this:

             void CEvolDoc::InvokeMethod0(short sColor)

{

DISPPARAMS dpParams;

HRESULT hResult;

COleVariant v, m_varResult;

EXCEPINFO m_excepInfo;

UINT iArgErr;

dpParams.rgdispidNamedArgs = NULL;

dpParams.cNamedArgs = 0;

dpParams.cArgs = 1;

dpParams.rgdispidNamedArgs = NULL;

dpParams.cNamedArgs = 0;

v.vt=VT_I2;

v.lVal=(short) sColor;//m_pvarParams;

m_varResult.Clear();

dpParams.rgvarg=v;

CEvolCntrItem* pItemHit =pItem;

IDispatchPtr m_pDispatch;

hResult = pItemHit->m_lpObject->QueryInterface( IID_IDispatch, (void**)&m_pDispatch );

if(FAILED(hResult))

{

return;

}

hResult=m_pDispatch->Invoke(1,IID_NULL,GetUserDefaultLCID(),INVOKE_FUNC,&dpParams,&m_varResult,&m_excepInfo,&iArgErr);

}

To really set a color to the ellipse I’ve made up a little dialog box with a combo box.

You choose the color then you hit a button and the next part of code will be executed:

            void CEvolSetColorDlg::OnButtonSetColor()

{

CMainFrame * parent = (CMainFrame*)this->GetParent();

CDocument* pDoc = parent->GetActiveDocument();

short pos = m_combo_set_color_ctrl.GetCurSel();

((CEvolDoc*)pDoc)->InvokeMethod0(pos);

}

 

By: Dragos BREZOI.


Attachments

Project Files Demo Files