All Topics, MFC / C++
>> COM/DCOM/COM+
>> ActiveX

Overriding MFC's Default Client
Site to Implement IServiceProvider
By R
Hartman

Describes
how to override the default control container provided by MFC
in order to provide a custom client site for provisioning of
services to ActiveX controls hosted by the
container.

Advanced

C++ (VC7, VC6)
Windows (WinXP,
Win2K, Win2003, Win95, Win98, WinME)
COM, MFC, Win32,
VS
Dev

Posted 6 Oct 2003

Articles
by this author

12,473
views

Search:

Articles Authors

Help!

Articles

Message
Boards

StoreFront

Lounge

Toolbox

Broken
Article?

VS.NET 2003 for
$899

MSDN Univ. from
$2499

Print version

Send
to a friend

Sign in / Sign
up

Email

Password

Remember me

Lost your Password?

7 members have rated this
article. Result:

Popularity:
3.66. Rating: 4.33 out of
5.

Download
source files - 30 Kb

Introduction

This article describes how to override the default client site
implementation provided by MFC for hosting ActiveX controls. At first
glance this may seem a bit arcane, but one of several cases in which you
may find a real need for this is when you are developing an MFC
application and using an ActiveX control that expects a custom service
implementation in the hosting application to be accessible through the
standard OLE control containment framework. You may also find that
understanding this technique is useful in cases where you are the
developer of the ActiveX control and wish to leverage services provided by
the hosting application.

As an example, I once developed an MFC application that used a
third-party ActiveX control that required the hosting application to
implement some authentication-related services and to provide the control
with access to them through IServiceProvider and the control
containment model. This required the sort of override technique described
in this article in order to point the control to a custom client site I
created rather than MFC’s default client site.

For this article, I've modified Microsoft's driller sample
(available at MSDN) to use as an example of how to accomplish the
override. Though the driller sample has been freely available
for some time, the modification and description of it in this article are
useful for two reasons. First, the Microsoft sample is focused much more
on demonstrating the manipulation of an embedded WebBrowser
control than on explaining the mechanics of the control container/client
site override. Second, the limited documentation has very little
explanation of the concepts behind the sample. For these reasons it can be
difficult to understand exactly how the MFC container override is
accomplished and how it can be of use to you in contexts other than using
the WebBrowser control.

Background

First, an important caveat: Microsoft provides no officially supported
means (that I am aware of) for accomplishing what this article describes.
Microsoft offers the driller sample as one possible approach
and I use that, along with some undocumented MFC features, as the basis
for the approach described in this article. Microsoft's documentation of
their sample states that it "is implementation specific to MFC. So, if
future versions of MFC change... this sample (and your code if you use
this technique) might not work. We are looking at possible ways to have
MFC expose the client site for customization." Be careful to consider what
this warning means to you if you are planning to use this approach in your
code.

Control Containers and Client Sites

I won't discuss the details of ActiveX control containment here, but a
few words of background will be helpful for those unfamiliar with the
concept. You can think of a control container as just what the name
implies: a “container” in the hosting application that allows the
application to include ActiveX controls and make meaningful use of them.
For each ActiveX control "contained" by the application, the container
must implement a client site.

A client site provides for communication between a control and the
container. In support of this, client sites implement a number of defined
interfaces that every control knows it can call on in order to report to
the container or to request services from it. Likewise, controls can
implement a number of well-known interfaces that the container can
confidently call through every control’s client site.

This model of controls being sited in containers is as old as OLE, the
technology model from which today's ActiveX controls derive. Though it has
become well hidden for developers over the years, the
control/site/container model remains fundamental to interactions between
controls and their hosts. It is still in place today and you work with it,
either directly or indirectly, every time you use an ActiveX control.

Requesting Service Interfaces Through Client Sites

