ActiveX and the VCL (part 1)
Go to part 2
Introducing the wizards
The purpose of these articles is to provide the reader with the background
information necessary to create and use ActiveX and COM servers with Borland
C++ Builder 4. The main focus of part 1 is on the code used and generated
by the ActiveX wizards. This article assumes familiarity with DLLs
and COM. For a good book on these topics, see "Inside COM" by Microsoft
Press.
In-process Servers
An in-process server is a DLL that serves one or more COM components.
It's referred to as in-process because, when DLLs, are loaded by an application's
process, they are placed within that process' address space. To create
an in-process COM server in C++ Builder, go to File | New, and on the ActiveX
tab, choose ActiveX Library. Two units are created: Project1.cpp
and Project1_ATL.cpp. These two units manage most of the details
associated with COM servers such as, component registration, the DLL's
lifetime control, and serving the component classes implemented in the
server. C++ Builder's COM wrappers are built upon the Active Template
Library (ATL). The ATL is Microsoft's template class library that
removes much of the repetitive tasks involved in building COM servers.
For example, the ATL provides a template class that implements a generic
version of IUnknown (CComObjectRootEx). The ATL also provides a template
class that implements the methods that are exported by an in-process COM
server (CComModule). The ATL was specifically designed to be used
by the Visual C++ ActiveX wizards, therefore, documentation on the ATL
nearly always assumes you're using VC++.
The ATL and the ATLVCL source files are located in "[BCB]includeatl".
I refer to C++ Builder's ATL wrapper as ATLVCL. The ATL consists
of the following files:
-
statreg.h/cpp : Performs high level registration key parsing. (CRegObject
and CRegParser).
-
atlbase.h : Contains declarations of many supporting classes such
as smart pointers, BSTR and VARIANT wrappers, threading model classes,
and declarations for global ATL functions (_ATL_MODULE, CComPtr, CComQIPtr,
CComBSTR, CComVariant, CComCriticalSection, CComMultiThreadModel, etc.).
-
atliface.h : Generated from atliface.idl. Contains the declaration
for IRegistrar, implemented by component CLSID_Registrar. This component
is served by atl.dll. To avoid requiring the distribution of this
DLL, the ATLVCL wrappers duplicate its functionality. Therefore,
atliface.h is rarely ever used in conjunction with C++ Builder.
-
atlcom.h : This file has most of the ATL declarations including
classes and functions for reference count debugging, component creation
via CreateInstance, and implementing IUnknown, IClassFactory, and IDispatch
(CComObjectRootEx, CComCreator, CComClassFactory, IDispatchImpl, etc.).
-
atlconv.h/cpp : Various functions for converting and copying strings
(e.g. converting from ANSI to UNICODE, copying OLESTR strings, etc.).
-
atlctl.h/cpp : Helper classes for implementing ActiveX controls.
(CComControl, CComDispatchDriver, IPersistImpl, IPersistStorageImpl, IPersistPropertyBagImpl,
etc.).
-
wtypes.h : Generated from wtypes.idl. Contains declarations
of many Win32 types to allow these types to be used by automation servers.
-
atlwin.h/cpp : Contains generic implementations of windows and dialog
boxes used with out-of-process COM servers.
-
build_.h : Contains macros for the current ATL build version.
-
atlimpl.cpp : Implements most of the global functions and some class
methods declared in the ATL header files.
The ATLVCL consists of the following:
-
atlmod.h : Contains TATLModule (derived from CComModule) and registry
manipulation classes for components. TATLModule provides extra functionality
for out-of-process servers in addition to CComModule's functionality.
The registry manipulation template classes, TRegistrarBaseT, TComServerRegistrarT,
TTypedComServerRegistrarT, TRemoteDataModuleRegistrar, and TAxControlRegistrar,
provide the same functionality as component CLSID_Registrar, served by
atl.dll.
-
atlvcl.h/cpp : Contains helper classes for VCL-specific ActiveX
controls. (IDataBrokerImpl, TVCLComControl, TVCLControlImpl).
The Project1_ATL source unit created for an ActiveX Library provides
the necessary ATL and ATLVCL headers and source files for the DLL.
Project1.cpp is the main source file for the DLL and exports the following
functions required for an in-process COM server.
-
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
-
STDAPI __export DllCanUnloadNow(void)
-
STDAPI __export DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID*
ppv)
-
STDAPI __export DllRegisterServer(void)
-
STDAPI __export DllUnregisterServer(void)
Found near the top of this file are the following lines:
TComModule Project1Module;
TComModule &_Module = Project1Module;
This creates a global instance of TComModule and assigns it to the
reference _Module. _Module is used in all ATL and ATLVCL source files
to refer to the single global instance of the CComModule-derived class.
This name must never be changed. Before continuing, save this project
as "Server.bpr".
Bare bones COM object
Before creating any components, you should save the library to specify
the name of the DLL. To create a bare bones COM object (bare bones
meaning it's not an automation server and all interfaces derive only from
IUnknown), go to File | New, select the ActiveX tab and choose COM Object.
The New COM Object dialog appears. "CoClass Name" refers to the name
of the component. The wizard will append this name to CLSID_ for
the CoClass identifier and will create a default interface named IName,
where "Name" is the CoClass Name. The "Threading Model" can be chosen
here and it can be changed later. The Threading Model edit box simply
tells the server what value to give the Threading Model registration sub
key for this component. It is still up to the component designer
to ensure that the component follows the model it claims to support in
the registry. The "Description" text is used as a human readable
string associated with this component's CLSID. This can be changed
later as well. For this example, specify the CoClass Name as "StandardCOM".
After creating this COM object, the type library editor is displayed
with two entries: one for the CoClass and one for the CoClass' default
interface. Select the IStandardCOM interface and go to the Flags
page. Uncheck Dual and Ole Automation. Return to the Attributes
page and change the Parent Interface to IUnknown. What we have now,
is a component (CLSID_StandardCOM) which implements an interface (IStandardCOM)
that derives only from IUnknown.
Three files have been added to the project: the Server_TLB source unit,
the Server.tlb type library, and StandardCOMImpl source unit. The
Server_TLB unit contains declarations for all GUIDs and interfaces that
the COM server implements and that are required by clients to access the
server. Development environments such as C++ Builder, Delphi, JBuilder,
and Visual C++, can import type libraries, allowing them to automatically
generate the Server_TLB source unit compatible with that particular development
language and environment. Any changes made to the type library will
be reflected in the Server_TLB unit. A consequence of this in C++
Builder, is that you cannot make changes to Server_TLB.h or Server_TLB.cpp
and expect them to remain. The StandardCOMImpl source unit implements
IStandardCOM. We are free to edit both the CPP and H file of this
unit.
At this point, you can open the type library and add methods to IStandardCOM
that will be implemented in StandardCOMImpl.h
Automation COM Object
An automation object is a component that implements IDispatch.
IDispatch provides methods that allow scripting languages and applications
such as MS-Word to access a COM Object. IDispatch uses a form of
run-time type checking at the expense of speed. This makes development
of components much more difficult for the developer, while making it easier
for those who use macro and interpretive languages. Fortunately,
C++ Builder handles all the tedious details of automation components for
us. To create an automation object, go to File | New, and on the
ActiveX tab, choose Automation Object. Use "AutomationCOM" for the
CoClass Name.
Another source unit, AutomationCOMImpl, is added to the project and
two entries, AutomationCOM and IAutomationCOM, are added to the type library.
This time, we're going to leave the Parent Interface as IDispatch and Dual
and Ole Automation will remain checked. The Dual flag indicates that
this component is to be accessible through automation and the virtual function
table, thus allowing languages such as C++ and Object Pascal efficient
access (through the vtable) and allowing scripting languages access at
all (through the slow IDispatch interface).
ActiveX Control
The term, ActiveX, is thrown around quite loosely these days.
Often the term is interchangeable with COM and OLE. For a little
clarification, COM is the underlying technology on which ActiveX and OLE
are both based. COM is predominantly a means of software versioning
and maintaining strict implementation independence. OLE refers to
all of it. It encompasses COM, automation, document embedding, etc.
An ActiveX control is a component that implements a specifically defined
set of interfaces, allowing it to be embedded in a document or application
that can act as a container, such as a web browser. Some of these
interfaces are IOleObject, IOleInPlaceObject, IOleControl, IPersistStream,
etc. An ActiveX control has a key in the registry named "Control".
More on component registration later in these articles.
To create an ActiveX control, go to File | New, select the ActiveX tab
and choose ActiveX Control. ActiveX controls in C++ Builder are based
upon a VCL control that is already implemented. The VCL Class Name
drop down list box has every VCL component that derives from TWinControl
as well as any custom components that exist in the current application.
For this example, choose TDateTimePicker. (Why? I don't know.)
Another implementation unit, DateTimePickerImpl, is added to the project.
The type library has several additions to it. Two interfaces are
implemented by the component, IDateTimePickerX (for the properties and
methods) and IDateTimePickerEventsX (for the events). Enumeration
types have been added for all enumerations used by TDateTimePicker: TxDTCalAlignment,
TxDTDateFormat, TxDTDateMode, TxDragMode, TxImeMode, and TxDateTimeKind.
Notice that the the project manager now displays "Server.ocx" as the
root of the project as opposed to "Server.dll". This is the file
extension used by ActiveX controls.
ActiveForm
This is just a special ActiveX control that uses either TActiveForm
or another TCustomActiveForm-derived VCL component for its implementation.
Go to File | New, ActiveX, ActiveForm. A form appears, allowing full
use of the form designer and object inspector when creating the control.
Limitations
Before continuing our exploration of the code generated by the wizards,
I'd like to point out some inherent limitations with using the wizards.
Unfortunately, automating any programming task involves imposing limitations
on the programmer. You can't avoid it. Some of these limitations
can be sidestepped with simple workarounds, while others are nearly impossible
to provide a solution for. Personally, I've gone to great lengths
attempting to raise some of the restrictions and have discovered that,
more often than not, you just have to roll with them.
One of the more obvious limitations is that, even when not using OLE
automation, the programmer is restricted to using only automation compatible
types for interface methods. Therefore, using a REFGUID (typedefed
as GUID& in objbase.h) is just not possible, since a C++ reference
is not automation compatible. Of course the simple solution is to
use a GUID* instead. This limitation also means that you cannot place
"#include <windows.h>" at the top of Server_TLB.h and expect to use
the structures and types defined in it. That #include will be erased
the next time C++ Builder decides to regenerate the Server_TLB unit. Or
perhaps you want to add your own types and structures. You'll run
into the same problem. If these types are defined in a header file,
they must be added to the type library manually before they can be used.
Pointers to pointers also are tricky because the wrappers force a smart
pointer on you, altering the type to a pointer to a class (which implements
a smart pointer). Another side affect of this limitation is a loss
of compile time checking for more complex types. For example, if
you want to pass function pointers to interface methods, you can do so
by casting to a PVOID (which is automation compatible), but no type checking
is enforced. This limitation isn't very restrictive when you start
building an entire COM application from scratch using the wizards and tools,
but you run into it often when porting non-COM applications to COM applications.
Another restriction is that multiple inheritance cannot be used for
interface declarations. The type library editor allows only one parent
interface and changing Server_TLB.h will not help you once again.
However, a component can implement multiple interfaces, but to jump from
one implemented interface to another, QueryInterface must be called, even
when calling methods through the vtable and not through IDispatch.
Allowing multiple inheritance in the interface declaration could provide
a performance boost for APIs written in and used by languages that support
multiple inheritance.
Most of the limitations exist when attempting to use language specific
features in a server for language specific clients. Although COM
is actually intended as a language independent solution, there are many
other benefits of COM to be reaped even when not building language independent
software. Workarounds for these restrictions will be covered throughout
these articles.
The Component Object Map
If you've created each of the objects discussed in this article, you'll
have the following code near the top of Server.cpp:
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_StandardCOM, TStandardCOMImpl)
OBJECT_ENTRY(CLSID_AutomationCOM, TAutomationCOMImpl)
OBJECT_ENTRY(CLSID_DateTimePickerX, TDateTimePickerXImpl)
OBJECT_ENTRY(CLSID_ActiveFormX, TActiveFormXImpl)
END_OBJECT_MAP()
These macros create an array of _ATL_OBJMAP_ENTRY structures, called
ObjectMap, which is passed to the TComModule::Init method when the DLL
is loaded. The definitions of these macros are found in atlcom.h
and the _ATL_OBJMAP_ENTRY structure can be found in atlbase.h. Following,
is the definition of _ATL_OBJMAP_ENTRY:
typedef HRESULT (WINAPI _ATL_CREATORFUNC)(void* pv, REFIID riid,
LPVOID* ppv);
typedef HRESULT (WINAPI _ATL_CREATORARGFUNC)(void* pv, REFIID riid,
LPVOID* ppv, DWORD dw);
typedef HRESULT (WINAPI _ATL_MODULEFUNC)(DWORD dw);
typedef LPCTSTR (WINAPI _ATL_DESCRIPTIONFUNC)();
struct _ATL_OBJMAP_ENTRY {
const CLSID* pclsid;
HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister);
_ATL_CREATORFUNC* pfnGetClassObject;
_ATL_CREATORFUNC* pfnCreateInstance;
IUnknown* pCF;
DWORD dwRegister;
_ATL_DESCRIPTIONFUNC* pfnGetObjectDescription;
HRESULT WINAPI RevokeClassObject() {
return CoRevokeClassObject(dwRegister);
}
HRESULT WINAPI RegisterClassObject(DWORD dwClsContext,
DWORD dwFlags) {
IUnknown* p = NULL;
HRESULT hRes = pfnGetClassObject(pfnCreateInstance,
IID_IUnknown,
(LPVOID*) &p);
if (SUCCEEDED(hRes))
hRes = CoRegisterClassObject(*pclsid, p,
dwClsContext,
dwFlags, &dwRegister);
if (p != NULL)
p->Release();
return hRes;
}
};
This structure contains pointers to functions that perform tasks specific
to each component implementation, such as updating the registry, obtaining
the class factory, and creating an instance of the component. Following
is the code created by expanding the object map macros in Server.cpp:
static _ATL_OBJMAP_ENTRY ObjectMap[] = {
{&CLSID_StandardCOM, &TStandardCOMImpl::UpdateRegistry,
&TStandardCOMImpl::_ClassFactoryCreatorClass::CreateInstance,
&TStandardCOMImpl::_CreatorClass::CreateInstance,
NULL, 0,
&TStandardCOMImpl::GetObjectDescription},
{&CLSID_AutomationCOM, &TAutomationCOMImpl::UpdateRegistry,
&TAutomationCOMImpl::_ClassFactoryCreatorClass::CreateInstance,
&TAutomationCOMImpl::_CreatorClass::CreateInstance,
NULL, 0,
&TAutomationCOMImpl::GetObjectDescription},
{&CLSID_DateTimePickerX, &TDateTimePickerXImpl::UpdateRegistry,
&TDateTimePickerXImpl::_ClassFactoryCreatorClass::CreateInstance,
&TDateTimePickerXImpl::_CreatorClass::CreateInstance,
NULL, 0,
&TDateTimePickerXImpl::GetObjectDescription},
{&CLSID_ActiveFormX, &TActiveFormXImpl::UpdateRegistry,
&TActiveFormXImpl::_ClassFactoryCreatorClass::CreateInstance,
&TActiveFormXImpl::_CreatorClass::CreateInstance,
NULL, 0,
&TActiveFormXImpl::GetObjectDescription},
{NULL, NULL, NULL, NULL}
};
Each class implementation (e.g. TStandardCOMImpl) provides static methods
for updating the registry, creating instances of the class factory, etc.
The global instance of TComModule (_Module) uses this ObjectMap array to
implement the in-process COM server's exported functions, as described
in the next section.
Exports
DllCanUnloadNow
TComModule contains a member (LONG m_nLockCnt) that is incremented
and decremented by calling TComModule::Lock and TComModule::Unlock.
DllCanUnloadNow simply checks if this member is zero and, if it is, returns
true, indicating that there are no locks on this module. TComModule::Lock
and TComModule::Unlock are called in two circumstances: 1) when a COM object
is created or destroyed (including implementations of IClassFactrory),
and 2) when IClassFactory::LockServer is called.
DllGetClassObject
This exported function calls TComModule::GetClassObject, providing
the CLSID of the component, the IID used to communicate with the class
factory (either IID_IUnknown, IID_IClassFactory, or IID_IClassFactory2),
and the address of the pointer that will receive the class factory interface.
When TComModule::GetClassObject is called, the module loops through all
entries in the object map array, looking for the entry that has the requested
CLSID. If and when it is found, the class factory is created (if
it hasn't been created for this CLSID already) by calling the function
pointed to by the pfnGetClassObject member of the _ATL_OBJMAP_ENTRY structure.
This method is provided by the CComCoClass template, which is a base class
of the implementation class (e.g. TStandardCOMImpl).
DllRegisterServer and DllUnregisterServer
These methods call TComModule::RegisterServer and TComModule::UnregisterServer.
Both of these methods loop through all entries in the _ATL_OBJMAP_ENTRY
array, calling the function pointed to by the pfnUpdateRegistry member.
This function is provided by each implementation class (e.g. TStandardCOMImpl).
The UpdateRegistry methods can be found in the header files of each COM
implementation. Following is the code for TStandardCOMImpl::UpdateRegistry:
// Data used when registering Object
//
DECLARE_THREADING_MODEL(otBoth);
DECLARE_PROGID("Server.StandardCOM");
DECLARE_DESCRIPTION("Standard COM Object, no automation");
// Function invoked to (un)register object
//
static HRESULT WINAPI UpdateRegistry(BOOL bRegister)
{
TTypedComServerRegistrarT<TStandardCOMImpl>
regObj(GetObjectCLSID(), GetProgID(), GetDescription());
return regObj.UpdateRegistry(bRegister);
}
TTypedComServerRegistrarT is one of many class templates where all the
action takes place when registering and unregistering COM objects.
Other classes that perform similar functionality are TComServerRegisrarT,
TRemoteDataModuleRegistrar, and TAxControlRegistrar. Each of these
classes derives from TRegistrarBaseT and can be found in atlmod.h.
The DECLARE_**** macros provide implementations for the GetObjectCLSID,
GetProgID, and GetDescription methods in the component implementation class.
Type Library Source
The Server_TLB.h file contains declarations for all interfaces and
GUIDs used in the COM server and required by client applications.
Wrapper classes are also provided for clients using C++ Builder that simplify
the creation and use of components. Server_TLB.cpp contains the GUID
definitions that are referenced throughout the source files. Remember
that changes made to this file will be lost since these files are periodically
updated from the type library.
Component Implementation Source
These units, such as TStandardCOMImpl, contain the actual implementation
of each component. Each implementation derives from several ATL and
ATLVCL class templates depending on the type of component (e.g. COM object,
Automation Object, ActiveX Control, etc.) and other various attributes
of the component (e.g. Thread Model, Aggregatable, etc.). Each component
implementation has a COM interface map associated with it declared similar
to the following:
BEGIN_COM_MAP(TStandardCOMImpl)
COM_INTERFACE_ENTRY(IStandardCOM)
END_COM_MAP()
This map declares methods that manage an array of the interfaces implemented
by the component for use with QueryInterface calls. These macros
are defined in atlcom.h and expand to the following:
public:
typedef TStandardCOMImpl _ComMapClass;
static HRESULT WINAPI _Cache(void* pv, REFIID
iid,
void** ppvObject,
DWORD dw) {
_ComMapClass* p = (_ComMapClass*)pv;
p->Lock();
HRESULT hRes = CComObjectRootBase::_Cache(pv,
iid,
ppvObject,
dw);
p->Unlock();
return hRes;
}
IUnknown* GetUnknown() {
_ASSERTE(_GetEntries()[0].pFunc
== _ATL_SIMPLEMAPENTRY);
return (IUnknown*)((int)this+_GetEntries()->dw);
}
HRESULT _InternalQueryInterface(REFIID iid,
void** ppvObject) {
return InternalQueryInterface(this,
_GetEntries(),
iid, ppvObject);
}
const static _ATL_INTMAP_ENTRY* WINAPI _GetEntries()
{
static const _ATL_INTMAP_ENTRY
_entries[] = {
DEBUG_QI_ENTRY(TStandardCOMImpl)
{&IID_IStandardCOM,
offsetofclass(IStandardCOM, _ComMapClass),
_ATL_SIMPLEMAPENTRY},
{NULL, 0, 0}
};
return _entries;
}
The important thing to understand is that whenever QueryInterface is
called on this component, _GetEntries is called which returns an array
of all interfaces implemented by this component. This array is used
to determine if the interface requested via QueryInterface is implemented
and, if so, a pointer to the interface is returned.
StandardCOM
The implementation class for this component has the following declaration:
class ATL_NO_VTABLE TStandardCOMImpl :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<TStandardCOMImpl,
&CLSID_StandardCOM>,
public IStandardCOM
CComObjectRootEx implements IUnknown using the CComSingleThreadModel
if the Apartment Thread Model was chosen or CComMultiThreadModel if either
the Free or Both Thread Models are chosen. The thread model simply
determines how the reference count for each component's interfaces are
handled. However, the component's implmentation must adhere to the
threading model chosen and regsitered for the component.
CComCoClass implements the class factory and handles implementation
details of aggregatable components.
AutomationCOM
The implementation class for this component has the following declaration:
class ATL_NO_VTABLE TAutomationCOMImpl :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<TAutomationCOMImpl,
&CLSID_AutomationCOM>,
public IDispatchImpl<IAutomationCOM,
&IID_IAutomationCOM,
&LIBID_Server>
Instead of deriving directly from IAutomationCOM, this class derives
from a class template which implements IDispatch.
DateTimePickerX
The implementation class for this component has the following declaration:
class ATL_NO_VTABLE TDateTimePickerXImpl:
VCLCONTROL_IMPL(TDateTimePickerXImpl,
DateTimePickerX,
TDateTimePicker,
IDateTimePickerX,
DIID_IDateTimePickerXEvents)
The VCLCONTROL_IMPL macro expands to the following:
public TVclControlImpl<TDateTimePickerXImpl,
TDateTimePicker,
&CLSID_DateTimePickerX,
&IID_IDateTimePickerX,
&DIID_IDateTimePickerXEvents,
LIBID_OF_DateTimePickerX>,
public IDispatchImpl<IDateTimePickerX,
&IID_IDateTimePickerX,
LIBID_OF_DateTimePickerX>,
public TEvents_DateTimePickerX<TDateTimePickerXImpl>
TVclControlImpl implements all interfaces required by ActiveX controls
(e.g. IUnknown, IPersistStorage, IOleInPlaceActivateObject, ISimpleFrameSite,
etc.) as well as the class factory for the component.
ActiveFormX
This component's implementation unit contains both the ActiveForm derived
VCL form class (TActiveFormX) and the ActiveX control object that wraps
the form into a COM interface (TActiveFormXImpl). All other details
are similar to DateTimePickerX.
Out-of-process Servers
Out-of-process servers are primarily used when there is a pre-existing
EXE application and the developer wants to allow third party client applications
to use the services provided by the EXE. Examples of this are Word
and Excel. When these applications are launched, they register their
components with the OLE function CoRegisterClassObject. This allows
other applications to connect to its COM interfaces. Unlike in-process
servers, applications cannot export a DllGetClassObject function.
If a client calls CoCreateInstance requesting a component implemented by
an out-of-process server, the application is automatically started and
the interface is returned. Also, if the local server was launched
via CoCreateInstance, and the client app releases all references to its
components, the local server application automatically shuts down.
Out-of-process servers can be multiple use servers or single use servers.
With single use servers, a different instance of the server application
is started for every client application requesting its services.
While multiple use servers allow multiple connections to its components.
The Project Options | ATL tab allows changing this setting.
Local server registration is accomplished by passing the /REGISTER or
/UNREGISTER command-line arguments when executing the application.
If either of these arguments is supplied, the application will quit as
soon as the registration process is complete.
All of the functionality described above is implemented by the VCL's
TATLModule. All other details of component servers are the same for
out-of-process and in-process servers.
To create an out-of-process server, open any application (or create
a new one) and go to File | New, select the ActiveX tab and choose COM
Object. The type library editor appears and its source files (*_TLB)
are added to the project along with the implementation unit. These
files are identical to those in an in-process server. Out-of-process
servers are initialized slightly differently than in-process servers.
When the global TComModule (_Module) is constructed in the application's
main source file, a zero is passed to the constructor. This argument
can be a TProcedure component that is called during system initialization
(during the call to TApplication::Initialize() at the beginning of WinMain).
The default system initialization procedure used when no procedure is provided
to the constructor of TComModule is TATLModule::InitATLServer. Inside
this function is a call to _Module::Init with ObjectMap and Sysinit::HInstance
provided as arguments.
Summary
This article, although it didn't go very in-depth into any one ActiveX
topic, provided much of the background information revolving around the
code used and generated by C++ Builder's ActiveX wizards. At this
point, the reader should have enough familiarity with the wizards to be
aware of their attributes and limitations. The reader should also
be comfortable enough with the ATL and ATLVCL source files to learn more
details about how ActiveX components are implemented. The next article
in this series discusses the various means of building applications that
connect to COM servers. Topics that will be covered include importing
type libraries, the Interface Description Language (IDL), and the VCL's
COM interface wrappers.
Connect with Us