The Makings Of An OCX Container
Putting the OCX in it's place
With OLE 1.0, only embedded objects 
were supported. This meant that all objects opened as separate windows. But it 
 would be preferable to have the objects appear as a part of our 
application. The user should not be aware of the fact that he is dealing with 
two or more separate applications. This need  was satisfied  when 
in-place activation was introduced in OLE 2.0.
With in-place 
activation, the server appears within the container window. This means that the 
server window is a child within  the container window. Read my lips "A 
child within the container window".
As explained earlier, DoVerb() 
is responsible for the OCX display. The first parameter to 
DoVerb() decides the nature of display. Hence, in the function 
DoVerb() in Progarm 1, replace the first parameter by 
OLEIVERB_SHOW. This indicates that the OCX should appear as an 
in-place active object. Sadly this doesn't happen.
Earlier we had said that 
the OLE interface has no code. We have to add code to implement any and 
every feature that we require. We have provided no support for in-place 
activation, so how can we expect the OCX to be displayed in-place?
Add 
code from Progarm 2 to Program 1. This provides the  function 
implementations required for an OCX to appear in-place.
Program 2
 void MFrameWnd::OnPaint()
 {
   CFrameWnd::OnPaint();
 }
 
 void MFrameWnd::ObjCreate(CLSID z_TempClsid)
 {
   IClassFactory *z_pClassFactory;
 
   z_pOleClientSite = new MOleClientSite;
   z_pInPlaceFrame = new MOleInPlaceFrame;
   z_pOleInPlaceSite = new MOleInPlaceSite;
   CoGetClassObject(z_TempClsid,1,0,IID_IClassFactory,(void**)& z_pClassFactory);
   z_pClassFactory-> CreateInstance(0,IID_IOleObject,(void**)& z_pOleObject);
   z_pOleObject-> SetClientSite((IOleClientSite*)z_pOleClientSite);
   z_pOleObject-> DoVerb(OLEIVERB_SHOW,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }  
 void* _export _cdecl MOleInPlaceSite::GetWindow (HWND * lphwnd)
 {
  *lphwnd = z_hFWnd;
   return 0;
 }
 
 void*  _export _cdecl  MOleInPlaceSite::GetWindowContext(IOleInPlaceFrame  **lplpFrame, IOleInPlaceUIWindow **,  RECT  *lprcPosRect,RECT *,OLEINPLACEFRAMEINFO *lpFrameInfo)
 {
   _fmemset(lpFrameInfo,0,sizeof(OLEINPLACEFRAMEINFO));
   *lplpFrame = (IOleInPlaceFrame*)z_pInPlaceFrame;
   ::CopyRect(lprcPosRect,& z_Rect);
   lpFrameInfo-> hwndFrame = z_hFWnd;
   return 0;
 }
 
 void* _export _cdecl MOleClientSite::QueryInterface (REFIID riid , void **ppvObj )
 {
   if (riid == IID_IOleInPlaceSite)
   {
    *ppvObj = z_pOleInPlaceSite;
     return 0;
   }
   return ResultFromScode(E_NOTIMPL);
 }
Once again use Insert Control... sub-option place the OCX 
Button as an object within our container. This time the OCX 
will appear to be a part and  parcel of our window. The OCX is now 
said to appear as an in-place active object.
ObjCreate() has 
been modified to facilitate in-place activation. The classes 
MOleClientSite, MOleInPlaceFrame and MOleInPlaceSite are 
our stand-ins for the interfaces IOleClientSite, IOleInPlaceFrame 
and IOleInPlaceSite respectively. We have created an  instance of 
each in ObjCreate().
So far we have called functions in the 
OCX. We have asked  the OCX for information. In a similar 
fashion, the OCX also wants to know more about the container. To do so, 
it requires a pointer from the container. 
The container has to use the 
IOleObject function SetClientSite() to pass it's 
IOleClientSite pointer to the OCX. Thus, we pass the 
MOleClientSite pointer to SetClientSite() after suitable casting. 
The OCX on receiving the pointer through SetClientSite() will use 
it to determine if the container supports in-place editing by asking for an 
IOleInPlaceSite pointer. This it does by calling QueryInterface() 
of MOleClientSite with IID_IOleInPlaceSite.The OCX uses the 
MOleInPlaceSite pointer passed  to it to call two functions from 
this interface: GetWindow() and GetWindowContext(). If the 
OCX is to appear in-place within the container, then it needs to know the 
handle  to the parent window. In the function GetWindow(), we pass 
 a pointer  to  the  handle of the container's window 
 back  to  the OCX.
It  is the function 
GetWindowContext() that makes crucial information regarding the container 
available to the OCX. It has five parameters. The container is expected 
to pass the address of it's IOleInPlaceFrame object to the OCX 
using the first parameter. 
The third is a pointer to a RECT 
structure. This RECT structure holds the area in which the OCX 
should appear when in-place active. We use the global CRect object 
z_Rect that holds the initial  dimensions of the OCX to 
initialize this parameter. 
The last and the most important parameter is a 
pointer to an OLEINPLACEFRAMEINFO structure. This structure holds details 
such as whether the container is an MDI application, pointer to the frame window 
etc. We initialize just one member of  the structure to hold the handle to 
the container window. This is the only  member of the structure that 
has to be initialized. This is why we use the _fmemset() function 
to set all other members of the structure to 0 at the start of 
GetWindowContext().
The interface IOleInPlaceFrame is required 
only if we are going to merge the OCX menus with the container menu; 
bring in the OCX's toolbar and for other such actions. As of now, we do 
not use any function of this interface, but cannot do without it either. If we 
do not pass the address of an IOleInPlaceFrame instance to the OCX 
in the GetWindowContext() function then the OCX will appear 
embedded and not in-place.
The first parameter to DoVerb() is 
OLEIVERB_SHOW to indicate that the OCX should appear in-place. It 
is passed the pointer to the interface MOleClientSite as the  third 
parameter. In addition, the address of z_Rect is passed as the  last 
parameter to DoVerb(). This indicates  the  size and position 
that the OCX is to occupy in the  container's window.
In the 
above explanation, you might have noticed that every thing seemed cut and dry. 
Use SetClientSite() before DoVerb()  to pass the 
IOleClientSite() pointer  to the OCX. The OCX will 
then use this pointer to call QueryInterface() with 
 IID_IOleInPlaceSite. Use GetWindow() and 
GetWindowContext()  of  IOleInPlaceSite to pass all 
relevant information to the OCX etc.
But things are not always as 
self-evident as they seem. Nowhere in the OLE documentation are the steps 
to follow in  writing a container and/or a server stated 
explicitly.
This sequence was something that we have figured out using 
our own devices. Before, we tackled the OCX container  we had worked 
extensively on the OLE containers and both  DLL and EXE servers that 
were written in C/C++ and studied the interaction between them in detail. We 
read between the lines and used native  wisdom to piece together the 
complete  picture. (Small 
is Beautiful).
In the above example, the OCX expects the 
IOleClientSite function GetWindow() to return the window 
 handle to it. Similarly, GetWindowContext() is expected to 
 provide the OCX with other information necessary for in-place 
activation. If we do not provide the code in either GetWindow() or 
GetWindowContext()  the OCX will not appear in-place. 
Consider another scenario. What if  the OCX does not return an 
IOleObject pointer in CreateInstance()? The entire process will 
come to a grinding halt.
While all through the OLE documentation 
alludes to what has to be done in the various functions of the interfaces, never 
ever does it state when the functions are to be used. Thus, OLE 
will work if and only if both participants follow certain rules. As long as 
everyone tows the line, the OLE apparatus works just fine. But one step 
out of line and the whole system collapses; faster than a house of 
cards.
Unfortunately, the rules of OLE are unstated. Rules 
everyone  know exist but cannot pin-point. As of now, trial and error is 
the only way that one learns about the rules of OLE. To say that the path 
to OLE is strewn with obstacles is an understatement of 
understatements.
Some meat on the bones
Next we will add a few features to the container. 
For example, if an user clicks within the container window but outside  of 
 the OCX area when the OCX is in-place active, then  the 
OCX will get hidden. And what's more! An image of it will be 
 displayed on screen. There are several other such additions that have been 
made. 
Program 3
 MFrameWnd::MFrameWnd()
 {
   Create(0,"OCX Container");
   z_pMenu = new CMenu;
   z_pMenu-> LoadMenu(IDR_MENU1);
   SetMenu(z_pMenu);             
   z_pTracker  = new CRectTracker(& z_Rect,CRectTracker::solidLine);
 }
 
 void MFrameWnd::ObjCreate(CLSID z_TempClsid)
 {
  IClassFactory *z_pClassFactory;
   char *z_sName;
   
   z_pOleClientSite = new MOleClientSite;
   z_pInPlaceFrame = new MOleInPlaceFrame;
   z_pOleInPlaceSite = new MOleInPlaceSite;
   CoGetClassObject(z_TempClsid,1,0,IID_IClassFactory,(void**)& z_pClassFactory);
   z_pClassFactory-> CreateInstance(0,IID_IOleObject,(void**)& z_pOleObject);
   z_pOleObject-> SetClientSite((IOleClientSite*)z_pOleClientSite);
   z_pOleObject-> DoVerb(OLEIVERB_SHOW,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
   z_nActive = TRUE;
 }
 
 void MFrameWnd::OnPaint()
 {
   if(z_pOleObject)
   {                           
     SIZEL z_sizel;
     CPaintDC z_DC(this);
 
     if(!z_nActive)
     {
       z_pTracker-> m_nStyle =  CRectTracker::resizeInside|CRectTracker::solidLine;
       OleDraw(z_pOleObject,DVASPECT_CONTENT,z_DC.m_hDC,& z_Rect);
     }                                                                         
     else
       z_pTracker-> m_nStyle = CRectTracker::solidLine;
       z_pTracker-> Draw(& z_DC);     
       z_sizel.cx  = XformWidthInPixelsToHimetric(z_DC.m_hDC,(z_pTracker- > m_rect.right -
        z_pTracker-> m_rect.left));
       z_sizel.cy  = XformWidthInPixelsToHimetric(z_DC.m_hDC,
        (z_pTracker- > m_rect.bottom - z_pTracker-> m_rect.top));
       z_pOleObject-> SetExtent(DVASPECT_CONTENT,& z_sizel);
       ReleaseDC(& z_DC);
   }
   CFrameWnd::OnPaint();
 }
 
 void MFrameWnd::OnLButtonDown(UINT,CPoint)
 { 
   z_nActive = FALSE;
   z_pOleObject-> DoVerb(OLEIVERB_HIDE,0,(IOleClientSite*)z_pOleClientSite,
     -1,m_hWnd,& z_Rect);
 }                                                                                                            
   
 void MFrameWnd::OnLButtonDblClk(UINT,CPoint z_Point)
 {
     if(!z_nActive & &  z_Rect.PtInRect(z_Point)) 
     {
         z_nActive = TRUE;
         z_pOleObject-> DoVerb(OLEIVERB_SHOW,0,(IOleClientSite*)z_pOleClientSite,
           -1,m_hWnd,& z_Rect);
     }
 } 
 
 void MFrameWnd::OnMouseMove(UINT z_nFlags,CPoint z_Point)
 {                               
   int z_nDraw = 0;
   if(!z_nActive & &  (z_nFlags &  MK_LBUTTON) & &  z_Rect.PtInRect(z_Point))                                           
   {
       z_nDraw = 1;
       z_pTracker-> Track(this,z_Point);
   }                                                      
   if(z_nDraw)      
     {
       z_Rect = z_pTracker-> m_rect;
       Invalidate();
     } 
 }    
The  OCX will appear in-place. Notice that the OCX is 
now given a thin black line as a border. Click within the container window but 
not in the area occupied by the OCX. This will cause the actual 
OCX to be hidden from  view, that is it is de-activated, and an 
image of it will be displayed instead. Note a change in the border. The image 
has a black solid-line border with the dots along the inside of the 
line.
Click on the OCX image and drag the mouse around. The OCX 
image will move to the new location. Click on a dot along the border and drag 
the mouse. This will allow us to re-size the OCX. To re- activate the 
OCX, double click on the image. The OCX will appear in-place 
active at  it's new position and in the new size.
In ObjCreate(), 
we set the boolean variable z_nActive to TRUE. This indicates that 
the OCX is in-place active. z_nActive is used to hold the active 
status of the OCX. Every time the OCX is hidden it will be set to 
FALSE. 
When  the OCX appeared as an embedded object in 
our container,  you must have noticed that it opened as a separate 
 window. When we bring in an OCX as an in-place active object, all 
 that happens is that the window of the container is set as the parent 
 to the window of the OCX. The window of  the OCX, thus, 
appears as a child within the container window. It has just a border and no 
caption or menu. As long as the OCX is active and we are within the 
bounds of it's  window all the messages are passed to it's call-back 
function else  they are sent to the call-back function of the 
container.
A user may want to change the size and location of an in-place 
active OCX interactively. Since, the OCX window  is a child 
within the container window with just a border, there  is no way that we 
can directly change it's dimensions and position. So we resort to a bit of 
subterfuge. What if we had a rectangle around the OCX? The dimensions and 
position of which we could change with a mouse. We could then manipulate the 
 rectangle and pass it's new size and/or position to the OCX. The 
OCX can use this information to redraw itself so as to occupy the new 
size and/or position. Such a rectangle can be created using the MFC and 
is called a tracker.
The class CRectTracker of the 
MFC is used to implement a tracker in our application. In the constructor 
of the class MFrameWnd, we create a CRectTracker  object. 
This object  is initially set to have the same dimensions  and 
position as the OCX. We have stipulated that the tracker is to initially 
appear as a solid line coinciding with the  borders of the OCX. The 
advantage of using this class is that it provides built-in support that allows 
us to re-size and  move the tracker with the aid of a mouse. The style of 
the tracker  is changed  so that the user is provided with a visual 
 feedback  on the status of the OCX.
Every time we want to 
change the size and/or  position  of  the OCX,  we 
first click within the container window but outside of the area of the 
OCX to de-activate it. The message WM_LBUTTONDOWN will be sent to 
the container  window's call-back function. OnLButtonDown() will get 
executed. In this  function, we use DoVerb() with 
OLEIVERB_HIDE as the first parameter. This will hide the OCX 
window from view. What is now displayed is an image of  the OCX. 
z_nActive is set to FALSE to indicate that the OCX is no 
longer active.
Then, we click anywhere within the area bounded by the tracker 
rectangle and drag it around. The CRectTracker function Track() 
used in OnMouseMove() will be executed. It will update the 
CRectTracker variable m_rect to hold the new size and/or location 
of the tracker. We update our global variable z_Rect to hold these new 
dimensions and then repaint the container window so that the OCX image 
appears at the new location.
In OnPaint() we initially check the 
status of z_nActive to determine if the OCX is active or hidden 
 and appropriately change the style of the tracker.
When the OCX 
is not in-place active, the image of  the OCX has to be drawn and 
this drawing is handled by the OLE API OleDraw(). To 
OleDraw(), we pass the IOleObject pointer of  the 
 OCX. DVASPECT_CONTENT specifies that the image of  the 
OCX is to be rendered. We also pass OleDraw() a device context to 
the container window and the area where the image is to be drawn. 
OleDraw() uses all this data to draw a spitting image of the actual 
OCX.
The OCX also has to be informed of it's  new 
 dimensions. OLE sets standards for any all information exchanges 
 that are to take place. One such standard states that whenever dimensions 
are to be passed between applications, they must always be in HIMETRIC 
units. We convert the width and height  which are in pixels to 
HIMETRIC using XformWidthInPixelsToHimetric() and 
XformHeightInPixelsToHimetric() respectively and store them in the 
SIZEL structure. Both of these are OLE UI functions. We pass 
the SIZEL structure which holds the new dimensions to the OCX 
through the function SetExtent() of  the  interface 
IOleObject.
Experience  shows  that these dimensions in 
HIMETRIC are converted back to pixel units by the OCX. Then why 
pass dimensions in HIMETRIC? Because OLE says so. If you write 
your own container and OCX then you can pass dimensions back and forth in 
pixels. But then nobody else who follows the OLE set standards will be 
able to communicate  with you.
Double click on the image to re-activate 
the OCX. Once again, a DoVerb() with OLEIVERB_SHOW as the 
first parameter is executed. In this case, in OnPaint(), the style of the 
tracker will be changed to indicate that the OCX is active and it will be 
displayed at the new position and size set using the tracker.
The OCX in action
We had mentioned before that verbs provoke action 
in the OCX. However, the OCX has to have support for 
 the action. What do we mean by support? Consider OLEIVERB_OPEN. 
When this verb is invoked on the OCX, there should be code provided in 
the  OCX  so that it opens as an embedded object. 
Consequently, this means that the OCX should have code for all the 
different verbs that it can be invoked with. However, it is not mandatory that 
all the OCX's or indeed all OLE servers, should support all the 
verbs. It is upto the programmer writing  the OCX to decide the 
verbs that it will support. 
In the next program, we have demonstrated the 
effect of a few  of the verbs. We have assumed that all the OCXes 
brought into this container support these verbs.
Program 4
 void MFrameWnd::ObjCreate(CLSID z_TempClsid)
 {
   IClassFactory *z_pClassFactory;
   char *z_sName;
   CMenu *z_pAddMenu,*z_pPopMenu,*z_pSubMenu;
   
   z_pOleClientSite = new MOleClientSite;
   z_pInPlaceFrame = new MOleInPlaceFrame;
   z_pOleInPlaceSite = new MOleInPlaceSite;
   CoGetClassObject(z_TempClsid,1,0,IID_IClassFactory,(void**)& z_pClassFactory);
   z_pClassFactory-> CreateInstance(0,IID_IOleObject,(void**)& z_pOleObject);
   z_pOleObject-> SetClientSite((IOleClientSite*)z_pOleClientSite);
   z_pOleObject-> DoVerb(OLEIVERB_SHOW,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
   z_nActive = TRUE;
   z_pSubMenu = z_pMenu-> GetSubMenu(1);
   z_pAddMenu = new CMenu;
   z_pAddMenu-> LoadMenu(IDR_MENU2);
   z_pPopMenu = z_pAddMenu-> GetSubMenu(0);
   z_sName = (char*) malloc(10);
   z_pOleObject-> GetUserType(0,& z_sName);
   z_pSubMenu-> AppendMenu(MF_ENABLED|MF_STRING|MF_POPUP,(UINT)z_pPopMenu-> m_hMenu,z_sName);
   free(z_sName);
 }
 
 void MFrameWnd::PrimaryVerb()
 {
   z_pOleObject-> DoVerb(OLEIVERB_PRIMARY,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }
 
 void MFrameWnd::ShowVerb()
 {
   z_pOleObject-> DoVerb(OLEIVERB_SHOW,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }
 
 void MFrameWnd::OpenVerb()
 {
   z_nActive = FALSE;
   z_pOleObject-> DoVerb(OLEIVERB_OPEN,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }
 
 void MFrameWnd::HideVerb()
 {
   z_nActive = FALSE;
   z_pOleObject-> DoVerb(OLEIVERB_HIDE,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }
 
 void MFrameWnd::UIActivateVerb()
 {
   z_nActive = TRUE;
   z_pOleObject-> DoVerb(OLEIVERB_UIACTIVATE,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }
 
 void MFrameWnd::PropertiesVerb()
 {
   z_pOleObject-> DoVerb(OLEIVERB_PROPERTIES,0,(IOleClientSite*)z_pOleClientSite,-1,m_hWnd,& z_Rect);
 }      
View the pop-up under the main menu-option Edit.... There will be a 
pop-up Button Control added to the end. This pop-up menu will hold six 
verbs that can be invoked on the OCX. See the effect of the different 
verbs on the OCX.
In ObjCreate(), we retrieve a handle to the 
pop-up associated with the main menu option Edit.... To the end of this 
pop-up we add a pop-up of the verbs. This pop-up is a part of  a menu 
resource; IDR_MENU2; in the RC file of  the container. A 
handle to this pop-up menu is obtained and is used in AppendMenu(). 
We wanted the text for this pop-up to indicate the name of the OCX 
used. This name is obtained from the OCX with the help of the 
IOleObject function GetUserType(). When we call the function, the 
OCX fills up a previously allocated string  with it's name. We 
append this string as the pop-up to the existing menu. We use the message map 
 entries  to trap messages from this pop-up and associate functions 
with them. All that these functions do is they call DoVerb() with the 
appropriate verb. 
It is interesting to see the effect of the 
OLEIVERB_PROPERTIES verb on the OCX. Selecting the sub-option 
Properties from the pop-up will call the function 
PropertiesVerb(). This will bring up a dialog box  titled `Button 
Control Properties'. What you will see is a sneak preview of a significant 
feature of Windows '95 in action! 
The dialog box that you see is the 
properties dialog box. Associated with each OCX there could be several 
properties,  for example, color. To provide the user with an easy and 
standard method of changing the properties of an object, Microsoft has 
come up with the concept of Property Pages. 
A property page could be 
seen as a dialog box in which all the required information about one or more 
properties is available. For example, a property page dealing with fonts has 
information about the current font, point size, nature of font (bold, 
 italics, etc.) and so on displayed in it. Naturally, this information can 
be modified. 
Some commonly used properties such as color, font etc. have 
been provided with standard property pages by Microsoft. For other 
properties unique property pages can be designed and implemented. A collection 
of all the property pages associated with a particular OCX is the 
properties dialog box for that OCX. 
In the `Button Control 
Properties' dialog box, there are two rectangles at the top titled 
`General' and `Fonts' respectively. These are called tabs. 
Clicking on any one of them brings up the property page associated with the 
title. By default, in this case, the dialog box displays the property page 
associated with `General'. This property page holds a single edit control 
titled `Caption'. Type in a string into this edit control. Click on the 
`Apply  Now' button  to see the changes made being applied to 
the OCX. In this way, the `Caption' property can be changed from 
the property page entitled `General'. 
Next click on the tab 
`Fonts'. This brings up the property page associated with fonts. This is 
a standard property page provided by Microsoft. 
The properties dialog 
box will also be displayed when we  select the Primary... 
sub-option. Thus, a  DoVerb() with OLEIVERB_PRIMARY as it's 
first parameter will produce the same results as a DoVerb() with 
OLEIVERB_PROPERTIES. Property pages are explained in greater detail later 
when we deal with properties of  the OCX. We reiterate that the 
verbs are to be implemented by the OCX. The container has absolutely 
nothing to do with  the verbs.
A choice in the matter
In the above programs, we had hard-coded the name 
of the OCX to be activated. Ideally, the user should be able to select an 
OCX from those available at run-time.
All the OLE servers are 
registered with `reg.dat', the registration database. This is true of the 
OCXes  too. So,  we can read `reg.dat' to populate a 
listbox with  the names of  the available controls. This will allow 
the user to select the OCX that is to be used.
Registering objects 
with the registration database calls for a bit of history. In the case of 
OLE servers, all you had to do was have an ASCII file with all details of 
the OLE server. This `.reg' file contained information like the 
CLSID of the server, the name of the server, the name of the 
`.exe' or `.dll' file, the verbs and so on. The `.reg' file 
used a program called REGEDIT to register the OLE server 
 with the registration database, `reg.dat'.
REGEDIT does 
not resort to any mysterious mantra. It merely uses standard 
Windows API like `RegOpenKey()', `RegSetKey()' and so on to 
write to the registration database. 
Displaying all the available OLE 
servers in a container program was child's play with the OLE UI 
function OleUIInsertObject(). On executing this function the 
OLE servers are displayed in a standard dialog box titled `Insert 
Object'.
With the advent of OCXes, this standard `Insert 
 Object' dialog has undergone a sea-change as seen in 
 Microsoft Access 2.0. In the new `Insert Object' dialog box 
an additional radio-button is provided for OCXes. Selecting  this 
radio-button enables us to exclusively see the registered 
OCXes.
However, there is no documentation on how this new `Insert 
Object' dialog box can be obtained and made a part of the OCX 
container. There must be something in the OCX registration code 
that differentiates it from an conventional OLE server.
We once again 
had to turn our attention to the OCX samples to understand the 
registration code. But in this particular  case the OCX samples too 
were no big help. All OCX samples provided with Visual C++ use the 
MFC. They have been generated using the Control Wizard. These 
applications  have  OCX registration code built into them and 
hence do not require a separate `.reg' file.
Well, our 
never-say-die spirit carried us through  this. After sifting through 
the code in the OCX samples we isolated the code used by an OCX to 
register itself. A single helper function; AfxOleRegisterControlClass(). 
We converted this function to the basic API registration functions only to 
realise that the only difference between the information in the registration 
code for an OCX and that in a `.reg' file for an OLE server 
is the presence of the keyword `Control'. 
Thus, we can easily 
have a `.reg' file to register an OCX which has the line 
HKEY_CLASSES_ROOT\CLSID\TheActualCLSIDOfTheControl\Control in addition to all 
the details that are normally present in  the `.reg' file for an 
OLE server.
In the following program we have created our own 
`Insert Control' dialog box which displays the names of only the 
OCXes from the registration database. 
Program 5
 void MFrameWnd::InsertControl()
 {
   MDialog z_Dialog(IDD_DIALOG1);
 
   z_Dialog.DoModal();
 }
 
 MDialog::MDialog(int ID):CDialog(ID)
 {
   z_nID = ID;
 }
 
 BOOL MDialog::OnInitDialog()
 {
   CDialog::OnInitDialog();  
   if(z_nID==IDD_DIALOG1)
   {
     long z_lCount = 0;
     CListBox *z_pListBox = (CListBox*)GetDlgItem(IDC_LIST1);
       
     while(1)
     {
       HKEY z_Root,z_Root1,z_Root2;
       char *z_sName,*z_sName1,*z_sName2;
       long z_lCopied;
       
       z_sName = (char*) malloc(100);
       z_sName1 = (char*) malloc(100);
       z_sName2 = (char*) malloc(100);
       z_lCopied = 100;
       RegOpenKey(HKEY_CLASSES_ROOT,"",& z_Root);
       if((RegEnumKey(z_Root,z_lCount,z_sName,100)))
         break;
       z_lCount++;
       strcpy(z_sName2,z_sName);                
       strcpy(z_sName1,z_sName);
       strcat(z_sName1,"\\CLSID");
       RegOpenKey(HKEY_CLASSES_ROOT,z_sName1,& z_Root1);
       RegQueryValue(z_Root1,0,z_sName,& z_lCopied);
       z_lCopied = 100;
       strcpy(z_sName1,"CLSID\\");
       strcat(z_sName1,z_sName);
       strcat(z_sName1,"\\Control");
       if(!RegOpenKey(HKEY_CLASSES_ROOT,z_sName1,& z_Root2))
       {
         RegQueryValue(z_Root,z_sName2,z_sName,& z_lCopied); 
         z_sPrgID.SetAt(z_sName,z_sName2);     
         z_pListBox-> AddString(z_sName);
       }  
       free(z_sName);
       free(z_sName1);
       free(z_sName2);
       RegCloseKey(z_Root);
       RegCloseKey(z_Root1);
       RegCloseKey(z_Root2);
     }           
     z_pListBox-> SetCurSel(0);
     return TRUE;                        
   }                      
   return TRUE;
 }
 
 void MDialog::OnOK()
 {
   if(z_nID == IDD_DIALOG1)
   {
     char *z_sPID;
     CListBox *z_pListBox;
     int z_nIndex;
     CString z_sTemp,z_sString;
     
     z_pListBox = (CListBox*)GetDlgItem(IDC_LIST1);
     z_nIndex = z_pListBox-> GetCurSel();  
     z_pListBox-> GetText(z_nIndex,z_sString);
     z_sPrgID.Lookup(z_sString,z_sTemp);
     z_sPID = z_sTemp.AllocSysString();    
     EndDialog(0);
     CLSIDFromString(z_sPID,& z_Clsid);
     z_pFrameWnd-> ObjCreate(z_Clsid);            
     ::SysFreeString(z_sPID);
   }       
   else
     EndDialog(0);
 }
When we select the menu-option Insert  Control..., we create 
an MDialog object and pass it the ID of the dialog resource to be 
associated with it. The ID is stored in the global variable z_nID. Next, 
the CDialog function DoModal() is used to display the dialog 
box.
In OnInitDialog(), we use the registration database functions of 
the Windows API to read `reg.dat'. RegOpenKey() is 
used to obtain a handle to required keys. Then, we use RegEnumKey() to 
access each individual sub-key.
The while loop reads individual 
entries in `reg.dat' and uses suitable string concatenation to read the 
CLSID. Using the CLSID, we determine whether the entry is an 
OCX by searching for the keyword Control.
If it is then the 
CMapStringToString object, z_sPrgID, is used to store both the 
human readable name of the  OCX and the ProgID of  the 
OCX as strings. CMapStringToString one of  the MFC collection 
classes. The name acts as the key in the map which stores the ProgID as 
the value. In addition, the name of the OCX is added as an element in the 
listbox of the dialog. Thus, the listbox is populated with the names of all 
 registered OCXes.
When  the user selects an OCX and 
clicks on the  OK button, OnOK() gets executed. We determine 
the OCX selected by the user in the list box and search z_sPrgID 
for the allied ProgID. CLSIDFromString() will search 
`reg.dat' for the CLSID associated with this ProgID. The 
CLSID is then passed to ObjCreate() where it is used by 
CoGetClassObject() to activate the OCX.
Continue