As in the example I cited at the beginning of this article, it is not
uncommon for ActiveX controls to request access to custom services that
are implemented outside the client site by objects in the hosting
application. A client site can implement IServiceProvider as
a means of handling these requests from a control and then passing service
interfaces from the object back to the control in response to the request.
The control can then communicate directly with the service implementation
in the host application without explicit reference to a client site or
container. To implement this, a control may query its client site for
IServiceProvider and then call
IServiceProvider::QueryService to request a specific service
interface. The control can then use this interface to call on the service.

The difficulty presented by MFC in this case is that the MFC framework
provides a default implementation for container and client site
construction and management with very little exposure of the internals.
Though in most cases this abstraction is quite helpful, more control over
the process is sometimes needed, as in the case of needing to point a
control to a custom client site implementation that handles
IServiceProvider-provisioned service interfaces.

What the Code Sample Demonstrates

The code sample accompanying this article illustrates how to implement
a custom client site in MFC and connect it into the MFC container
framework. It consists of a VC++ 6 workspace (MFC Client Site
Override) containing two projects: an MFC application (Client
Site Sample project) and an ActiveX control (CS Test
Control project). Client Site Sample demonstrates how
to implement the client site and a container manager that allows your site
to substitute for MFC's default implementation. It also demonstrates how
to implement IServiceProvider in the custom client site for
service provisioning. CS Test Control demonstrates how an
ActiveX control might call on your custom client site for a service
interface and is provided so you can see a service call in action as it is
handled by the custom client site in the MFC application.

The Code

The following steps walk through the Client Site Sample
project. Each code listing is followed by a description of the
implementation. Note that to run the code some minor modifications may be
required. See the relevant section
later in this article for information on these modifications.

Step 1: Declare the Custom Client Site

To provide a custom client site requires that we subclass MFC's client
site encapsulation class: COleControlSite. The code in
listing 1 illustrates this.

Listing 1

// custsite.h

...

// declare our custom control site to serve as the client site
class CCustomControlSite:public COleControlSite
{
public:
// constructor associates this site with the container
CCustomControlSite(COleControlContainer *pCnt):COleControlSite(pCnt){}

protected:

// declare the ServiceProvider interface to create a nested class
// XServiceProvider on which we can implement the interface. Note that
// the IUnknown functions are not explicitly declared here because the
// macros automatically declare them for us
DECLARE_INTERFACE_MAP();
BEGIN_INTERFACE_PART(ServiceProvider, IServiceProvider)
// declare the interface method(s)
STDMETHOD(QueryService) (
/* [in] */ REFGUID guidService,
/* [in] */ REFIID riid,
/* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
END_INTERFACE_PART(ServiceProvider)
};

In this code we declare our custom client site,
CCustomControlSite, as deriving from MFC's
COleControlSite. We define the constructor to accept a
pointer to a control container with which the site will be associated, and
to forward the container pointer to the base class.

Step 2: Declare Client Site Interfaces

Now that we have a client site we can declare any interfaces our site
must support. In our example, we want the client site to implement
IServiceProvider to support services queries from controls,
so we include servprov.h and then declare
IServiceProvider and its QueryService member. We
use MFC's interface map macros to simplify things by having MFC provide,
among other things, a standard IUnknown implementation for
us. This step is shown in the latter part of Listing 1.

Step 3: Implement the Client Site

Now we implement the client site. The following code illustrates this.

Listing 2

// custsite.cpp

...

BEGIN_INTERFACE_MAP(CCustomControlSite, COleControlSite)
INTERFACE_PART(CCustomControlSite, IID_IServiceProvider, ServiceProvider)
END_INTERFACE_MAP()

...

// ServiceProvider implementation

...

// IServiceProvider::QueryService
// Description: support queries for service interfaces from controls
STDMETHODIMP CCustomControlSite::XServiceProvider::QueryService(
REFGUID guidService, REFIID riid, void **ppvObject)
{
METHOD_PROLOGUE(CCustomControlSite, ServiceProvider)
*ppvObject = NULL;

// support the SMyService service
if(guidService == SID_SMyService)
{
MessageBox(NULL, "QueryService for SID_SMyService",
"CCustomControlSite::XServiceProvider::QueryService", MB_OK);
// support query for IMyInterface interface
if(riid == IID_IMyInterface)
{
// in a real implementation the interface would be returned via
// ppvObject
MessageBox(NULL, "QueryService for IID_IMyInterface",
"CCustomControlSite::XServiceProvider::QueryService",
MB_OK);
return E_NOINTERFACE;
}
}
return E_NOINTERFACE;
}

Note that the MFC interface macros create a nested class
(XServiceProvider) and we simply implement its members. The
implementation of QueryService examines the service ID and
responds accordingly. We support a fictitious SMyService
service and IMyInterface interface. Note that in a realistic
implementation we would return the interface pointer via
ppvObject, but for simplicity (we have to stop somewhere!)
the example does not. Note that the interface that is returned could be
from any object in the containing application, even from outside the
client site.

Step 4: Implement a Custom Control Container Manager for the Client
Site

MFC manages the container and client site framework through a control
container manager. We must provide MFC with a custom control container
manager by which it can connect our new client site into a container
within the MFC framework. To do so, we subclass MFC's OLE control
container manager class: COccManager. The following code
illustrates this.

Listing 3

// custsite.h
...
// declare our control container manager
class CCustomOccManager :public COccManager
{
public:
CCustomOccManager(){}
// creates an instance of our custom control site and associates it
// with the container
COleControlSite* CreateSite(COleControlContainer* pCtrlCont)
{
CCustomControlSite *pSite = new CCustomControlSite(pCtrlCont);
return pSite;
}
};
...

This code declares our custom control container manager,
CCustomOccManager, as deriving from MFC's
COccManager. We override COccManager::CreateSite
to accept from MFC a pointer to a control container, to create an instance
of our custom client site bound to that container, and to then return a
pointer to the site. Note that an include of occimpl.h is
required to support this.

Step 5: Connect the Custom Client Site to MFC's Container

Now we need to point MFC to our custom client site. We do this by (in
our client site sample application's InitInstance), creating
an instance of our custom container manager and then modifying MFC’s
existing call to AfxEnableControlContainer to use this
instance instead of the default. The following code illustrates this.

Listing 3

// client site sample.cpp

...

// Create a custom control container manager class so we can overide the
// client site
CCustomOccManager *pMgr = new CCustomOccManager;

// Set our control containment up but using our control container
// manager class instead of MFC's default
AfxEnableControlContainer(pMgr);

...

Once this is in place, MFC will use our custom container manager to
create our custom client site, which will in turn be passed in MFC's
internal call to the ActiveX control's SetClientSite in order
to inform the control of its site. This establishes our custom client
site, with IServiceProvider support for custom service
interfaces, as the site for the control. At this point we have
successfully overridden the default client site for controls with our own
client site implementation.

To Run the Sample Code

You can walk through the code to see the interactions between MFC, the
container, the client site, and the control, but there are a few things
you need to set up first.

Modifications and Dependencies

For simplicity, the Client Site Sample project references
absolute paths in the #includes of occimpl.h and you may need to change
these to your VC++ install folder for the code to compile. The project
also uses the CSTest control implemented in the CS Test
Control project to show control/client site interaction, so be sure
to build and register CSTest.

Using the Sample ActiveX Control to Observe the Client Site

I've included the CSTest sample ActiveX control so you can
run the client site sample application and observe MFC's creation of the
custom client site, and also see the control query into the custom site
for IServiceProvider and the IMyInterface custom
service interface. One way to do this is to open the MFC Client Site
Override workspace that includes both the site and control
projects. Build the projects with a debug configuration, point the
CS Test Control project to the Client Site
project's exe for debugging, insert breakpoints in Client
Site at the creation of the control manager in
InitInstance, and in CS Test Control's
SetClientSite function, then run CS Test Control
and step through the code.

The client site sample will display message boxes when the control
queries it, and the control will update its text when it receives word
back that the site supports IServiceProvider. The following
code from the CS Test Control project illustrates how this is
done in the control by overriding the control's
SetClientSite, which is called by the MFC container to notify
the control of the client site that has been created for it. Note that in
a real implementation the control's call to QueryService
would return a pointer to an interface (in pMI) that could be
stored by the control and used at any time to directly call service
functions.

Listing 4

// cstest.cpp
...
STDMETHOD(SetClientSite)(IOleClientSite *pClientSite)
{
if (pClientSite) {
IServiceProvider *pSrvPrv = NULL;
IMyInterface* pMI = NULL;

pClientSite->QueryInterface(IID_IServiceProvider, (void**)&pSrvPrv);

if(pSrvPrv) {
m_bSupportISP = true;
pSrvPrv->QueryService(SID_SMyService, IID_IMyInterface,
(void**)&pMI);
pSrvPrv->Release();
}

if(pMI)
pMI->Release();
}

return IOleObjectImpl<CTEST>::SetClientSite(pClientSite);
}

What About .NET and MFC7?

With MFC7 you accomplish this in essentially the same way, but you do
need to change your occimpl.h include to point to
afxocc.h instead. Also, the CDHTMLDialog
provided in MFC7 provides the very useful CreateControlSite
member that you can override to create custom control sites on a per
control basis when using the DHTML Dialog class.

About R Hartman

Robert is a Principal Engineer at
Vytek, a technology consulting firm based in San Diego, CA (USA)
with product and service offerings ranging from embedded solutions
to enterprise portals.

Click here
to view R Hartman's online profile.

Other popular articles:

Understanding The
COM Single-Threaded Apartment Part 1
Learn
the fundamental principles of the COM Single-Threaded Apartment Model by
code examples.

Automating
Windows Applications
A Windows application
that does not export any program interface, may be converted to
automation server with COM object(s) injected into the application
process.

Reading
and Writing Messages in Outlook Express
This article was done to provide an example of
IStoreNamespace / IStoreFolder.

DCOM
D-Mystified: A DCOM Tutorial, Step 6
We
build our new server and install it on the server
machine.

[Top]

Sign
in to vote for this article:
PoorExcellent

Premium
Sponsor

FAQ

Noise level
Very High High
Medium Low Very
low

Search comments

View Normal
(slow) Preview (slow)
Message View Topic View Thread
View Expanded (Supporters
only)

Per page 10 25
50 (must logon)

New thread

Msgs 1 to 1 of 1 (Total: 1) (Refresh)

First Prev
Next

Subject

Author

Date

URGENT: How to save a CArray into ActiveX
PX_Blob function???

ZeroCoolLP

21:41 1 Feb
'04

I'm
developing a ActiveX in MFC Visual C++ 6.0 (Not
ATL) and I have a serius problem and I can't
find documentation in books or
Internet.

I need to save a CArray or a
dynamic array with typedef structure into the
persistence of an ActiveX (PX_Blob
function).

any body know how to do
this???.

A Thousand Thanks, to all.

Diego

[Reply][Email][View
Thread][Get
Link]

[Modify|Delete]
Score:
2.0 (1 vote).

Last Visit: 11:19 Saturday 5th
February, 2005

First Prev
Next

All Topics, MFC / C++ >> COM/DCOM/COM+ >> ActiveX
Updated: 6
Oct 2003

Article content
copyright R Hartman, 2003
everything else Copyright © CodeProject, 1999-2005.

Advertise on The
Code Project | Privacy

MSDN Communities |
ASPAlliance • Developer Fusion • DevGuru • Programmers Heaven • Planet Source Code • Resource Index • Tek-Tips Forums • What is XML? • VisualBuilder • ZVON • Search Us!