ActiveX Programming Unleashed
ActiveX Programming Unleashed
Chapter 1
An Overview of ActiveX
Chapter 1
An Overview of ActiveX
by Weiying Chen
Microsoft has unveiled an extensive new solution technology for the Internet called ActiveX. Microsoft
ActiveX is a broad and powerful abstraction for Microsoft Internet Solutions. Content providers and
Internet application developers now have a robust and extensible frameworks that enables them to
develop a new generation of Internet applications.
Microsoft is aggressively adding features to the Win32 Application Programming Interfaces (APIs) that
will let developers "Internet-enable" their applications. These new features are based on Object Linking
and Embedding (OLE) technology so that developers who have made investments into Win32 and OLE
ActiveX exposes a set of APIs that enables developing a new generation of client/server applications for
the Internet. ActiveX has interfaces to integrate almost every media technology within an application. It
provides extensive support for animation, 3D virtual reality, real-time audio, and real-time video.
ActiveX gives developers an open framework for building innovative applications for the Internet.
ActiveX technologies form a robust framework for creating interactive content using reusable
components, scripts, and existing applications. Specifically, ActiveX technologies enable content
providers and application developers to create powerful and dynamic Web content and Web server
extensions quite easily. This feat is achieved by using ActiveX controls, Active client and server side
scripts, and the Active document interfaces and ISAPI (Internet Server Application Programming
Interface).
COM (Component Object Model) is the technical cornerstone for the ActiveX technology; it defines how
objects expose themselves for use within other objects and how objects can communicate between
processes and across a network. You can easily integrate COM objects for use in many languages, such
as Java, Basic, and C++. COM objects are reusable binary components.
There are two primary pieces to the ActiveX Object Model: the Microsoft HyperText Markup Language
(HTML) Viewer component (MSHTML.dll) object and the Web Browser Control (shdocvw.dll). Both
are in-process (DLL-based) COM objects.classes.
All interfaces defined in the ActiveX Object Model are "dual" interfaces. A "dual" interface means that
the objects inherit from IDispatch and IUnknown. They can be used by client application at "early-bind"
via Vtable and at "late bind" via OLE automation controller by using IDispatch::GetIdsOfNames and
IDispatch::Invoke.vtable).
MSHTML is the HTML viewer part of Microsoft Internet Explorer 3.0. It is an in-process COM server
and a Document Object. It can be hosted in OLE Document Object containers.
MSHTML implements the OLE Automation object model described in the HTML Scripting Object
Model. With this object model, you can develop rich multimedia HTML content. VBScript running
inline in the HTML and Visual Basic 4.0 running external to the HTML can use the object model.
The Web browser control object is an in-process COM Server. It also serves as a Document Object
container that can host any Document Objects, including MSHTML, with the added benefit of fully
supporting hyperlinking to any document type.
The Web browser control is also an OLE control. The IWebBrowser interface is the primary interface
exposed by the Web Browser Control.
The Web browser control is the core of what customers see as "the Internet Explorer 3.0 product.".
Internet Explorer 3.0 also provides a frame to host this control. Internet Explorer 3.0 supports the
following HTML 3.x0 extensions:
Frame: Creates permanent panes for displaying information, supporting floating frames or
borderless frames..
NOFRAMES: Content that can be viewed by browsers not supporting frames
OBJECT: Inserts an OLE control
TABLE: Fully compliant with HTML 3.x0 tables with cell shading and text wrapping.
StyleSheet: font size, intra-line space, margin, highlighting and other features related with styles
can be specified in the HTML by the user.
In-Line sound and video
ActiveX Controls
ActiveX Control (formerly known as OLE control) has a broader definition. It refers to any COM objects.
For instance, the following objects are all considered an ActiveX control.
Note
ActiveX Control used inside scripting languages make this binary reusable components reused in the
Internet world. Almost any type of media wrapped into an ActiveX control can be seamlessly integrated
into your Web page. Sound, video, animation, or even credit-card approvals controls can be used within
your Web page.
ActiveX Scripting
ActiveX Scripting is the interface for script engines and script hosts. Following this interface
specification, the script vendors can use their custom script engine in any script host, such as IE 3.0.
What's more, with its infrastructure, developers can choose any script language they prefer. A script is
some executable block, such as a DOS batch file, Perl script or an EXE file.
ActiveX Scripting components can be grouped into two major categories: an ActiveX Scripting Engine
and an ActiveX Scripting host. A host creates a script engine so that scripts can run against the host.
The ActiveX Scripting Engine is an OLE COM object that supports the IOLEScript interfaces, with at
least one of the IPersist interfaces and an optional IOleScriptParse interface.
Microsoft ActiveX Scripting Languages products, such as Visual Basic Script and Java Script, can be
used to "glue" together the functionality exposed in ActiveX Controls to create rich Web-based
applications.
Active Documents
Active Documents are based on the OLE Document Objects (DocObjects, for short). The DocObjects
technology is a set of extensions to OLE Compound Document technology. It is the core technology that
makes Microsoft Office Binder work. Active Document also refers to any document that contains
ActiveX controls, a Java Applet, or a Document Object.
One other obvious application for this technology is "Internet browsers." You can open richly formatted
documents, such as Microsoft Word and Excel spreadsheets, directly in the browser.
Figure 1.1 shows how seamlessly the Word document placed in IE 3.0. The word toolbar is added to the
browser, enabling you to work on the document while cruising the Internet.
The following describes the general criteria for the document object container and the document object.
A Document Object container must implement the following objects and interfaces:
Similarly, Document Objects must implement the following objects and interfaces:
Objects
Containers
For more information on the Document Object, please refer to Chapter 19, "OLE Document Objects," in
this book.
The ActiveX Server Framework, another component of ActiveX technologies, is based on the Microsoft
Internet Information Server (IIS). IIS is built on the Windows NT Advanced Server 3.51 or greater. This
framework enables developers to take advantage of the powerful Microsoft Back Office family of
products, such as Microsoft SQL, SNA, Management, and Exchange Server.
Server support consists of ActiveX Server-side scripting and the usage of Aside, Batch, or JavaScript.
The Common Gateway Interface (CGI) is also supported under the ActiveX Server Framework. Common
Gateway Interface is a protocol used to communicate between your HTML forms and your program so
that your program can extract the information from the form. A lot of languages can be used to write
your program, as long as the language has the capability to read the STDIN, write to the STDOUT, and
read the environment variables.
An HTTP server responds to a CGI execution request from a client browser by creating a new process,
reading the input from the form through the environment variable, doing some processing with the form
data and write the HTML response to the STDOUT.
The server creates one process for each CGI request received. However, creating a process for every
request is time consuming and takes a lot of server resources. Using too many server resources can starve
the server itself.
One way to avoid this is to convert the current CGI executable file into a Dynamic Link Library(DLL) so
that the DLL can be loaded into the same address space as the server. The server can load the DLL the
first time it gets a request. The DLL then stays in memory, ready to service other requests until the server
decides it is no longer needed. There are two types of DLLs that can be created for this purpose: one is
the Internet Server application (ISA), the other is the ISAPI filter.
An ISA(Internet server application) is also known as the Internet Server Extension. There are two entry
points for this DLL, GetExtensionVersion and HttpExtensionProc.
HTTP Server first calls the ISA at the entry point of GetExtensionVersion to retrieve the version number
of the ISAPI specification on which the extension is based. For every client request, the
HttpExtensionProc entry point is called. Interaction between an HTTP server and an ISA is done through
extension control blocks (ECBs). The ISA must be multithread-safe because multiple requests can be
received simultaneously.
An ISAPI filter is a replaceable DLL that sits on the HTTP server to filter data traveling between web
browser and HTTL server. Figure 1.2 shows the relationship between the HTTP server, ISA, and ISAPI
filter.
ISAPI filter has the same entry point as the ISA. When the filter is loaded, it tells the server what sort of
notifications it will accept. After that, whenever a selected event occurs, the filter is called and is given
the opportunity to process the event.
Authentication
Compression
Encryption
Logging of HTTP requests
You can also install Multiple filters can be installed on the server. The notification order is based on the
priority specified by the filter, then the load order in the registry, the registry key for filter is under
\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\parameters\Filter DLLs\.
After you install the filter, IIS must be restarted.
To ease the development of the server-side application, Microsoft also provides an Internet
Personalization System (IPS) on top of the IIS. It provides a server side VBScript and a set of server
components and a system installed with IIS on a web server computer to make the server-side scripting
available. With this system installed, you can integrate Visual Basic Script commands and expressions
into the HTML files, and calls out to server side OLE automation objects. When you point your browser
to a script file, this system will process the script and return HTML to the browser. This allows
developers to create more elaborate content with less effort. Server-side scripting will make the server-
side development a piece of cake.
Active Animation (formally known as Active VRML; VRML stands for Virtual Reality Modeling
Language) is an advanced markup language and a viewer for 3D multimedia. The Active Animation
viewer is an ActiveX control and can be used inside client side VBScript in Microsoft Internet Explorer
3.0. Web pages that use Active VRML can include interactive 2D and 3D animation, accompanied by
synchronized sounds. These effects can also be triggered by events from VBScript in IE 3.0 and from
Active Animation synchronizes all media interaction without developers needing to write low-level code.
It supports the operation and media types such as 3D geometry, images, sound, montages, 2D and 3D
points, and vectors, 2D and 3D transforms, colors, numbers, text, strings, characters, and so forth.
Animation: The following is a list of the operations and media types supported by Active
Animation:
Sound: Supports for importing, mixing, and rendering 3D models of sound waves.
Images: Support infinite resolutions, 2D transformation, overlaying, and rendering images from
3D models.
3D geometry: Supports texture mapping of animated images combined with control of color and
opacity, aggregation, and transformations.
Advanced 2D and 3D coordinate systems and transformations: Supports many advanced
manipulations of vector/vector, point/vector, and scalar/vector. Also supports construction and
destruction of rectangular or polar/spherical coordinates. Translate, scale, rotate, shear, inversion,
identity, composition and matrix-based constructions.
Montages: Supports multi-layered cel animation and 2 -D images.
Note
Text: Supports formatted text, its colors and its fonts families including optional bold and italics.
Colors: Construction and destruction in RGB and HSL colors.
The Active Movie is based on the Microsoft Active Movie Streaming Format (.ASF), a new data-
independent format for storing and transmitting multimedia content across the Internet. Because .ASF
files can be streamed, you can begin playback of these files immediately. Active Movie is an ActiveX
control and can be used inside client-side VBScript or JavaScript.
.asf is an open and extendible data-independent format. With this format, you can combine and store
different data objects such as audio objects, video objects, URLs, and HTML pages into a single
synchronized multimedia stream. This encapsulation feature enables popular media types and formats,
such as MPEG, .avi, .wav, and Apple QuickTime, to be synchronized and stored efficiently on a variety
of servers.
.asf data is also network independent and can be transmitted over different protocols and networks,
including TCP/IP, UDP, RTP, IPX/SPX, and ATM.
.asf also provides efficient packaging for different network transports, supports multiple-bit rates, error
correction, and other multimedia-content storage and transmissions.
You can efficiently play back .asf content by using Active Movie, Microsoft's next generation, cross-
platform video technology for the desktop.
With ActiveX, you can make the most of Internet resources with less effort. ActiveX Controls and
Scripting give you the infrastructure needed to add language- and tool-independent extensions to Web
pages. Using ActiveX Controls lets developers take advantage of existing OLE development tools and
the investment they have already made in OLE. ActiveX Scripting allows you to drop any scripting
engine into IE 3.0, enabling developers to add behavior to Web pages in whatever scripting language
they prefer.
ActiveX has also greatly improved extending the HTTP and FTP protocols. The ActiveX IBindXXX
interfaces encapsulate a new protocol that supports the concept of binding to a URL dynamically from
within your application. An application binds to a URL moniker, which then communicates through the
appropriate protocol to activate the OLE object. This abstraction allows newly developed protocols to
integrate into your existing objects, independent of your object design or expressed functionality.
Using the Internet Extensions for the Win32 API (WinINet) makes it easy for developers to add Internet
access to their applications. WinINet abstracts the TCP/IP protocol-specific details and gives developers
a simplified programming interface to access the internet instead of worrying about the WinSocket
details. This API includes HTTP, FTP and Gopher access.
As with most systems, server efficiency and resource use becomes a concern when designing multi-user
server applications for the Internet. The Internet Information Server offers a high-performance, secure,
and extendible framework. An ISAPI Internet Server Applications (ISA) is a dynamic link library that
loads into the same address space as the HTTP Server component versus CGI creates a separate process
for every work request. Each new process in the CGI model requires more server resources. The
advantage of developing an ISA rather than a CGI is high-level performance that requires considerably
fewer resources, which frees up resources that can then be used by the server.
The Internet Database Connector allows ODBC database access through an HTTP request.
ISAPI filters can be used to enhance the Microsoft Internet Information Server with custom features,
such as enhanced logging of HTTP requests, custom encryption, aliases for URLs, compression schemes,
or new authentication methods. The filter applications sit between the client network connection to the
HTTP server.
IIS has a few built-in ISAPI DLL. One of them is Httpodbc.dll which is called the Internet Database
Connector. The Internet Database Connector allows ODBC database access through an HTTP request.
Developers can use this feature to create Web pages with information from the database so that they can
retrieve, insert, update, and delete information in the database based on user input and perform any other
SQL commands.
Active Movie provides next-generation, cross-platform video; the Active Movie Stream Format solves
several important synchronization issues in multimedia-content storage and transmission.
Active Animation gives you a powerful foundation for Internet interactive, animated, and multimedia-
based content, targeting domains such as advertising, entertainment, online shopping, and technical
illustration.
Microsoft is building an infrastructure around the client/server model that enables secure transactions,
billing, and user authentication. IE 2.0 and 3.0 supports the secure socket layer (SSL) 2.0 and 3.0 version
and personal communications technology (PCT) 1.0 version. Most importantly, ActiveX is built on
Win32 and OLE, which enables developers to build on their existing investments. ActiveX is the
doorway to a whole new world of Internet applications.
The most fundamental model that you should understand before embarking on ActiveX development is
the COM (Component Object Model). This model is the same model as discussed in the OLE COM
specification.
COM is the "Object Model" for ActiveX and OLE. Microsoft also provides OLE COM wizard and
ActiveX Template Library (ATL) so that developers can develop lightweight, fast COM objects. ATL
offers a template to write OLE automation server, OLE controls, and the very basic dual interface or any
arbitrary COM objects. ATL also provides a custom AppWizard (called OLE COM AppWizard in VC
4.x project workspace) which can be used with Visual C++ 4.1 or later to create a COM object skeleton.
Microsoft Visual C++ 4.1 Development Studio integrates different AppWizards and Control Wizard to
simplify the development process. Along with Visual C++ 4.x is version 4.x of the Microsoft Foundation
Classes. 4.1. In Microsoft Visual C++ 4.2, there is new support for ActiveX programming, such as
WinInet
Active document
Asynchronous moniker
URL moniker
Besides the tools, wizards, frameworks, and foundation classes, Microsoft also provides a set of
specifications to implement certain ActiveX controls. For instance, it provides the OLE controls for
Internet, Document object, ActiveX scripting interface, Hyperlink interface, and Asynchronous Moniker
specification.
ActiveX controls can be easily manipulated by any scripting languages in IE 3.0. The scripting languages
include Java script and the client side VBScript and JavaScript, or any other third party scripting
language that implements the ActiveX scripting interface. ActiveX controls, particularly OLE
automation servers, can also be used with the server side VBScript.
Microsoft also provides J++ to develop the Java Applet and Java Applications. Java Applet can be used
as an ActiveX controls.
Besides these ActiveX client side script, Microsoft also provides a system on top of the IIS 2.0 to use the
server-side VBScript. ActiveX controls, particularly OLE automation server, can be referenced in the
server-side VBScript.
A variety of tools can be used to develop server-side components, such as Perl and C for CGI
programming.
To facilitate developing ISAPI applications, Microsoft Visual C++ 4.x provides the ISAPI Extension
Wizard and some foundation classes for ISAPI. Along with this, Microsoft also provides the ISAPI
specification.
The Active Movie add-on tool kit includes tools to develop applications that handle streamed media. This
tool kit allows software developers to integrate real-time audio and video content in virtually any type of
application.
Active Animation and Active Movie controls can be manipulated through client-side VBScript. A
developer can glue the control's functions together without the need for complex stream synchronization
methods.
Summary
ActiveX is a technology that has the potential to change the way information is accessed and used on the
Internet. Powerful abstractions based on OLE have been developed that enable fast, scaleable integration
of your objects within the Internet. Microsoft is making a major effort to make the Internet everything it
can possibly be. By using ActiveX, developers can make the best use of their system resources while
providing instant, dynamic content and functionality in their Internet applications. How information is
presented greatly affects how interesting and usable people find it.
Chapter 2
OLE Components
by Vincent W. Mayfield
OLE: An Introduction
OLE: The User's Perspective
OLE Services: A Programmer's View
Component Object Model (COM)
Structured Storage
OLE Documents
OLE Automation
OLE Controls
ActiveX Controls
COM
Internet Monikers
ActiveX Technologies
ActiveX Hyperlinks
ActiveX Conferencing
ActiveX Scripts
Code Signing
HTML Extensions
ActiveMovie
Summary
Chapter 2
OLE Components
by Vincent W. Mayfield
Welcome to the exciting new world of ActiveX Programming! But is ActiveX really new? Well, yes and
no. In Chapter 1, "An Overview of ActiveX," you learned the ActiveX themes. In addition, you also got
an overview of what ActiveX can do for you. But what are the foundations of ActiveX? Moreover, what
technologies does ActiveX include? These are the same questions I found myself asking when I returned
from the Software Development 96 Conference last March.
During a lecture I attended, someone suggested that ActiveX was nothing more than Internet-aware OLE
Controls. I started asking questions, and someone else informed me that ActiveX is nothing more than a
sly marketing attempt to sell OLE under a different name. I was left perplexed and confused because no
one could give me a definitive answer about the internals and framework of ActiveX or even an explicit
definition of what ActiveX is. I decided to find out for myself just what ActiveX is.
What I found is that ActiveX is composed of a group of technologies or components to develop and
implement applications for the Internet. I soon understood why no one could give me a clear definition.
At the core of these technologies is OLE. ActiveX is an extension of OLE technologies across the
Internet. But ActiveX is more than an extension of OLEit also comprises a series of Internet and
multimedia services that can be used to create rich Internet applications. However, to understand
ActiveX, you must first understand OLE. So this is where this chapter's exploration of ActiveX begins.
To understand the ActiveX Control, you must first understand ActiveX and most importantly OLE. This
chapter examines each component technology that makes up OLE. Next, it covers how each component
that makes up OLE is extended across the Internet through ActiveX. Then you will investigate the new
technologies in ActiveX. Lastly, you will examine the OLE/ActiveX technologies included in an
ActiveX Control. Keep in mind during your reading that OLE, ActiveX, and Internet programming are
not easily mastered. The key to the deployment of these technologies is a thorough understanding of their
concepts.
OLE: An Introduction
In 1991, Microsoft introduced a new specification called OLE 1.0. The OLE in OLE 1.0 stood for object
linking and embedding. OLE 1.0 was basically a way of doing compound documents. A compound
document is a way of storing data in multiple formats, such as text, graphics, video, and sound, in a
single document. Object-oriented was the new programming buzzword, and the OLE 1.0 specification
was a move to a more object-oriented paradigm. Furthermore, OLE 1.0 was an effort to move toward a
more document-centric approach, instead of an applications-centric approach. Unfortunately, OLE 1.0
was coldly received by software developers. Very few independent software vendors (ISVs) and
corporations raced to embrace OLE 1.0 and OLE-enable their applications. This reluctance to deploy
OLE 1.0 in applications was mainly because OLE 1.0 had a steep learning curve. In addition, OLE 1.0
had to be coded using a very complex C API, which embodied programming concepts new to most
developers.
Fortunately, Microsoft continued to strive to improve OLE and in 1993, released the OLE 2.0
specification. This new specification encompassed more than just compound documents; it sported an
entire architecture of object-based services that could be extended, customized, and enhanced. The
foundation of this services architecture was the Component Object Model (COM). The services available
through this architecture are
COM
Clipboard
Drag and Drop
Embedding
In-Place Activation
Linking
Monikers (Persistent Naming)
OLE Automation
OLE Controls
OLE Documents
Structured Storage
Uniform Data Transfer
From a programmatic view, OLE 2.0 is a series of services built on top of each other. These services
form an architecture of interdependent building blocks built on the COM foundation.
The release of OLE 2.0 had such an impact on standard ways of computing that it received two
prestigious industry awards: a Technical Excellence award from PC Magazine and the MVP award for
software innovation from PC/Computing. Adding to the OLE 2.0 success was a new and improved
programming interface. Developers could now move to OLE-enabled applications much more easily.
The OLE 2.0 services incorporate many of the principles embodied in object-oriented programming:
encapsulation, polymorphism, and an object-based architecture. Further adding to the success of OLE 2.0
was the release in February of 1993 of Visual C++ 1.0 with the Microsoft Foundation Class (MFC)
Library version 2.0. MFC had wrapped the OLE API in a C++ class library, thus making it much easier
for programmers to utilize the OLE services architecture.
Note
Don't let the ease of use of the MFC Library fool you. OLE programming is
very difficult to master. However, I recommend that fledgling OLE and
ActiveX programmers utilize MFC. MFC provides a framework to get you
up and programming very quickly. Trying to program at the API level
initially can lead to frustration and discouragement. If you do not know
OLE, my advice is to learn the services, concepts, and standards and then go
back and understand the low-level C API. Understanding the services, their
interfaces, and when to use them is the main key to OLE and ActiveX
programming.
Today OLE is no longer an acronym. The term object linking and embedding is now obsolete. Microsoft
refers to it as simply OLE. Notice that there is no version number attached to OLE any more. Because
OLE is an extensible architecture, it can be enhanced and extended without changing its basic
foundation. A testimonial to this capability is OLE Controls. OLE Controls were not part of the original
release of OLE. OLE Controls were not added to the available OLE services until almost a year after the
original release. In fact, objects created with OLE 1.0 still work and interact with modern OLE
applications. However, their functionality is limited to the original 1.0 specification. Thus there is no
need for versions. From here on out, this chapter will simply refer to OLE unless specifically outlining a
feature of OLE 1.0.
Because the end user is the main reason software is developed, this section will view OLE from the user's
eyes. This will help you to grasp the benefits and the available services of OLE and ActiveX. The end
user's view is simple, less technical, and very understandable. I firmly believe that the user decides in the
first ten minutes of using an application whether he or she likes it. This sets the stage for all further
experiences utilizing that application. Therefore, an application's intuitiveness, appearance, ease of use,
capability of performing work or entertaining, and performance are of paramount importance.
Always keep in mind that the "devil-spawned end user", as the cartoon character Dilbert, by Scott Adams
would say, is the main reason for our existence as software engineers. The best software engineers never
forget this and always tackle every programming endeavor with the end user in mind.
Microsoftand Apple before themknew this. This is why Windows and the Macintosh each have a
standard interface, not only from a user's perspective, but also from a programmer's perspective. Users
interact with OLE in three ways: OLE Documents, OLE Automation, and OLE Controls. As a computer
professional, you have seen or worked with Microsoft Word or Excel before. Microsoft Word is the
classic example of an OLE Document. This chapter is not going to outline the functionality of Word but
merely point out the features of OLE. However, do not be deceived; OLE Documents are not always the
classic word processor. It is easy to think so because of the word "documents."
The first feature of OLE Documents is a common user model. This simply means that the User Interface
(UI) features used to access OLE Documents are similar from application to application. The common
user model features document-centricity and takes advantage of OLE's integrated data capabilities.
Note
One of these integrated data capabilities is called Linking and Embedding. Data objects of different
types, created from other applications, can be embedded or linked into an application's OLE Document.
This enables the user to manipulate the object in the host application without returning to the creating
application. The object is simply edited in place, hence the term "in-place editing." The user interface is
modified in the host application with menus, toolbars, and context menus from the application that
created the object.
In Figure 2.2, take note of the two kinds of data, text and an embedded Visio drawing. Also note the
toolbars and menu.
Figure 2.2. Microsoft Word document with embedded text and graphics.
If you double-click the Visio Drawing, the Word application changes, and new user interface objects are
added to Word from Visio (see Figure 2.3). Notice that the Word user interface performs a
metamorphosis and now has the Visio tool bars, floating dialog boxes, and menu items, as well as the
Visio drawing and rulers. The user can then edit this drawing object without switching applications. In
addition, these objects can be dragged and dropped between and within applications.
Figure 2.3. Microsoft Word document with the Visio drawing activated for in-place editing.
These features are implemented in the same way from application to application. Thus, there is a smaller
learning curve for users when they get new applications, because the applications function similarly and
have a common user model.
The next level of visibility to the user is OLE Automation. OLE Automation enables the user to access
and modify objects through properties and methods utilizing a high-level language like Visual Basic for
Applications (VBA). This enables the user to customize objects, and the inter-activity between objects, to
perform operations the way the user defines. Microsoft Excel spreadsheets are the classic OLE
Automation objects. The user can create Excel spreadsheets that update a Microsoft Graph object or
update information in a Microsoft Access or Borland Paradox database. The greatest part of OLE
Automation is that you do not have to be a programmer to take advantage of it. This is done through
VBA. Microsoft has made VBA easy to learn using a Macro Recorder (see Figure 2.4) that records your
keystrokes in VB code and an "object browser" that you use to paste the proper code where you need it.
Anyone can learn to use it.
OLE Controls are the last area of OLE visibility to the end user. They are self-contained, reusable
components that can be embedded in applications. To the user, they are nothing more than a control that
takes their input and passes it to the application that contains it. However, some OLE Controls are static
in nature, such as a picture control. OLE Controls are also OLE Automation objects that can have
properties set at both compile time and runtime, and OLE Controls also have methods that can perform
certain operations. The difference between OLE Controls and OLE Automation objects is that they are
self-contained objects. They provide two-way communication between the control and the container. In
future sections, you will discover that OLE Controls have been extended to ActiveX Controls that can be
utilized across the Internet. These components have a very profound impact in the area of application
development (see Figure 2.5), because they are prebuilt. From the end user's perspective, they provide
increased functionality and lower software costs.
Figure 2.5. Properties and methods of an OLE Control during development in the Visual C++
Microsoft Developer studio.
This section covers the OLE Services from a programmatic view. For each service, you will be given a
description of the technology and a programmer's view of the interfaces to these OLE services. Pay
particular attention to understanding what each service does and where it fits into the architecture. The
explanations highlight the interfaces to these objects and some key properties and methods where
appropriate. Some of these services will be discussed in detail in later chapters as they pertain and
integrate into ActiveX. Remember that OLE consists of the following services:
COM
Clipboard
Drag and Drop
Embedding
In-Place Activation
Linking
Monikers (Persistent Naming)
OLE Automation
OLE Controls
OLE Documents
Structured Storage
Uniform Data Transfer
Notice these are the same technologies the end user sees. However, the end user's view is a visual one,
and the programmers view is a menagerie of interfaces that must be mastered to provide the slick visual
representation the end user sees. As discussed earlier, these services form building blocks on which each
element in the architecture builds upon, as shown in Figure 2.6.
When Microsoft designed OLE, it was designed with object-oriented programming in mind. COM
objects are much like instantiated C++ classes or an ADA Package. In fact, COM was designed with C++
programmers in mind. It supports encapsulation, polymorphism, and reusability. However, COM was
also designed to be compatible at the binary level and therefore has differences from a C++ object. As a
programmer, you are aware that compiled programming languages such as C, C++, PASCAL, and ADA
are machine-dependent. As a binary object, a COM object concerns itself with how it interfaces with
other objects. When not utilized in the environment of its creator, an interface is exposed that can be seen
in the non native environment. It can be seen because it is a binary object and therefore not machine-
dependent. This does not require the host environment or an interacting object to know anything about
the COM object. When the object is created in the womb of its mother application, COM does not
concern itself with how that object interacts within it. This interaction is between the mother application
and the child object. It is when the object interacts with the rest of the world that COM is concerned
about how that object can be interfaced with. It is important to note that COM is not a programming
language: it is a binary standard that enables software components to interact with each other as objects.
It is also a programming model to facilitate the programmability of this standard.
COM objects consist of two types of items, properties and methods. Properties are the data members, and
methods are member functions. COM objects each have a common interface. No matter what they do,
COM objects all have to implement the IUnknown interface. This interface is the main interface for all
others. The IUnknown interface has the following member functions:
ULONG AddRef(void)
ULONG Release(void)
HRESULT QueryInterface(REFIID id, void **ipv)
Each object implements a vtable. A vtable is nothing more than an array of pointers to member functions
implemented in the object (see Figure 2.7). This vtable is shared between all the instances of the object
also maintaining the private data of each object. A client application evokes an instance of the interface
and gets a pointer to a pointer that points to the vtable. Each time a new interface to the object is
instantiated, the reference count of objects is incremented with AddRef(). Conversely, each time a
reference is destroyed, the reference counter is decremented with Release(). Once the reference count is
zero, the object can be destroyed. In order to see what interfaces an object supports, you can use
QueryInterface().
This section has covered a general overview of COM. An in-depth review of COM and how COM is
implemented through ActiveX, complete with examples, is presented in Chapter 3, "Creating COM
Objects."
Structured Storage
Unfortunately, most platforms today have different file systems, making sharing data a very difficult
task. In addition, these file systems arose during the mainframe days when only a single application
needed to access the disk at any one time. COM is built with interoperability and integration between
applications on dissimilar platforms in mind. In order to accomplish this, COM needs to have multiple
applications write data to the same file on the underlying file system. OLE Structured Storage addresses
this need.
Structured Storage is a file system within a file. Think of it as a hierarchical tree of storages and streams.
Within this tree, each node on the tree will have one and only one parent, but each node may have from
zero to many children. Another way to think of it is like the Windows 95 Explorer. The folders are the
storage nodes, and the files are the streams. Structured Storage provides an organization chart of data
within a file as seen in Figure 2.8. In addition, this organization of data is not limited to files, but includes
memory and databases.
Stream objects contain data. This data can be either native data or data from other outside objects.
Storage objects are compatible at the binary level; thus, in theory, they are compatible across platforms.
However, you all know that there are minute differences between the various platforms. Notice in Figure
2.8 the tree of the structured storage object. The definition of the tree is dependent on how the objects
creator defined the storage of the object.
Structured Storage objects are manipulated utilizing the following OLE Interfaces:
IPersistStorage
IStorage
IStream
IStorage,as the name implies, manipulates storage objects. Likewise, IStream manipulates streams.
Rarely would you want to manipulate stream or storage objects individually. More than likely, you
would want to manipulate the persistent storage object with the IPersistStorage. By persistent storage, I
mean data that will continue to exist even after an object is destroyed. For example, if you wanted and
allowed the user to define the color of an object such as a text label, you would persistently store that
object's foreground and background colors. The next time the object was created you could read in from
persistent storage the colors previously chosen by the end user. You could then apply those attributes to
the object and thus maintaining the users preferences. IPersistStorage enables you to do this by
performing the following operations:
IsDirty
InitNew
Load
Save
SaveCompleted
HandsOffStorage
A great way to see what structured storage looks like is with a utility that comes with Visual C++ 4.X
called DfView. DfView is in the \MSDEV\BIN directory of Visual C++. DfView enables you to look at
a compound file also known as an OLE Document. OLE Documents implement structured storage.
Figure 2.9 shows an example of DfView (this is the Word document with an embedded Visio drawing
object seen earlier in Figure 2.2).
Figure 2.9. DfView shows the hierarchical tree of a structured storage object.
If you double-click a stream object, you can see its binary contents (see Figure 2.10).
Monikers are a way to reference a piece of data or object in an object-based system like OLE. When an
object is linked, a moniker is stored that knows how to get to that native data. For example, if you link a
sound file into a Word document, the .WAV file is not stored natively in that document. A moniker is
created that can intelligently find the .WAV file object.
To utilize a moniker to locate and bind to an object, you must utilize the IMoniker interface and call
IMoniker::BindToObject. By utilizing the intelligent persistent name of that object, the IMoniker
interface negotiates the location of that object and returns a pointer to the interface of that object's type.
The moniker itself then dies. Think of it as similar to de-referencing a pointer in C or C++ to locate a
piece of data. Remember that monikers are persistent. IMoniker is derived from IPersistStream, and thus
it can serialize itself into a stream, hence persistence. There are five basic types of monikers:
File monikers
Item monikers
Anti monikers
Pointer monikers
Composite monikers
File Monikers
File monikers store a filename persistently. In binding the text filename to the file object, a pointer to the
file object interface is returned so that you can manipulate that file object.
Item Monikers
Item monikers point to a specific place inside a file, such as a paragraph or a portion of an embedded
video.
Anti Monikers
Anti monikers delete the last moniker in a series or chain of monikers, as in a composite moniker.
Pointer Monikers
Pointer monikers simply point to other monikers wrapping them in a chain. However, it should be noted
that pointer monikers are not persistent.
Composite Monikers
A composite moniker is an ordered collection of monikers. At the root of a composite moniker is a file
moniker that references the document path name. It then holds a series of item monikers. Composite
monikers are used when you need to have a collection of monikers within a single object.
Through OLE, you can utilize structured storage to store your objects, and monikers to find your objects,
but there has to be a mechanism to move this data from the place it is stored (linked or embedded) to
where you can output it to the client for manipulation. In addition, Uniform Data Transfer (UDT) also
notifies the data object and the client of changes in the data. Uniform data transfer provides this service
through the IDataObject interface. UDT is used primarily in three areas:
ClipBoard
OLE Drag and Drop
Clipboard
The system clipboard is a system-level service used for interprocess communications. Because it is a
system-level service, all applications have access to it. OLE can utilize the clipboard to do UDT of
objects between processes. With an IDataObject pointer, you can use the function OleSetClipboard() to
take a cut or copied object and expose this object to all processes through the clipboard. Likewise, when
you want to paste data from the clipboard, you can use your IDataObject pointer to utilize the
OleGetClipboard() function. This is a very powerful mechanism because it maintains the integrity of the
object as a whole, enabling you to move complex object data types between applications.
Drag and Drop is a method by which the user can select and move objects within an application and
between applications. UDT is used to perform Drag and Drop actions. On the selection of the object, the
source application packages the object and uses an IDataObject pointer to call DoDragDrop(). The
source uses the IDropSource interface, which yields a pointer to its implementation. This pointer is
passed to DoDragDrop(). The source controls the mouse cursors and handles the object in case of a
cancellation.
Once the user brings the dragged object to its new client location or target, the client application evokes
the IDropTarget interface. With the pointer to the IDropTarget, the client application tracks the object in
relation to itself with the functions available in the IDropTarget interface. One function called
IDropTarget::Drop() is called when the object is dropped on the target. Drop() passes the IDataObject
pointer of the source to the target. Now that the client has the IDataObject pointer, it is free to manipulate
the object.
A linked object is an object that is not stored within an OLE Document. In the document, a moniker is
stored that references the linked object. This OLE function utilizes UDT to move the data from the data
object source to the container application so that the data can be rendered as appropriate. Linked objects
are manipulated through the IOleLink interface. By linking an object instead of embedding it, you cut
down on the size of the compound file. In addition, you expose the linked object so that multiple people
can utilize it.
An embedded object is an object that is stored, through the OLE structured storage mechanism, as native
data within an OLE Document. Although this increases the size of the compound file, it provides a single
file object that can contain multiple data types.
OLE Documents
OLE Documents are nothing more than compound files that utilize structured storage to hold the objects
that make up the document. These objects that make up the document can be native data, or they can,
through the use of monikers, link to data outside of the document. In addition, an OLE Document can
contain objects created by other processes, embedded as if they were natively a part of the document.
OLE Documents are handled through interfaces just like any other OLE object. As you can see, OLE
Documents are a conglomeration of several OLE services. Here are some of the interfaces utilized to
implement OLE Document interfaces:
IOleItemContainer
IPersistFile
IClassFactory
IOleInPlaceActiveFrame
IOleInPlaceUIObject
IOleInPlaceSite
In-Place Activation
OLE Documents support in-place activation or what is commonly referred to as visual editing. This
enables you to edit embedded objects in a container application as if they were native. When you activate
visual editing in the container, the user interface of the container morphs to support selected user-
interface functions of the server application that created the object. There are a whole series of interfaces
to enable you to implement and support in-place activation. These interfaces all begin with IOleInPlace.
These are some of the interfaces you can utilize to implement and support in-place activation:
IOleInPlaceObject
IOleInPlaceActiveObject
IOleInPlaceSite
IOleInPlaceActiveFrame
IOleInPlaceUIObject
IOleInPlaceSite
OLE Automation
OLE Automation basically enables you to manipulate the properties and methods of an application from
within another application through the use of high-level macro languages and scripting languages like
VBScript and JavaScript. This enables you to customize objects and provide interoperability between
applications.
In the world of OLE Automation, there are OLE Automation Components and OLE Automation
Controllers. An OLE Automation Component is a service that is exposed by an application for use by
another. Microsoft Excel is a good example of this, as it exposes services that can create and manipulate
worksheets, cells, and rows.
What services are available through an OLE Automation Component are stored in a type library. A type
library is stored in a binary file with a TLB extension. Object Description Language is used to define the
services of an OLE Automation Component. Object Description Language instructions are stored in a
file with the extension ODL. The ODL file is compiled into a type library. In Visual C++ and in the
ActiveX SDK, there is a nice utility that reads type libraries and graphically displays the services
provided by OLE Automation Components.
The utility in Visual C++ is called OLE 2 View 32 application. The utility in the ActiveX SDK is called
OLE\COM Viewer, and it is a newer implementation than the OLE 2 View 32 application in Visual C++.
Figure 2.11 shows the OLE/COM Viewer, which can be used to view OLE and COM objects
graphically.
Note
Figure 2.11. The OLE/COM Viewer that comes with the ActiveX SDK.
Notice that the Type Library Viewer screen (see Figure 2.12) shows the disassembled type library in
Object Description Language. It also displays the constants, properties, methods, and interfaces to the
Automation Component.
OLE Automation Controllers are applications that use the services provided by OLE Automation
Controllers. OLE Automation Controllers work through an interface called IDispatch. This dispatch
interface exposes the available services to the controller application.
OLE Controls
As discussed previously, OLE Controls are self-contained reusable components that can be embedded in
applications. OLE Controls are also OLE Automation objects that can have properties set at both compile
time and runtime, and OLE Controls also have methods that can perform certain operations. The
difference between OLE Controls and OLE Automation objects is that they are self-contained objects.
They provide two-way communication between the control and the container. These components have a
very profound impact in the area of application development. These reusable self-contained pockets of
functionality are discussed in detail in Chapter 5, "Creating OLE Controls."
ActiveX has taken the OLE Technologies and extended them beyond the bounds of the local machine, to
Enterprise Wide networks and the Internet. Specifically, OLE Technologies have aggrandized into the
following ActiveX services:
ActiveX Documents
ActiveX Controls
COM
Internet Monikers
This is not the total affect. Elements of OLE are also present in the new ActiveX technologies, as will be
discussed in the next section, "ActiveX Technologies." For now we will concentrate our discussion on
the evolution of OLE Technologies into ActiveX.
ActiveX Documents
ActiveX has taken OLE Documents and extended them across the Internet. This technology is a way for
existing OLE Documents such as Microsoft Word, Microsoft Project, and Microsoft PowerPoint to be
activated by a Web browser and brought up through a viewer. Thus you can have compound files with
various data that can contain linked and embedded objects being accessed across the World Wide Web
(WWW). Utilizing the ActiveX Hyperlinks technology, you can extend OLE Documents across the Web.
ActiveX Hyperlinks are discussed in the next section. In addition, ActiveX Documents are discussed in
depth in Chapter 19, "OLE Document Objects."
Asynchronous Storage
The ability to bring ActiveX Documents across the WWW gives rise to another ActiveX technology,
Asynchronous Storage. Basically this extends structured storage across the Web, allowing for the storage
to happen asynchronously. Obviously, with the slow bandwidth of the Internet, if you allowed a storage
operation to happen synchronously, nothing else could happen on the client or server until the transfer of
data to or from persistent storage took place. Utilizing ActiveX Hyperlinks and the technology of
Asynchronous Monikers, Asynchronous Storage is accomplished.
ActiveX Controls
ActiveX Controls are simply OLE Controls or OCXs that have been extended to the Internet
environment. Microsoft has now replaced the term OLE Control with ActiveX Control. Remember, OLE
is an extendible architecture; therefore, these reusable components can not only be embedded in a Web
page, but also in a non-Internet-enabled application. ActiveX Controls are covered in depth in Chapter 5,
"Creating OLE Controls."
ActiveX Controls can be created in a variety of languages, including C, C++, Java, and according to
Microsoft, the next release of Visual Basic. They can also be manipulated though VBScript or
JavaScript, so you do not even have to be a programmer to use them.
ActiveX Controls are great components as you have a virtual plethora of little pockets of prefabricated
functionality you can take advantage of. The possibilities for ActiveX Controls are endless. Currently,
ActiveX Controls range from a Calendar Control to a Picture Control that enables you to display static
pictures.
COM is at the base of the ActiveX Control technology. ActiveX Controls are built on a series of OLE
services, with COM as the base. The following list depicts the technologies that are encompassed in the
ActiveX Control:
Just like the OLE Controls previously discussed, ActiveX Controls are COM objects. They are in-process
OLE Automation servers activated from the inside out. Like every other COM object, they expose the
IUnknown so that container applications can access their properties and methods through the pointers
returned by the interface.
ActiveX Controls support two-way communication from the control to the client application. This
method of communication is called Connectable Objects. It enables the control to notify the client of
events or invoke a method or event. It also enables the client to communicate directly with the control.
Controls can be dragged and dropped within their client application if that functionality is enabled in the
client application.
In the beginning of this chapter, you saw how an object from another application could be embedded in a
host application. In addition, that object could be in-place activated for visual editing. Likewise, OLE
Controls are built on the concept of OLE Documents and can be in-place activated.
ActiveX Controls have property pages, like their predecessor OLE Controls, that expose its properties
and methods to the user. From the property pages, the properties can be set.
ActiveX Controls are automation servers. Their properties and methods can be set at compile time
through the use of property pages and at runtime through VBScript and JavaScript.
COM objects can utilize Persistent Storage in a variety of ways. ActiveX Controls utilize Persistent
Storage to store their state. This enables the control to be initialized to the state it was when you last
utilized it.
COM
As you learned previously, COM is a binary standard for objects. Basically COM operates the way it did
before ActiveX, except that COM has been extended so that you can exchange and utilize objects across
the Internet. This has given rise to Distributed COM.
Distributed COM, also known as DCOM and formerly known as Network OLE, is the basic extension of
binary COM objects across LANs, WANs, and the Internet. Now you can instantiate and bind objects
across a network. A detailed analysis of COM is presented in Chapter 3, "Creating COM Objects."
Internet Monikers
With the advent of ActiveX and the extension of COM across the net, quite naturally monikers were also
extended and incorporated into this architecture. This gave rise to two new types of monikers.
URL monikers
Asynchronous monikers
URL Monikers
A URL is a universal resource locator, used for Web-based addressing of objects. As you learned earlier,
monikers are an intelligent naming system, so that by utilizing the IMoniker interface to an moniker
object and the intelligent name, you can locate the object. This capability was simply extended to include
URLs because of the capability to pass objects across the net from DCOM.
Asynchronous Monikers
Previously, monikers carried out their binding to the object synchronously. Nothing could happen until
the binding was complete. On the high latency, slow-link communications network of the Internet,
holding up operations while binding is accomplished is unacceptable. Thus with asynchronous monikers,
the interfaces to the object negotiate the transmission of the binding process, so as to perform it
asynchronously. Right now, URL monikers are the only implementation of asynchronous monikers.
ActiveX Technologies
ActiveX brings to the table some new technologies that are not necessarily related to OLE. However,
these technologies facilitate the creation of interactive applications for the World Wide Web. These items
are
ActiveX hyperlinks
ActiveX conferencing
ActiveX server extensions
Code signing
HTML extensions
ActiveMovie
ActiveX Hyperlinks
ActiveX hyperlinks basically allow in-place activation from HTML files of non-HTML based
documents. Utilizing an ActiveX document container, you can access Microsoft Word, Microsoft Excel,
Microsoft PowerPoint, Visio, and CorelDraw! documents from a hypertext link in an HTML document.
ActiveX Conferencing
The ActiveX conferencing services are a suite of technologies that enable real-time, multiparty,
multimedia communication over the Internet. This is much like video teleconferencing except you can do
it on a PC. Just think what this does for programmers; we could all work at home and telecommute. This
is a programmable interface opening up endless possibilities for innovation.
ActiveX server extensions, formerly known as the ISAPI Internet Server API, are used to give
functionality to Internet servers. Previously this could only be done utilizing common gateway interface
(GCI) code. ActiveX server extensions provide an alternative means to achieve this functionality.
Usually server extensions are implemented utilizing a dynamic link library (DLL) and provide some
functionality not provided by the HTTP server, such as connecting to a database.
ActiveX Scripts
ActiveX scripts bring OLE Automation to the Internet. Automation Controllers can now access
Automation Component Services across the Internet with DCOM and ActiveX support for scripting. You
can use a variety of scripting languages such as VBScript, JavaScript, Perl, Visual Basic for
Applications, Lisp, and Scheme. To explore scripting, see Chapters 8, "Microsoft Internet Explorer
Object Model for Scripting," 9, "VBScript," and 10, "JavaScript."
Code Signing
Code signing is a new technology that enables electronic signatures for code. This provides security from
tampering of interactive applications across the net. Basically, the application vendors will provide a
digital signature for their code that compiles with the Code Signing specification. On the client side,
when an application or component is downloaded from the net, it calls a Win32 API function called
WinVerifyTrust(). This function checks the digital signature and verifies it.
HTML Extensions
HyperText mark up language (HTML) is the language for all Web-based document production. In order
to support ActiveX Controls and ActiveX Scripts, extensions had to be made to the HTML language. In
addition, Web browsers had to be modified to accommodate the new language extensions. Now you can
add ActiveX Controls to Web pages using the HTML <OBJECT> tag. These new extensions will be
covered in Appendix O, "HTML Extensions."
ActiveMovie
ActiveMovie is a new technology to replace the old Media Control Interface and Video for Windows.
ActiveMovie is an audio and video steaming framework. With ActiveMovie you will be able to play
back MPEG, AVI, and Apple Quicktime movies.
Summary
This chapter has discussed OLE and the component architecture that makes up OLE. Because OLE is an
object-oriented architecture founded on the COM, it is an extendible services architecture. Each OLE
component is a building block for the rest of the technologies. Microsoft has extended this architecture to
ActiveX to facilitate the creation of Internet-enabled applications. ActiveX builds on OLE and COM and
adds new technologies of its own. ActiveX Controls are Internet-aware controls that are nothing more
than an extension of the OLE Control architecture and make it easy to extend the use of reusable
components on the Internet.
Next, Chapter 3 explores the COM in depth and shows you how to create COM objects. You will find
out why COM objects, as opposed to C++, ease the programming challenge.
Chapter 3
Creating COM Objects
by Weiying Chen
COM Client
COM Library
Summary
Chapter 3
by Weiying Chen
The Component Object Model(COM) is an open architecture for cross-platform development of client/server applications. It is the
cornerstone for ActiveX technology and OLE 2.0.
This chapter presents the fundamental concept of COM, such as COM client/server architecture, COM server, and COM client. A set of
fundamental COM interfaces are also examined in detail to describe their roles in creating COM objects.
To illustrate the fundamental blocks and the concept of the COM architecture, serial examples are built step-by-step to demonstrate how to
create various COM servers and corresponding COM client applications. All source codes in examples are written in Microsoft VC++ 4.1.
Microsoft Active Template Library (ATL) simplifies the procedure of creating COM servers by providing commonly used templates. At
the end of this chapter, an example demonstrates ATL creating COM objects.
The Component Object Model (COM) is an open architecture for cross-platform development of client/server applications. It is the
cornerstone for ActiveX technology and OLE 2.0 as shown in Figure 3.1.
All ActiveX controls are COM objects. COM objects refer to any object that implements IUnknown interface.
ActiveX scripting provides a set of OLE interfaces for a scripting engine and a scripting engine host. All these interfaces inherit from
IUnknown.
ActiveX Document provides a set of Document object and Document object container interfaces, which inherit from IUnknown interfaces.
COM provides a client/server architecture. The COM client uses COM server via the COM library. Figure 3.2 illustrates.
COM Server
A COM server is a component that implements one or more COM Class objects. A COM Class object is a COM object that is creatable via
a COM Class Factory object. A COM Class object has a CLSID associated with it. A COM object is anything that implements IUnknown
interface. It is different from an object in object-oriented programming. In Object-Oriented programming, an object called OO object here
is an entity that has state, behavior and identity. The OO object's state is represented by the value of the attributes in the object. But a COM
object's state is implied by the interface; the state is not explicitly stated, because there are no public attributes exposed in the interface. The
Interface is just a set of functions without any attributes.
The OO object's behavior is a sequence of messages sent to the object, which is a sequence of methods called on this object. But for a COM
object, the object's behavior is defined as the interface it supports.
The OO object's identity is a way to look at the object, whereas for a COM object, the identity is defined by moving between interfaces
exposed by the COM object, this is done by invoking IUnknown::QueryInterface interface.
Each COM object provides functionality via exposing interfaces. An interface is a group of related functions and provides some specific
service. For example, the COM server in Figure 3.3 exposes two interfaces, one is IPrint, the other is IHelp. IPrint interface provides the
print service, whereas IHelp supports the help service. Each interface groups its own functionality.
In order to uniquely identify the class object provided by the COM server, a class identifier(CLSID) is used, whereas to identify the
interface, an interface identifier(IID) is used.
A COM server is usually a .DLL or .EXE file. A DLL based COM server is called an in-process(in-proc for short) server because it loads
into the same address space as the client. The client can make direct call to the object, which is faster and more efficient. But the crash of
the DLL can destroy the client's address space.
An EXE based COM server is called an out-process(out-proc for short), because it runs in its own separate process space.
An EXE based COM server isolates from the address space of the caller, which makes it more reliable. If the server crashes, it will not
destroy the address space of the client. But because it is in a separate process, all interface calls must be marshaled across process(data gets
copied), which affects the performance.
COM Client
A COM client(client for short) is an application that uses COM server. A COM client asks COM to instantiate object in exactly the same
manner regardless of the COM server types. This is done by invoking the COM function CoCreateInstance. After COM client retrieves the
first pointer to the COM object, it can not distinguish from the interface whether the COM server being used is an in-proc, or out-proc
server.
COM Client is an executable(EXE) application as compared with COM server that can be DLL based.
COM Library
The COM library provides an implementation of the Application Programming Interface (API). The specification also defines a set of
interfaces that will be used by different COM objects.
The component in COM also supports the communication establishment between the client and server. This component provides location
transparency for the client. In other words, the client does not need to know where the server locates; all these are taken care of by the COM
library.
COM library API functions provide the functionality to the COM applications. COM applications are any application that uses COM. The
following gives an example of API functions.
CoInitialize: Initialize the COM library. The COM library must be initialized before calling its functions. This API should be called
when the COM application starts.
CoUninitialize: Uninitialize the COM library. This will free all the maintained COM resources and close all RPC connections. This
API should be called when the COM application exits.
CoCreateInstance: Create an instance of the object class. This API is a helper function. A helper function means a function that
encapsulates other functions and interface methods defined in the COM specification. CoCreateInstance wraps the following
sequence calls: COM API CoGetClassObject, IClassFactory method CreateInstance, and IClassFactory method Release.
COM predefines a set of interfaces to be used by client/server applications. Among these, IUnknown and IClassFactory interfaces are the
most fundamental ones. IUnknown interface is required for any COM object. The QueryInterface method in IUnknown interface allows the
client to access the object's identity and move between interfaces.
A class factory object is required for every object identified by a given CLSID. A class factory object implements the IClassFactory
interface.
IUnknown Interface
IUnknown is the interface that any other interfaces inherit from. In other words, every interface except IUnknown inherits from IUnknown.
Listing 3.1 illustrates the IUnknown interface definition.
interface IUnknown
ULONG AddRef();
ULONG Release();
COM object must implement this interface. COM client will invoke the methods in the interface implemented by the COM object.
QueryInterface Method
QueryInterface provides the mechanism by which a client, having obtained one interface pointer on a particular object, can request
additional pointers to other interfaces on the same object. The COM object exposes itself via a set of interfaces.
There are two parameters for QueryInterface. riid is IID of the interface requested. ppv is a return value. It is an indirect pointer to the
interface. If the interface requested does not exist, ppv must be set to be NULL and an E_NOINTERFACE error code should be this
method's return value.
The Listing 3.2 demonstrates an implementation of the QueryInterface method for CLowerStr class.
NOTE
The code listings in this chapter are from the example created in this chapter.
HRESULT hr;
*ppv = NULL;
AddRef();
hr = S_OK;
else
//if interface does not exist, *ppv set to be NULL, and E_NOINTERFACE
returns.
*ppv = NULL;
hr = E_NOINTERFACE;
return hr;
Here, CLowerStr is an implementation of the ILowerStr interface, ILowerStr interface inherits from IUnknown. CLowerStr can be called a
COM object because it implements IUnknown interface.
AddRef Method
AddRef method provides the technique for an object to keep track of the reference count. The reference count should be incremented
whenever an interface pointer is queried.
STDMETHODIMP_(ULONG) CLowerStr::AddRef()
m_dwRef++;
return m_dwRef;
Release Method
The Release method decrements the reference count. If the reference count is zero, the object should destroyed since the object is no longer
needed. The client application needs to invoke this method whenever the interface is not accessed.
STDMETHODIMP_(ULONG) CLowerStr::Release()
m_dwRef--;
if(m_dwRef == 0)
delete this;
return m_dwRef;
IClassFactory Interface
IClassFactory is the interface that class factory object inherits from. In other words, class factory implements the IClassFactory interface.
Class factory object is required in COM to create an instance of the object. It is a rule. For example, when the client application uses the
CLowerStr object, CLowerStr object has to be created via its class factory.
Look at Figure 3.4; COM server has a class factory, which creates an instance of the object.
The CreateInstance method creates an instance of the object class. It has to be implemented by the class factory object to instantiate the
object. This method will be used inside the CoCreateInstance function call. CoCreateInstance will first return a pointer to the IClassFactory
and then invoke IClassFactory's CreateInstance method to create an object's instance and returns an indirect pointer to the object's requested
interface. This method only needs to be implemented but never needs to be invoked by the application itself.
punkOuter indicates whether the object is being created as part of the aggregate. If there is no aggregation in the COM server, NULL
should be provided, otherwise, a pointer to the controlling IUnknown of the aggregate should be provided.
riid is the IID of the interface queried by the client. If the punkOuter is NULL, the IID of the initializing interface should be provided.
Otherwise, riid must be IUnknown.
ppv is a pointer to the pointer of the requested interface. If the object does not support the interface specified in riid, ppv should be set as
NULL, and E_NOINTERFACE should be returned as the method's return value.
LockServer locks the server in memory. The class factory will be revoked when the lock count is decremented to zero. LockServer(TRUE)
will increment the lock count and ensure that the class factory will not be revoked.
HRESULT hr;
CLowerStr *pObj;
*ppv = NULL;
if (pObj)
hr=pObj->QueryInterface(iid,ppv);
pObj->Release();
else
hr = E_OUTOFMEMORY;
*ppv = NULL;
return hr;
CreateInstance first instantiates the CLowerStr object and then queries whether the iid interface exists in the CLowerStr object. If yes, ppv
will return an indirect pointer to the interface, and the CLowerStr object will be released.
if (fLock)
g_cLock++;
else
g_cLock--;
return S_OK;
The LockServer first checks fLock to see whether it is true; if yes, the g_cLock will be increased, otherwise, the g_clock will be decreased.
This LockServer method will be invoked by the client application.
In the following section, a set of examples will be demonstrated to further illustrate the concept. First, an in-proc server will be created and
used. Then this in-proc server will be built as an out-proc server and used. After that, ATL will be used to create this in-proc server.
The server(lst31.dll) is illustrated in Figure 3.5. For the complete project, please refer to the lst31 directory on the CD.
There is one interface ILowerStr exposed by the CLowerStr object. There is only one method called Lower in this interface.
This method accepts an input and converts the input string to lowercase and then returns the input string to the caller.
lpOutput indicates the returned string that converts the strInput to uppercase.
1. 1. Generate two GUIDs in DEFINE_GUID format, one for IID, the other for CLSID by using guidgen.exe. Replace the <<name>>
in the code generated by guidgen.exe by IID of the interface and the CLSID of the object class.
2. The CLSID and IID is a universal unique ID (UUID). CLSID stands for class identifier, whereas IID is for interface identifier. The
use of this unique ID avoids the possibility of a naming collision among COM objects and interfaces.
3. COM clients use these unique identifiers at runtime to locate the object and its interfaces. COM library uses these IDs to locate the
COM server module path in the registry.
4. UUID can be obtained through uuidcreate RPC function. There are tools that directly or indirectly use this function to generate the
UUID, such as guidgen.exe, and uuidgen.exe.
5. guidgen.exe is a window-based application and uuidgen.exe is a console based application. They both are contained in Microsoft
Visual C++. uuidgen.exe only generates a UUID in a registry format, whereas guidgen.exe generates four formats as shown in
Figure 3.6.
1. Format 2: "DEFINE_GUID(...)" is used to identify a CLSID or IID. For example, a CLSID is defined in
1. Format 4: "Registry Format" is used to build registry entry for the COM server.
REGEDIT
HKEY_CLASSES_ROOT\CLSID\{4F126D90-1319-11d0-A6AC-00AA00602553} =
[icc]CLowerStr Object
HKEY_CLASSES_ROOT\CLSID\{4F126D90-1319-11d0-A6AC-00AA00602553}
[icc]\InprocServer32 = d:\areview\ch3\lst31\debug\lst31.dll
1. Format 1: IMPLEMENT_OLECREATE(...) and Format 3: static const struct GUID={...} are not used as often as Format 1 and 4.
2. By convention, symbolic constants are used to identify a specific CLSID or IID. It is in the form of CLSID_<class name> or
IID_<interface name>.
Note
I've used d: for my drive letter, but you should substitute the letter of the hard drive you installed the code to.
// {4F126D90-1319-11d0-A6AC-00AA00602553}
// {4F126D91-1319-11d0-A6AC-00AA00602553}
1. For the interfaces defined in the COM, such as IUnknown, the IIDs are predefined, because every interface needs to have a IID
associated with it.
2. Listing 3.9 demonstrates the IID_ILowerStr, and CLSID_CLowerStr for the CLowerStr object.
// {4F126D90-1319-11d0-A6AC-00AA00602553}
// {4F126D91-1319-11d0-A6AC-00AA00602553}
public:
};
public:
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
CLowerStr();
~CLowerStr();
private:
DWORD m_dwRef;
};
HRESULT hr;
*ppv = NULL;
AddRef();
hr = S_OK;
else
*ppv = NULL;
hr = E_NOINTERFACE;
return hr;
STDMETHODIMP_(ULONG) CLowerStr::AddRef()
m_dwRef++;
return m_dwRef;
STDMETHODIMP_(ULONG) CLowerStr::Release()
m_dwRef--;
if(m_dwRef == 0)
delete this;
return m_dwRef;
int i;
if(isupper(*(lpInput+i)))
else
*(*pOutput+i) = '\0';
return S_OK;
CLowerStr::CLowerStr()
m_dwRef = 1;
CLowerStr::~CLowerStr()
1. CLowerStr is the implementation of the ILowerStr interface. In the CLowerStr::QueryInterface(...) method, the AddRef() is invoked
whenever an interface is successfully queried.
2. AddRef() and Release() are the most standard implementation.
3. Method Lower(...) accepts the input and converts the input string to lowercase.
4. In the CLowerStr constructor, m_dwRef is initialized to 1 because this object is successfully instantiated by the client application.
5. 4. Implement IClassFactory interface.
6. Listing 3.12 demonstrates implementation of the class factory object for the CLowerStr object. This class factory object implements
two methods (CreateInstance, and LockServer) defined in the IClassFactory interface, and three methods(QueryInterface, AddRef,
and Release) defined in the IUnknown interface. Because IClassFactory inherits from IUnknown interface.
protected:
DWORD m_dwRef;
public:
CLowerStrClassFactory(void);
~CLowerStrClassFactory(void);
//IUnknown
[icc]iid,void **ppv);
STDMETHODIMP LockServer(BOOL);
};
CLowerStrClassFactory::CLowerStrClassFactory()
m_dwRef=1;
CLowerStrClassFactory::~CLowerStrClassFactory()
[icc]iid,void **ppv)
HRESULT hr;
*ppv = NULL;
*ppv=this;
AddRef();
hr = S_OK;
else
*ppv = NULL;
hr = E_NOINTERFACE;
return hr;
STDMETHODIMP_(ULONG) CLowerStrClassFactory::AddRef(void)
return m_dwRef++;
STDMETHODIMP_(ULONG) CLowerStrClassFactory::Release(void)
m_dwRef--;
if(m_dwRef == 0)
delete this;
return m_dwRef;
[icc]*pUnkOuter,REFIID
[icc]iid,void **ppv)
HRESULT hr;
CLowerStr *pObj;
*ppv = NULL;
if (pObj)
hr=pObj->QueryInterface(iid,ppv);
pObj->Release();
else
hr = E_OUTOFMEMORY;
*ppv = NULL;
return hr;
if (fLock)
g_cLock++;
else
g_cLock--;
return S_OK;
1. The implementation of AddRef and Release are the same as in CLowerStr in Listing 3.11. QueryInterface is almost the same except
the interface exposed by the CLowerStr is different from CLowerStrClassFactory. CLowerStr inherits from two interfaces,
IUnknown and ILowerStr, whereas CLowerStrClassFactory inherits IUnknown and IClassFactory.
2. 5 Define and implement export functions from Lst31 COM server.
3. Because lst31.dll is an in-proc server, functions need to be exported to be accessed by the client application. For every in-proc
server, the DllGetClassObject function needs to be exported so that COM library can access this function to create an instance of the
COM object.
4. Listing 3.13 demonstrates the module-definition (.DEF) file provided for Lst31 COM server.
EXPORTS
DllGetClassObject @1
DllCanUnloadNow @2
1. Exporting DllGetClassObject is mandatory for every in-proc server. It is the function invoked by the COM library to create an
instance of the COM object.
2. Listing 3.14 demonstrates the implementation of the DllGetClassObject function.
long g_cObj = 0;
HRESULT hr;
CLowerStrClassFactory *pObj;
if (CLSID_CLowerStr!= rclsid)
return ResultFromScode(E_FAIL);
if (!pObj)
hr= pObj->QueryInterface(riid,ppv);
if (FAILED(hr))
delete pObj;
return hr;
SCODE sc;
1. DllGetClassObject returns the interface to the IClassFactory. This function has three parameters, rclsid is the input parameter,
referring to the CLSID of the class object. iid is the input parameter that is the interface ID which the caller uses to communicate
with the class object. In most cases, it is IID_IClassFactory. ppv is the return value; ppv is an indirect pointer to the IClassFactory
interface of the class factory object.
2. 6. Build lst31.dll.
3. 7. Register Lst31 COM server by providing .REG file.
4. Before a COM server can be used, the proper information such as CLSID and the full path of the DLL has to be stored in the registry
under the HKEY_CLASSES_ROOT\CLSID. This is required for every COM server because the registry information will be
accessed by the COM library to access the COM server location so that COM library can access the exported functions such as
DllGetClassObject from the COM server.
5. Listing 3.15 demonstrates the lst31.REG file for Lst31 COM server. Run regedit /s lst31.reg to register lst31.dll.
REGEDIT
HKEY_CLASSES_ROOT\CLSID\{4F126D90-1319-11d0-A6AC-00AA00602553}
HKEY_CLASSES_ROOT\CLSID\{4F126D90-1319-11d0-A6AC-00AA00602553}
[icc]\InprocServer32 = d:\areview\ch3\lst31\debug\lst31.dll
1. For every COM server, all the information has to be stored under the string representation of the CLSID, which is an immediate
subkey of the HKEY_CLASSES_ROOT\CLSID.
2. The string representation of the CLSID is in the CLSID's registry format, denoted as {CLSID}. The value associated with the
{CLSID} is the description of the COM object. The value associated with the InProcServer32 subkey is the full path to the 32 bit in-
proc server. InProcServer32 subkey is defined by OLE to indicate the full path to the 32-bit in-proc server. There are other subkeys,
such as Control and LocalServer32; subkeys are defined by OLE to serve their different purposes.
3. Control subkey with no value indicates that the COM server is an OLE control. The value associated with LocalServer32 subkey
indicates the full path to the local server.
4. For more information on this, please refer to Chapter 7 in Windows NT Registry Guide by Addison-Wesley.
5. For complete project, see lst31 directory in CD.
The following example will demonstrate how to use the COM server just created (lst31.dll) in a console application. This console
application is called lst31use.exe.
Note
COM server is an binary reusable component. It can be used in any applications such as Visual C++, and
Visual Basic.
1. 1. Initialize the COM library by calling CoInitialize(NULL). OleInitialize can be called instead of CoInitialize, because OleInitialize
initializes OLE library, which is a superset of the COM library.
2. 2. Create an instance of the CLowerStr by invoking
[icc]ILowerStr, (void**)&pLoweStr)
CLSCTX_INPROC_SERVER = 1,
CLSCTX_INPROC_HANDLER = 2,
CLSCTX_LOCAL_SERVER = 4
CLSCTX_REMOTE_SERVER = 16
} CLSCTX;
[icc]SERVER| CLSCTX_REMOTE_SERVER)
char *lpOutput;
pLowerStr->Release();
Note
OleUninitialize can be invoked to uninitialize OLE library. Because OLE library is a superset of COM library.
CoUnInitialize();
#include <objbase.h>
#include <initguid.h>
#include <stdio.h>
public:
};
[icc]0x25, 0x53}};
[icc]0x25, 0x53}};
void main()
HRESULT hr;
ILowerStr *pILowerStr;
hr = CoInitialize(NULL);
if(FAILED(hr))
exit(1);
IID_ILowerStr,(void**) &pILowerStr);
if(FAILED(hr))
if(hr == REGDB_E_CLASSNOTREG)
exit(1);
char *lpOutput;
pILowerStr->Release();
CoUninitialize();
1. Before lst31.dll can be invoked, the interface ILowerStr needs to be included and so does the CLSID and IID declared by the COM
server.
lst31.dll
1. This complete project is under the lst31use directory contained on the CD.
Assumed some new requirements coming up for lst31.dll COM server, a new version needs to be released. In a lot of circumstance, the
client application that uses the dll needs to be recompiled if the library is statically linked. But the COM client using the old version of
lst31.dll can use the new version of lst31.dll without any changes to source code and compilation.
Figure 3.7 illustrates the functionality provided by the new version of lst31.dll.
There are two interfaces exposed by the lst31.dll. ILowerStr is the old interface, IHelpLowerStr is the new interface. This new interface
includes one method HelpLower() which provides the help information, here for simplicity, S_OK will be returned.
The following steps demonstrate how to create the new lst31.dll COM server.
// {9EC1CA01-133F-11d0-A6AD-00AA00602553}
public:
};
1. 3. Implement IHelpLowerStr by modifying CLowerStr class. The changes are highlighted in bold font in Listing 3.18.
public:
};
public:
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP HelpLower();
CLowerStr();
~CLowerStr();
private:
DWORD m_dwRef;
};
HRESULT hr;
*ppv = NULL;
AddRef();
hr = S_OK;
AddRef();
hr = S_OK;
else
*ppv = NULL;
hr = E_NOINTERFACE;
return hr;
STDMETHODIMP CLowerStr::HelpLower()
return S_OK;
1. Because a new interface (IHelpLowerStr) is exposed by the CLowerStr object, QueryInterface needs to check IHelpLowerStr
interface, too.
2. 4. IUnknown] Rebuild lst311 project.
3. 5. Create Registry setting
REGEDIT
HKEY_CLASSES_ROOT\CLSID\{4F126D90-1319-11d0-A6AC-00AA00602553}
[icc]\InprocServer32 = d:\areview\ch3\lst311\debug\lst311.dll
Run lst31use.exe. It still works! But the value associated with the InprocServer32 has been changed, it is
d:\areview\ch3\lst311\debug\lst311.dll now. The reason is that from the client perspective, nothing really cares what dll name is associated
with the COM server. The client side is only interested in the CLSID exposed by the COM server. It is the COM library's role to locate the
COM server and instantiate the COM object. The library also returns the interface pointer requested by the client application.
The complete project is located under the lst311 directory on the CD.
To illustrate how to create an out-proc server, changes will be made to lst31.dll so that it is an out-proc server.
The out-proc server runs in a separate address space from the client application; all interface calls must be marshaled across process (data
gets copied). It is more reliable compared with in-proc server because the corruption of the out-proc server will not influence the process
space of the caller application, whereas in-proc server will destroy the caller's process space. The local out-proc server should be created
when an application supports automation. A remote out-proc server should be used to take advantage of resources on another machine.
object,
uuid(4F126D91-1319-11d0-A6AC-00AA00602553),
pointer_default(unique)
import "oaidl.idl";
1. An IDL file specifies the contract between the client and server using IDL.
2. It consists of two parts, the interface header and the interface body.
3. Table 3.1 illustrates the attributes for IDL. For more information on the IDL, please refer to the Microsoft RPC document.
Keyword Meaning object COM interface object. uuida universal unique identifier associated with particular interface usage
uuid(80FA6EE2-0120-11d0-A6A0-00AA00602553), dual Dual interface, which inherits from IDispatch, IUnknowndual, import Imports
idl file of the base interface import oaidl.idl out Output parameter [out, retval]BSTR *pBSTR in input parameter in, out Data is sent to the
object initialized and will be changed before sending it back. retval Designates the parameter that receives the return value helpstring Sets
the help stringhelpstring("test only") pointer_default Specifies the default pointer attribute unique pointer Attribute,designates a pointer as a
full pointer ref pointer attribute, Identifies a reference pointer.
Listing 3.20. Makefile for lower.idl.# Change CPU to MIPS or ALPHA for compiling on those platforms
CPU=i386
TARGETOS=BOTH
!include <win32.mak>
all: lower.dll
.cxx.obj:
.c.obj:
lower_i.obj : lower_i.c
dlldata.obj : dlldata.c
# run midl to produce the header files and the proxy file
$(link) \
-dll \
-entry:_DllMainCRTStartup$(DLLENTRY) \
-DEF:lower.def \
-out:lower.dll \
# Clean up everything
cleanall: clean
clean:
1. midl in Listing 3.20 stands for Microsoft IDL compiler. It takes the lower.idl file and generates the following files:
2. lower_p.c contains proxy/stub code
3. lower_i.c contains the actual definition of IIDs and CLSIDs.
4. lower.h contains the definition for the interface
5. dlldata.c regenerated by MIDL compiler on every idl file.
6. The midl switch /m_ext will support Microsoft extensions to DCE IDL. These extensions include: Interface definition for OLE
objects, multiple interfaces, enumeration, cpp_quote (quoted_string) and wide character types(wchar_t), and so on.
7. Let's say that if the IDL file includes
typedef enum
A=1,
B,
} BAD_ENUM;
2. /c_ext switch enables the use of C-language extensions in the IDL file. For instance, if // is used to comment the code, /c_ext switch
needs to be on.
3. 2. Lower.Def
LIBRARY LOWER
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
HRESULT RegisterClassFactory()
HRESULT hr;
CLowerStrClassFactory *pClassFactory;
if (pClassFactory != 0)
hr = CoRegisterClassObject(CLSID_CLowerStr,
(IUnknown *) pClassFactory,
CLSCTX_LOCAL_SERVER,
REGCLS_SINGLEUSE,
&g_dwRegister);
pClassFactory->Release();
hr = S_OK;
else
hr = E_OUTOFMEMORY;
return hr;
1. CoRegisterClassObject is the function that needs to be called on startup. It registers OLE so that other applications can connect to
this class object. There are 5 parameters in the function. In particular, REGCLS_SINGLEUSE indicates the types of connection to
the class object. If an application has connected to the class object via CoGetClassObject, no other application can connect to it.
2. There are other two connection types, they are REGCLS_MULTIPEUSE and REGCLS_MULTI_SEPARATE.
REGCLS_MULTIPEUSE indicates that multiple applications can connect to the class object via CoGetClassObject, whereas
REGCLS_MULTI_SEPARATE indicates that the application has separate control over the each copy of the class object context.
3. g_dwRegister is a returned value, identifying the class object registered. It will be used in CoRevokeClassObject function call.
HRESULT RevokeClassFactory()
HRESULT hr;
hr = CoRevokeClassObject(g_dwRegister);
return hr;
1. CoRevokeClassObject function indicates that the class object, previously registered with OLE via CoRegisterClassObject function is
no longer available for use.
HRESULT hr = S_OK;
int i;
MSG msg;
bRegisterServer = TRUE;
bUnregisterServer = TRUE;
if(bRegisterServer)
RegisterLocalServer(CLSID_CLowerStr);
return;
if(bUnregisterServer)
UnregisterLocalServer(CLSID_CLowerStr);
return;
hr = CoInitialize(NULL);
if (FAILED(hr)) {
return;
hr = RegisterClassFactory();
if (SUCCEEDED(hr))
TranslateMessage(&msg);
DispatchMessage(&msg);
RevokeClassFactory();
else
CoUninitialize();
return;
HRESULT hr;
LONG lError;
HKEY hKeyCLSID;
HKEY hKeyClassID;
DWORD dwDisposition;
char szServer[MAX_PATH];
char szClassID[39];
ULONG ulLength;
if (ulLength == 0)
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
lError = RegCreateKeyExA(
HKEY_CLASSES_ROOT,
"CLSID",
0,
"REG_SZ",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
0,
&hKeyCLSID,
&dwDisposition);
if (!lError) {
sprintf(szClassID,
"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
rclsid.Data4[0], rclsid.Data4[1],
rclsid.Data4[2], rclsid.Data4[3],
rclsid.Data4[4], rclsid.Data4[5],
rclsid.Data4[6], rclsid.Data4[7]);
lError = RegCreateKeyExA(hKeyCLSID,
szClassID,
0,
"REG_SZ",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
0,
&hKeyClassID,
&dwDisposition);
if (!lError) {
lError = RegCreateKeyExA(hKeyClassID,
"LocalServer32",
0,
"REG_SZ",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
0,
&hKey,
&dwDisposition);
if (!lError) {
lError = RegSetValueExA(hKey,
"",
0,
REG_SZ,
strlen(szServer) + 1);
RegFlushKey(hKey);
RegCloseKey(hKey);
RegCloseKey(hKeyClassID);
RegCloseKey(hKeyCLSID);
if (!lError)
hr = S_OK;
else
hr = HRESULT_FROM_WIN32(lError);
return hr;
HRESULT hr;
HKEY hKeyCLSID;
HKEY hKeyClassID;
long lError;
char szClassID[39];
lError = RegOpenKeyExA(
HKEY_CLASSES_ROOT,
"CLSID",
0,
KEY_ALL_ACCESS,
&hKeyCLSID);
if (!lError) {
sprintf(szClassID,
"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
rclsid.Data4[0], rclsid.Data4[1],
rclsid.Data4[2], rclsid.Data4[3],
rclsid.Data4[4], rclsid.Data4[5],
rclsid.Data4[6], rclsid.Data4[7]);
lError = RegOpenKeyExA(
hKeyCLSID,
szClassID,
0,
KEY_ALL_ACCESS,
&hKeyClassID);
if (!lError) {
RegCloseKey(hKeyClassID);
RegCloseKey(hKeyCLSID);
if (!lError)
hr = S_OK;
else
hr = HRESULT_FROM_WIN32(lError);
return hr;
1. lst32.exe provides the self registration features. Self registration means that the COM server can register itself. An in-proc server
registers by providing two entry points by the in-proc server, they are
HRESULT DllRegisterServer(void)
HRESULT DllUnRegisterServer(void);
1. DllRegisterServer entry point adds or updates registry information for all the classes implemented by the in-proc server. The
DllUnRegisterServer entry point removes all the information for the in-proc server from the registry.
2. For an out-proc server, there is no way to publish well-known entry points. The self registration for out-proc server is supported by
using special command-line flags. The command-line flags are
1. /regserver argument should add the registry information for all classes implemented by the out-proc server and then exit. /unregister
argument should do all the necessary uninstallation and then exit.
2. lst32.exe supports the /regserver and /unregserver argument.
3. 6. Register lst32.exe by running lst32 /regserver.
4. Lst32.exe out-proc server needs to be registered before being used. Without proper information in the registry, the out-proc server
will not be seen by any other COM applications or COM library.
5. Because lst32.exe supports self registration, lst32.exe can be registered by invoking lst32 /regserver.
A COM client will be created to use this out-proc (lst32.exe) COM server. Listing 3.22 demonstrates how to use lst32.exe.
#include <windows.h>
#include <stdio.h>
#include <olectl.h>
#include <initguid.h>
#include <olectlid.h>
// {4F126D90-1319-11d0-A6AC-00AA00602553}
{0x4f126d90, 0x1319, 0x11d0, {0xa6, 0xac, 0x0, 0xaa, 0x0, 0x60, 0x25, 0x53}};
// {4F126D91-1319-11d0-A6AC-00AA00602553}
{0x4f126d91, 0x1319, 0x11d0, {0xa6, 0xac, 0x0, 0xaa, 0x0, 0x60, 0x25, 0x53}};
public:
};
HRESULT hr;
hr = CoInitialize(NULL);
if (FAILED(hr))
exit(1);
hr = CoCreateInstance(CLSID_CLowerStr, 0, CLSCTX_LOCAL_SERVER,
IID_ILowerStr,(void**)&pILowerStr);
if (FAILED(hr))
if (hr == REGDB_E_CLASSNOTREG)
exit(1);
char *lpOutput;
pILowerStr->Lower("HELLO", &lpOutput);
pILowerStr->Release();
CoUninitialize();
From Listing 3.22, no changes need to be made from lst31use.cpp to lst32use.cpp except in the CoCreateInstance activation call. The
execution context changes from CLSCTX_INPROC_SERVER to CLSCTX_LOCAL_SERVER.
Active Template Library is an OLE COM AppWizard providing the framework for building COM servers.
From the previous section, a lot of implementation, such as IUnknown and IClassFactory can be reused as noticed. ATL encapsulates these
implementations in a template class so that the COM server functionality can be concentrated.
lst33.dll will be created to illustrate the ATL. lst33.dll provides the same functionality as lst31.dll Version 1.
1. 1. From the File menu, choose new, and select project workspace.
2. 2. In the new project workspace dialog box, select OLE COM Appwizard. Enter lst33 in the name text box. Click the Create
button. The dialog box in Figure 3.8 will be displayed.
1. 3. In the dialog box shown in Figure 3.8, select the generate IDL only option. Note: This option can be chosen only when MIDL
version 3.0 or higher is available.
2. 4. In dialog box shown in Figure 3.8, select the Choose Custom Interface option. Note: Custom interface refers to the fact that all the
interfaces are inherited from IUnknown.
3. 5 Click Finish button.
The OLE COM AppWizard will generate the new skeleton project with the following files.
StdAfx.cpp: source file that includes just the standard include file.
StdAfx.h: include file for standard system include files.
lst33.cpp: the DLL initialization code, the same role as in example 1.
lst33.def: As described before, an in-proc server needs to expose DllGetClassObject so that COM can use this exported function.
DllRegisterServer should be exported to support the self-registration function. The wizard will generate all these exported functions
This also conforms to the DCOM design, because all of the custom interface has been marshaled.
1. 1. There is one interface in this method, the lst33.idl will be changed to add the Upper method.
2. The changes in the skeleton code created by the ATL will be in bold font.
import "oaidl.idl";
};
[2] Implement this method for ILst33 interface, exposed by the lst33 object class by
adding the definition in the lst33obj.h
class CLst33Object :
public ILst33,
public CComObjectBase<&CLSID_Lst33>
public:
CLst33Object() {}
BEGIN_COM_MAP(CLst33Object)
COM_INTERFACE_ENTRY(ILst33)
END_COM_MAP()
// to support aggregation
DECLARE_AGGREGATABLE(CLst33Object)
// ILst33
public:
};
int i;
*(*pOutput+i) = '\0';
return S_OK;
1. The implementation of the Lower method is the same as implemented in lst31.dll, and lst32.dll.
2. After the custom interface has been implemented, the following steps need to follow to create this COM server.
3. Run the command midl lst33.idl. This requires MIDL 3.0 or higher. Otherwise, the following error will be generated:
Processing .\lst33.idl
1. In order to avoid this error, MIDL 3.0 must be available. It is contained in the Win32 SDK.
2. After lst33.idl is successfully compiled, the following files will be generated:
dlldata.c:
lst33_i.c
1. Before this COM server can be used, it needs to be registered first by running regsvr32 lst33.dll. Or the these steps can be followed
to register the server after compile.
2. 1. Choose Build|Setting.
3. 2. Select Custom Build tab in the Project Setting dialog box displayed in Figure 3.9.
regsvr32 /s /c "$(TargetPath)"
$(OutDir)\regsvr32.trg
1. The Microsoft Developer Studio will perform the following Custom Build Step
A console application will be created to use the lst33.dll COM server shown in Listing 3.23.
#include <objbase.h>
#include <initguid.h>
#include <stdio.h>
#include "..\lst33\lst33.h"
void main()
HRESULT hr;
CLSID clsid;
hr = CoInitialize(NULL);
hr = CLSIDFromProgID(L"LST33.Lst33Object.1",&clsid);
hr = CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER,
IID_ILst33,
(LPVOID*)&m_pILst33);
if(FAILED(hr))
CoUninitialize();
return;
char *lpLowerString;
m_pILst33->Release();
CoUninitialize();
Here, CLSIDFromProgID is used to retrieve the CLSID of the class object by providing ProgId for this COM server. No code change is
made compared with lst31use.cpp.
Summary
From the previous example, it is not difficult to see that COM supports
Network independence: An out-of-process COM server can run locally as the client application or remotely. A COM server
can be DLL-based or EXE-based without the COM application doing extra work except informing the COM library about the
execution context of the class object.
Language independence: A COM server can be used in C++ applications and any other applications such as Visual Basic. For
a basic COM object that only supports IUnknown interface, there is a lot of work to be done so that Visual Basic can use the
COM server. For examples on how to use basic COM objects in Visual Basic, please refer to "MFC/COM Objects 6: Using
COM Objects from Visual Basic" in MSDN.
In order for Visual Basic application easily to use COM server, the IDispatch interface is defined in COM. For more information on this,
please refer to Chapter 4, "Creating OLE Automation Server."
Chapter 4
Creating OLE Automation Server
by Weiying Chen
Chapter 4
by Weiying Chen
An OLE Automation server exposes automation objects that have methods and properties as their external interfaces. The
methods and properties exposed by the automation objects can be directly accessed by the automation controllers such as
Visual Basic or by invoking methods defined in the IDispatch interface.
In this chapter, an automation server will be created through the implementation of the IDispatch interface to illustrate the
fundamental OLE automation concept. This automation server will be used in both C++ and Visual Basic.
Then, this automation server will be created through the use of MFC AppWizard (exe), Control Wizard, and the Active
Template Library (ATL).
At the end of the chapter, this automation server will be used in Internet Explorer 3.0 and the HTTP Web Server.
lst41.exe is an automation server implemented without using any wizards or code generation tools. This automation server exposes
one method called GetMachineName. GetMachineName has no input parameter. The return value is the name of the computer
where the application runs.
To expose the objects, IDispatch interface must be implemented. There are two ways to implement IDispatch interface. One way is
to implement four methods in the IDispatch interface. The other way used in lst41.exe is to expose the objects through OLE
automation by using the CreateStdDispatch method. CreateStdDispatch creates a standard implementation of the IDispatch
interface through a single function call.
uuid(9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B),
lcid(0x0409),
version(1.0)
library Lst41
importlib("stdole32.tlb");
odl,
uuid(9FBBEDE3-1B40-11d0-88E0-00AA004A7C7B),
BSTR GetMachineName(void);
uuid(9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B),
dispinterface DLst41
interface ILst41;
uuid(9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B),
helpstring("Lst41")
coclass CLst41
dispinterface DLst41;
interface ILst41;
};
BSTR GetMachineName(void);
uuid(9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B),
dispinterface DLst41
interface ILst41;
1. can be described as
dispinterface DLst41
BSTR GetMachineName(void);
The coclass statement defines the class ID for class name CLst41 and the interfaces supported by CLst41.
1. mktyplib is a type library creation tool. It processes lst41.odl scripts and produces ilst41.h header file and a type library
lst41.tlb. lst41.tlb can be read by ITypeInfo or ITypeLib interfaces.
2. The ilst41.h header file is shown in Listing 4.2.
#ifndef _Lst41_H_
#define _Lst41_H_
DEFINE_GUID(LIBID_Lst41,0x9FBBEDE2L,0x1B40,0x11D0,0x88,0xE0,0x00,
0xAA,0x00,0x4A,0x7C,0x7B);
#ifndef BEGIN_INTERFACE
#define BEGIN_INTERFACE
#endif
DEFINE_GUID(IID_ILst41,0x9FBBEDE3L,0x1B40,0x11D0,0x88,0xE0,0x00,
0xAA,0x00,0x4A,0x7C,0x7B);
#undef INTERFACE
DECLARE_INTERFACE_(ILst41, IUnknown)
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS
/* IUnknown methods */
#endif
/* ILst41 methods */
};
DEFINE_GUID(DIID_DLst41,0x9FBBEDE4L,0x1B40,0x11D0,0x88,0xE0,0x00,
0xAA,0x00,0x4A,0x7C,0x7B);
#undef INTERFACE
DECLARE_INTERFACE_(DLst41, IDispatch)
BEGIN_INTERFACE
#ifndef NO_BASEINTERFACE_FUNCS
/* IUnknown methods */
/* IDispatch methods */
STDMETHOD(GetTypeInfo)(
THIS_
UINT itinfo,
LCID lcid,
STDMETHOD(GetIDsOfNames)(
THIS_
REFIID riid,
UINT cNames,
LCID lcid,
STDMETHOD(Invoke)(
THIS_
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
#endif
};
DEFINE_GUID(CLSID_CLst41,0x9FBBEDE5L,0x1B40,0x11D0,0x88,0xE0,0x00,
0xAA,0x00,0x4A,0x7C,0x7B);
#ifdef __cplusplus
class CLst41;
#endif
#endif
dispinterface DLst41
interface ILst41;
1. has been expanded into seven methods, three IUnknown methods and four IDispatch methods, which include
GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, and Invoke.
2. Method GetTypeInfoCount retrieves the number of type information interfaces provided by an object. If the object provides
the type information, pctinfo will be 1, otherwise it will be 0.
3. GetTypeInfo method retrieves a type-information object. This object can be used to get the type information for an
interface.
4. GetIDsOfNames method retrieves a DISPID corresponding to the methods and arguments provided.
5. Invoke method accesses the properties and methods given their DISPID.
6. 3. Implement the ILst41 interface. (See Listing 4.3.)
#include <objbase.h>
#include "clsid.h"
#include "ilst41.h"
class CLst41;
public:
STDMETHOD_(ULONG, AddRef)(THIS);
STDMETHOD_(ULONG, Release)(THIS);
STDMETHOD_(BSTR, GetMachineName)(THIS);
CLst41* m_pLst41;
};
public:
CLst41();
~CLst41();
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
private:
};
#include <objbase.h>
#include "resource.h"
#include "lst41c.h"
BSTR bsz;
UINT cch;
cch = strlen(lpString);
if(bsz == NULL)
return NULL;
if(cch > 0)
bsz[cch] = NULL;
return bsz;
1. Function CreateBSTR will convert an ASCII string to a wide character (Unicode) string. In particular, function
MultiByteToWideChar will convert an ASCII string pointed by lpString to a wide character string pointed by bsz. CP_ACP
specifies the code page to be used by the conversion. It stands for ANSI code page. Other code page includes CP_MACCP
that indicates a Macintosh code page, and CP_OEMCP that indicates an OEM code page.
STDMETHODIMP_(BSTR)
CLst41I::GetMachineName()
BSTR b;
ULONG ulLen;
char *lpName;
ulLen = MAX_PATH;
GetComputerName(lpName, &ulLen);
b = CreateBSTR(lpName);
return b;
STDMETHODIMP_(ULONG) CLst41I::AddRef()
return m_pLst41->AddRef();
STDMETHODIMP_(ULONG) CLst41I::Release()
return m_pLst41->Release();
IUnknown FAR*
HRESULT hresult;
ITypeLib* ptlib;
ITypeInfo* ptinfo;
IUnknown* punkStdDisp;
if (hresult != S_OK)
return NULL;
if (hresult != S_OK)
return NULL;
ptlib->Release();
hresult = CreateStdDispatch(punkController,pProgInterface,
ptinfo, &punkStdDisp);
if (hresult != S_OK)
return NULL;
ptinfo->Release();
return punkStdDisp;
1. Function CreateDispatchInterface will first use registry information to load the type library by invoking the
LoadRegTypeLib function. There are four parameters. LIBID_Lst41 is the Library ID being loaded. 1 is the type library
(lst41.tlb)'s major version number. 0 is the lst41.tlb's minor version number. 0x0409 is the U.S English, which is the
library's national language code. ptlib is a indirect pointer to ITypeLib interface.
2. If the type library information cannot be loaded from the registry, LoadTypeLib function will be called. LoadTypeLib loads
and registers the type library stored in lst41.tlb.
3. GetTypeInfoOfGuid will retrieve IID_ILst41's type description, and return ptinfo, which is a indirect pointer to ITypeInfo.
4. After successfully invoking GetTypeInfoOfGuid, the CreateStdDispatch function will be invoked. This function creates a
standard implementation of the IDispatch interface through one single function call. There are four parameters in this
function. punkController is a pointer to the ILst41 IUnknown implementation. pProgInterface is a pointer to the object to
expose. ptinfo is a pointer to the ILst41's type information that describes the exposed object. punkStdDisp is an indirect
pointer to the ILst41 IDispatch interface implementation.
CLst41::CLst41()
m_refs = 1;
m_disp_interface = NULL;
m_prog_interface->m_pLst41 = this;
CLst41::~CLst41()
delete m_prog_interface;
CLst41 * CLst41::Create()
CLst41* pLst41;
IUnknown* punkStdDisp;
if(pLst41 == NULL)
return NULL;
pLst41->m_prog_interface);
if (punkStdDisp == NULL) {
pLst41->Release();
return NULL;
pLst41->m_disp_interface = punkStdDisp;
return pLst41;
if (riid == IID_IUnknown)
*ppv = this;
*ppv = &m_prog_interface;
else
*ppv = NULL;
return ResultFromScode(E_NOINTERFACE);
AddRef();
return S_OK;
1. QueryInterface will check the interface identifier riid. If riid is equal to IID_IUnknown, this value will be assigned to *ppv
because CLst41 is the controlling IUnknown. If riid is equal to IID_IDispatch or DIID_DLst41 or IID_ILst41, *ppv will be
the standard dispatch interface. Otherwise, *ppv will be NULL and the error code E_NOINTERFACE will be returned.
STDMETHODIMP_(ULONG) CLst41::AddRef()
return ++m_refs;
STDMETHODIMP_(ULONG) CLst41::Release()
if(--m_refs == 0)
if(m_disp_interface != NULL)
m_disp_interface->Release();
PostQuitMessage(0);
delete this;
return 0;
return m_refs;
1. PostQuitMessage function will indicate to the system that the thread will be terminated by posting a WM_QUIT message to
the thread's message queue and return. When the thread receives the WM_QUIT from the message queue, it will terminate
the message loop and return the control to the window.
2. 4. Implement the Class Object for CLst41.
3. Listing 4.4 demonstrates the CLst41 class factory implementation.
public:
CLst41CF();
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
REFIID riid,
void** ppv);
STDMETHOD(LockServer)(BOOL fLock);
private:
ULONG m_refs;
};
CLst41CF::CLst41CF()
m_refs = 1;
IClassFactory* CLst41CF::Create()
AddRef();
*ppv = this;
return S_OK;
*ppv = NULL;
return ResultFromScode(E_NOINTERFACE);
STDMETHODIMP_(ULONG) CLst41CF::AddRef()
return ++m_refs;
STDMETHODIMP_(ULONG) CLst41CF::Release()
if(--m_refs == 0)
delete this;
return 0;
return m_refs;
void** ppv)
return S_OK;
#include <objbase.h>
#include "lst41c.h"
#include <stdio.h>
void main()
MSG msg;
DWORD g_dwLst41CF = 0;
HRESULT hr;
return;
return;
pcf = CLst41CF::Create();
if (pcf == NULL)
goto Clean;
hr = CoRegisterClassObject(CLSID_CLst41,
pcf,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&g_dwLst41CF);
if (hr != NOERROR)
goto Clean;
pcf->Release();
TranslateMessage(&msg);
DispatchMessage(&msg);
Clean:
if(g_dwLst41CF != 0)
CoRevokeClassObject(g_dwLst41CF);
if (g_pLst41 != NULL)
g_pLst41->Release();
OleUninitialize();
1. In Listing 4.5, first OleInitialize is invoked to initialize the OLE library. This function must be called before calling any
OLE functions. Then CLst41::Create is invoked to create a single global instance of CLst41. CLst41CF::Create is called to
create an instance of the class factory for CLst41. Register the class factory by invoking CoRegisterClassObject. Then the
message loop is provided. When the WM_QUIT message is received, the message loop will be terminated.
CoRevokeClassFactory will be called to inform OLE that the object is no longer available if the class factory was
successfully created earlier. Finally, OleUninitialize will be called to uninitialize the OLE library and release all the
resources.
2. 6. Create the registration entry.
3. lst41.exe has to be registered before being used. Listing 4.6 shows the registration file for lst41.exe.
REGEDIT
HKEY_CLASSES_ROOT\Lst41.Application.1\Clsid =
{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}
HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B} =
IDispatch Lst41
HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}\ProgID =
Lst41.Application.1
HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}\
VersionIndependentProgID = Lst41.Application
HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}\
HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}
HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}\
HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}\
1.0\HELPDIR =
HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}\
1.0\409\win32 = c:\ch4\lst41\lst41.tlb
HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B} = DLst41
HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B}\
ProxyStubClsid = {00020420-0000-0000-C000-000000000046}
HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B}\
NumMethod = 7
HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B}\
BaseInterface = {00020400-0000-0000-C000-000000000046}
1. In Listing 4.6, Lst41.Application.1 is the ProgID, which is required for any automation objects. It is used by an automation
controller to reference an automation server. LocalServer32 subkey specifies the full path to the 32-bit automation server
lst41.exe.
lst41use.exe is a C++ application that uses lst41.exe. It uses IDispatch to access exposed objects.
Listing 4.7 demonstrates how to use IDispatch to access the methods exposed by lst41.exe.
#include <objbase.h>
#include <initguid.h>
#include <stdio.h>
DEFINE_GUID(CLSID_CLst41,0x9FBBEDE5L,0x1B40,0x11D0,0x88,0xE0,
0x00,0xAA,0x00,0x4A,0x7C,0x7B);
DWORD dwError;
LPSTR lpString;
if(pszW == NULL)
return NULL;
cCharacters = wcslen(pszW) + 1;
cbAnsi = cCharacters * 2;
if(NULL == lpString)
return NULL;
dwError = GetLastError();
CoTaskMemFree(lpString);
lpString = NULL;
return lpString;
Function BstrToSz converts a wide-character (Unicode) string to an ASCII string. CoTaskMemAlloc allocates a memory block
using the default allocator. It behaves the same way as IMalloc::Alloc. The application should always check the return value from
this function. Function WideCharToMultiByte maps a wide character string pointed by pszW to an ASCII string pointed by
lpString.
void main()
HRESULT hr;
IDispatch *pIDispatch;
DISPID dispidGetMachineName;
IUnknown *pIUnknown;
VARIANT varResult;
hr = OleInitialize(NULL);
(void**)&pIUnknown);
if(FAILED(hr))
pIUnknown->QueryInterface(IID_IDispatch, (void**)&pIDispatch);
pIUnknown->Release();
pIDispatch->GetIDsOfNames(IID_NULL,
&pGetMachineName,
1, LOCALE_SYSTEM_DEFAULT, &dispidGetMachineName);
DISPATCH_METHOD, &dispparms,
pIDispatch->Release();
CoUninitialize();
Function CoCreateInstance creates an CLst41 object. Because lst41.exe is a local server, execution context CLSCTX_SERVER is
used. CLSCTX_SERVER is defined as
GetIDsOfNames retrieves the DISPID and will be stored in dispidGetMachineName. There are five parameters in
GetIDsOfNames. IID_NULL must be NULL; it is reserved for future use. pGetMachineName points to the method name. 1
indicates there is only one name to be mapped. LOCALE_SYSTEM_DEFAULT indicates the locale context in which to interpret
the name.
Invoke accesses the GetMachineName method by providing its DISPID varResult is used to hold the return value from
GetMachineName method.
With Visual Basic, using lst41.exe is straightforward. Listing 4.8 demonstrates how to use lst41.exe.
Dim x As Object
Set x = CreateObject("lst41.application.1")
strMachineName = x.getmachinename
MsgBox strMachineName
In Listing 4.8, variable x is declared as an object and assigned the return of CreateObject call. The parameter in the CreateObject
call is the ProgID of lst41.exe.
After the automation server (lst41.exe) is instantiated, the method GetMachineName can be invoked; the return value is assigned
to strMachineName.
To try the example, place a CommandButton control on a form, and type Listing 4.8 into the command button's click proc. Run the
example and click the Command1 button. A dialog box with the computer name on which the application is running will be
displayed.
There are two MFC ClassWizard option provided by the New Project Workspace as shown in Figure 4.1.
MFC AppWizard is designed to configure the skeleton of a new C++ application using the MFC.
MFC AppWizard (exe) is designed to create MFC extension .EXE, whereas MFC AppWizard (dll) is designed to create an MFC
extension .DLL. The following steps illustrate how to create lst42.exe.
1. 1. Choose File|New. In the New dialog box, select the file type "Project Workspace."
2. 2. In the New Project Workspace dialog box, choose the MFC AppWizard (exe) in the type box. Type lst42 in the Name
edit box and click the Create... button shown in Figure 4.2.
Note
The single-document option allows the application to work with one document at a time
The multiple-document option allows the application to work with multiple documents, each
document with its own view.
1. 4. Choose the Mini-server option and the OLE automation as shown in Figure 4.4.
The Mini-server option allows the application to create and manage the compound document object.
Mini-server cannot run standalone, and only supports embedded objects, whereas Full-server can run
standalone and supports both linked and embedded objects.
The OLE automation option allows the application to be accessed by the automation clients, such as
Visual Basic, Access, Excel.
1. 5. Click the Finish button. The files listed in Figure 4.5 will be generated by the MFC AppWizard (exe). Click the OK
button to generate all the files.
1. Among the files listed in Figure 4.5, the following classes and source files are specific to OLE:
2. SrvrItem.h, SrvrItem.cpp: This is the class that connects CLst42Doc to the OLE system. It also optionally provides links to
the document.
3. IpFrame.h, lpFrame.cpp: This class is derived from COleIPFrameWnd and controls all frame features during in-place
activation.
4. lst42.reg: This is a .REG file. This file can be used to manually register the application.
5. lst42.odl: This is the .ODL file, which is read by MkTypLib to create a type library (.TLB). A Type library is used by
automation clients to retrieve information about the server's properties and its data types, methods, and its return value and
parameters. Listing 4.9 shows the lst42.odl file.
// This file will be processed by the Make Type Library (mktyplib) tool to
[ uuid(06B70DA1-1818-11D0-A6AD-00AA00602553), version(1.0) ]
library Lst42
importlib("stdole32.tlb");
[ uuid(06B70DA2-1818-11D0-A6AD-00AA00602553) ]
dispinterface ILst42
properties:
//{{AFX_ODL_PROP(CLst42Doc)
//}}AFX_ODL_PROP
methods:
//{{AFX_ODL_METHOD(CLst42Doc)
//}}AFX_ODL_METHOD
};
[ uuid(06B70DA0-1818-11D0-A6AD-00AA00602553) ]
coclass Document
};
//{{AFX_APPEND_ODL}}
};
[ uuid(06B70DA1-1818-11D0-A6AD-00AA00602553), version(1.0) ]
1. is the attribute, which associates information with the library. The uuid is for the type library. All applications that expose
type information must register the information to the system registry so that it is available to type browsers or any
automation clients.
2. The definition on the library Lst42 is enclosed between { and }. In the definition. import indicates that lst42.tlb imports the
standard OLE library stdole32.tlb. dispinterface defines a set of methods and properties that can be invoked by
IDispatch::Invoke. coclass named Document indicates that supported interface ILst42 in this component object (lst42.exe).
A GUID must be given on a coclass. This GUID is the same as CLSID registered in the system.
3. 6. From the View menu, choose the ClassWizard command, select the OLE Automation tab, and choose CLst42Doc in the
Class name dropdown list box shown in Figure 4.6.
1. 7. Click the Add Method... button in Figure 4.6. Type GetMachineName in the External name dropdown combo box, select
BSTR in the Return type dropdown list box, and click OK button in the Add Method dialog box displayed in Figure 4.7.
External name is used by the automation clients to invoke the exposed method, whereas Internal name
is the member function that implements the exposed method.
1. 8. Modify the class lst42Doc.cpp. Add the code shown in Listing 4.10.
BSTR CLst42Doc::GetMachineName()
CString strResult;
ULONG ulLen;
char *lpName;
ulLen = MAX_PATH;
GetComputerName(lpName, &ulLen );
strResult = lpName;
delete [] lpName;
return strResult.AllocSysString();
1. Lst42.exe only has one exposed method, GetMachineName. Listing 4.11 demonstrates the dispatch map generated by the
MFC AppWizard (exe).
BEGIN_DISPATCH_MAP(CLst42Doc, COleServerDoc)
//{{AFX_DISPATCH_MAP(CLst42Doc)
VT_BSTR, VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
1. Listing 4.11 is abridged from lst42Doc.cpp to emphasize the essential parts. DISP_FUNCTION macro is used in the
dispatch map to define an exposed method. The dispatch map is a mechanism provided by MFC to dispatch the request
made by the automation clients, such as calling the methods and accessing the properties.
2. The Dispatch map indicates the external and internal name of the properties and methods, as well as the properties' data
types and method's argument and return type.
3. In Listing 4.11, CLst42Doc is the name of the class. COleServerDoc is the base class. DISP_FUNCTION macro is used to
define an automation method. GetMachineName is the external name used by the automation clients, GetMachineName is
the internal name, VT_BSTR is the return type, VTS_NONE is the method's parameter list. In this case, there is no input
parameter.
4. Besides the DISP_FUNCTION macro defined in the dispatch map, other macros are provided as shown in the following
list:
5. DISP_PROPERTY defines an automation property.
6. DISP_PROPERTY_EX defines an automation property and names the "get" and "set" functions.
7. DISP_PROPERTY_NOTIFY defines an automation property with notification.
8. DISP_PROPERTY_PARAM defines an automation property, names the "get" and "set" functions, and an index parameter.
9. DECLARE_DISPATCH_MAP is used in the class declaration to indicate that a dispatch map will be used.
10. For example,
...
//{{AFX_DISPATCH(CLst42Doc)
//}}AFX_DISPATCH
DECLARE_DISPATCH_MAP()
DECLARE_INTERFACE_MAP()
1. is used in the lst42Doc.h to indicate that a dispatch map will be used in CLst42Doc class.
BEGIN_DISPATCH_MAP is used in the class implementation to indicate the start of the dispatch map definition as shown
in Listing 4.11.
END_DISPATCH_MAP is used in the class implementation to indicate the end of the dispatch map definition as shown in
Listing 4.11.
Note
Automation server generated by the MFC AppWizard (exe) provides the self registration features. In
other words, lst42.exe accepts the program argument /regserver to register itself to the system registry.
After lst42.exe is registered with the system, it can be used by automation clients, such as Visual Basic, Access, Excel. lst42.exe
can be accessed by using IDispatch interface in the C++ application.
The following example demonstrates how lst42.exe be used in Visual Basic 4.0.
With Visual Basic, using lst42.exe is straightforward. Listing 4.12 demonstrates how to use lst42.exe inside Visual Basic.
Dim x As Object
Set x = CreateObject("lst42.document")
strMachineName = x.getmachinename
MsgBox strMachineName
In Listing 4.12, variable x is declared as an object and assigned the return of the CreateObject call. The parameter in the
CreateObject call is the ProgID of lst42.exe. For any application generated by the MFC AppWizard with OLE automation enabled,
the ProgID is always Name for the new project workspace plus the .document.
After the automation server (lst42.exe) is instantiated, the method GetMachineName can be invoked and the return value is
assigned to strMachineName.
To try the example, place a CommandButton control on a form and type Listing 4.13 into the command button's click proc. Run
the example and click the Command1 button. A dialog box with the name of the computer the application is running on will be
displayed.
Besides using MFC AppWizard (exe) to create the automation server, Control Wizard can be used. The IsInvokeAllowed()
method is required to be overriden to support the automation.
The following steps demonstrates how to implement an automation server supporting the same functionality as lst41.exe.
1. 1. Choose File|New. In the New dialog box, select the file type "Project Workspace."
2. 2. In the New Project Workspace dialog box, choose the OLE ControlWizard. Type lst43 in the Name edit box and click
the Create button shown in Figure 4.8.
1. 3. Click the Finish button. Files shown in Figure 4.9 will be generated. Click the OK button in the dialog box displayed in
Figure 4.9.
Among the files shown in Figure 4.9, the following files are specifically related to the Control.
lst43Ctl.h, lst43Ctl.cpp: all the methods, properties, and events will be placed in this class.
lst43Ctl.bmp: containing a bitmap displayed in the container's toolbox, such as Visual Basic's toolbox.
1. 4. Choose View|ClassWizard. Select the OLE Automation tab shown in Figure 4.10, click the Add Method button, enter
GetMachineName, and choose BSTR as the return type in the Add Method dialog box displayed in Figure 4.11.
BSTR CLst43Ctrl::GetMachineName()
CString strResult;
ULONG ulLen;
char *lpName;
ulLen = MAX_PATH;
GetComputerName(lpName, &ulLen );
strResult = lpName;
delete [] lpName;
return strResult.AllocSysString();
1. 6. Override IsInvokeAllowed.
2. Add the following declaration in class CLst43Ctrl in lst43Ctl.h.
private:
return TRUE;
Dim x As Object
Set x = CreateObject("lst43.lst43ctrl.1")
strMachineName = x.getmachinename
MsgBox strMachineName
1. Lst43.ocx supports self registration. To register lst43.ocx, run regsvr32 lst43.ocx, to unregister lst43.ocx, run regsvr32 /u
lst43.ocx.
2. In Listing 4.13, ProgID for lst43.ocx is lst43.lst43ctrl.1. The default ProgID generated by OLE ControlWizard is always the
name for the new project workspace plus .lst43ctrl.1.
3. There is one method exposed by lst43.ocx. Listing 4.14 is the dispatch map generated by the ClassWizard.
BEGIN_DISPATCH_MAP(CLst43Ctrl, COleControl)
//{{AFX_DISPATCH_MAP(CLst43Ctrl)
VT_BSTR, VTS_NONE)
//}}AFX_DISPATCH_MAP
VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
1. In Listing 4.14, DISP_FUNCTION is exactly the same as in Listing 4.12, except the class name is CLst43Ctrl instead of
CLst42Doc.
ATL provides an OLE COM AppWizard to create COM objects. It supports COM objects with a custom interface, IDispatch, and
IConnectionPoint and so on. ATL is designed to create COM objects. For more information on ATL, please refer to Appendix E,
"ActiveX Template Library."
The following example illustrates how to use ATL to create an automation server supporting the same functionality as lst41.exe.
1. 1. Choose File|New. In the New dialog box, select "Project Workspace" in the type box.
2. 2. In the New Project Workspace dialog box, choose the OLE COM AppWizard. Type lst44 in the Name edit box and click
the Create button shown in Figure 4.12.
The Dual Interface option indicates that the interface supports IDispatch and IUnknown.
1. The Custom Interface option indicates the interface only supports IUnknown.
import "oaidl.idl";
1. Add the following code in bold font to interface ILst44 definition in lst44.odl.
};
public:
STDMETHOD(GetMachineName)(BSTR *retval);
BSTR bsz;
UINT cch;
cch = strlen(lpa);
if (bsz == NULL)
return NULL;
if (cch > 0)
bsz[cch] = NULL;
return bsz;
ULONG ulLen;
char *lpName;
ulLen = MAX_PATH;
GetComputerName(lpName, &ulLen);
if(ulLen == 0)
*retval = NULL;
else
*retval = CreateBSTR(lpName);
return S_OK;
1. In Listing 4.15, function CreateBSTR accepts an ASCII string, and converts into a Unicode string. Unicode stands for a 16-
bit character set that can encode all known character sets and is used as a world-wide character encoding standard.
2. 5. Before building the project, using the midl lst44.idl command to generate a source file for a custom OLE interface.
3. 6. Build the project to generate lst44.dll.
lst44.dll can be used in Visual Basic the same way as lst42.exe. Instead of input Listing 4.11, code in Listing 4.16 should be
entered.
Dim x As Object
Set x = CreateObject("lst44.lst44object.1")
strMachineName = x.getmachinename
MsgBox strMachineName
Before running the preceding code, lst44.dll needs to be registered by running regsvr32 lst44.dll.
In Listing 4.16, ProgID for lst44.dll is lst44.lst44object.1. The default ProgID generated by OLE COM AppWizard is the name of
the project workspace plus .lst44object.1.
The default ProgID can be modified by replacing the code in bold font as shown in the following; this code is contained in
lst44.cpp.
BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()
Reusable components such as automation server not only can be used in the automation clients such as Visual Basic, Access,
Excel, or in the application that accesses the automation server via IDispatch. Microsoft Internet Explorer (IE) 3.0 also supports
the use of automation server.
The automation server inside IE 3.0 requires an <OBJECT> tag to be used to include the object.
Listing 4.17 demonstrates how to use lst44.dll inside an HTML page and displayed in IE 3.0 browser.
<HTML>
<HEAD>
<OBJECT classid="clsid:C566CC25-182E-11D0-A6AD-00AA00602553"
id= MachineName
</OBJECT>
<SCRIPT language="VBScript">
msgbox MachineName.getmachinename
</SCRIPT>
</HEAD>
</HTML>
In Listing 4.17, "clsid:..." is the string representation of the CLSID, denoted as {CLSID}, for lst44.dll. The following steps
illustrates how to get the {CLSID} for lst44.dll.
1. 1. Run regedt32
2. 2. Go to HKEY_CLASSES_ROOT and then find lst44.lst44object.1 subkey in HKEY_CLASSES_ROOT.
3. 3. Get the value of the CLSID subkey under lst44.lst44object.1, shown in Figure 4.14.
1. 4. Double-click the data, and a string editor dialog box will be displayed shown in figure 4.15.
1. The collection name is optional; if it is not provided, the server will search the collection in the following order:
1. The ServerVariables collection supports all HTTP headers by prefixing them with HTTP_ and the variables including
AUTH_TYPE, CONTENT_LENGTH, CONTENT_TYPE, GATEWAY_INTERFACE, PATH_INFO,
PATH_TRANSLATED, QUERY_STRING, REMOTE_ADDR, REMOTE_HOST, REMOTE_IDENT, REMOTE_USER,
REQUEST_METHOD, SCRIPT_NAME, SERVER_NAME, SERVER_PORT, SERVER_PROTOCOL, and
SERVER_SOFTWARE.
2. The HTTP headers can be found at http://www.w3.org.
3. The QueryString collection provides access to all parameters in the Get method.
4. The Body collection provides access to all parameters in the Post method
5. The Response components exposes methods or properties including Add(header-value, header-name),
AppendToLog(string), Clear, Expires, Redirect(url), SetCookie(name, value[expires, [domain,[path,[secure,]]]]), and
Status.
6. The Server components exposes three methods: HTMLEncode (string), Include(filename), and MapPath(vitual path).
7. Listing 4.18 demonstrates how to use lst44.dll on the Web server side so that the user can get the Web server machine
name.
machinename.html file
<HTML>
<BODY>
</BODY>
</HTML>
1. In Listing 4.18, .asp stands for active server page. It is designated script files. The extension .asp will cause the Web server
to invoke the IPS script interpreter. <% ... %> indicates that scripting language expressions, <% = %> indicates that the
value of the expression will put into the HTML stream.
2. In Listing 4.18, getmachinename.asp should be placed under the scripts directory, and machinename.html placed under the
wwwroot directory. When machinename.html page is launched and link "Get the Server Machine Name" is clicked, the
Web server machine name will be displayed in the browser.
Summary
An OLE automation server is a COM server with the support of the IDispatch interface. Applications can use IDispatch to access
exposed objects in automation server. A lot of tools can be used to develop the automation servers by using MFC AppWizard,
Control Wizard, and Active Template Library. The automation server is a reusable component, which can be used in the
automation controller, IE 3.0, and Web server.
Chapter 5
OLE Controls
by Vincent W. Mayfield
A Short History
What Is an OLE Custom Control?
OLE Control Architecture
OLE Control Interfaces
ActiveX Controls
Supporting the IUnknown
Be Self-Registering
Component Categories
Code Signing
Performance Considerations
Summary
Chapter 5
OLE Controls
by Vincent W. Mayfield
This chapter covers OLE Controls. OLE Controls, also called OLE Control Extensions, are commonly referred to as
OCXs. OLE Controls are known as OCXs, for their file extension, and also as ActiveX Controls, which are OLE
Controls extended for use in Internet applications. A developer can create ActiveX Controls for use in Internet
applications, and those controls can also be utilized in non-Internet applications. ActiveX Controls are a superset of OLE
Controls. Therefore, throughout this chapter, the terms OLE Control, OCX, and ActiveX Control are used somewhat
interchangeably (see Figure 5.1).
Figure 5.1. OLE Controls, OCXs, and ActiveX Controls are terms that refer to similar entities.
If a control is an OLE Control, it is not necessarily an ActiveX Control. Conversely, though, if a control is an ActiveX
Control it is an OLE Control. There are some distinct things that make a control an ActiveX Control. Not to over
trivialize things, for all intents and purposes, OLE Controls and ActiveX Controls are the same except for a few
differences. I don't want you to feel that ActiveX Controls are some entirely new thing as the marketing types would
have us believe. This chapter highlights those differences. To start out with, take a look at the origins of the OLE
Control.
A Short History
The term control or custom control has been around since Windows 3.0, when it was first defined. In fact, a custom
control was nothing more than a Dynamic Link Library that exported a defined set of functions. Unlike a .DLL a custom
control can manipulate properties and handle the firing of events in response to user or programmatic input.
The Visual Basic development environment had caught on in the development community. Custom controls were
necessary because developers found they needed better ways to express the user interface of their applications, and many
times, there was simply no way to perform a complex operation in Visual Basic. Unfortunately, or fortunately depending
on your perspective, these C DLLs had no way of allowing Visual Basic to query the control for information on the
properties and methods supported by the control. This made custom controls difficult to use in the Visual Basic
development environment.
In 1991, Microsoft unveiled the VBX. The VBX stood for Visual Basic Extension. The idea was that these little reusable
software components could be embedded in their container. To everyone's surprise, VBXs took off like wildfire.
Companies cropped up all over the place developing these little reusable software components. VBXs were able to
provide a wide range of functionality, from a simple text label to a complex multimedia or communications control.
VBXs were written in C and C++ and provided a wide variety of capabilities that could not have been done in a Visual
Basic application otherwise. VBXs became extremely popular.
Because VBXs had become popular, demand for them grew within the developer market. Soon developers wanted them
for 32-bit applications and even on non-Intel platforms such as the DEC Alpha, RISC, Power PC, and the MIPS.
Developers wanted to extend VBXs by using Visual Basic for Applications to connect VBXs with applications such as
Access, PowerPoint, Excel, Project, and Word.
Unfortunately, VBXs are severely restricted. They are built on a 16-bit architecture that is not designed as an open
interface. They were primarily designed to accommodate the Visual Basic environment. This made VBXs almost
In 1993 OLE 2.0 was released. With the release of OLE 2.0, Microsoft extended the OLE architecture to include OLE
Controls. OLE Controls, unlike their predecessors, the VBX and the custom control, are founded on a binary standard,
the Component Object Model. In addition, OLE Controls support both a 16- and 32-bit architecture.
Note
Kraig Brockschmidt wrote what is sometimes considered the bible for OLE programmers.
The book is Inside OLE, published by Microsoft Press. The original title of the book was
Inside OLE 2.0, but as you discovered in Chapter 2, "OLE Components," OLE is an
ostensibly virtual standard building on each layer. Therefore, in the second edition, the 2.0
was dropped. Inside OLE thoroughly explores the OLE standard from the API level. Every
OLE programmer should read Inside OLE.
Instead of creating an extended architecture for VBXs, Microsoft decided to develop the OCX to offer the benefits of a
component architecture to a wider variety of development environments and development tools (See Figure 5.2). The
Component Object Model and OLE are open architectures, giving them a wider variety of input from the industry. Like
their predecessor the VBX, OLE Controls are also known by their file extension; OCXs (OLE Control Extension),
likewise, have taken the market by storm.
Figure 5.2. The progression of development of the OLE and ActiveX Controls.
From 1993 to 1995, OLE Controls have flourished. Many Independent Software Vendors (ISVs) converted their VBXs
to OLE Controls, and in some cases they maintained three versions; VBX, 16-bit OCX, and a 32-bit OCX. The makers
of Visual C++ and MFC created the OLE Control Developers Kit, and even incorporated it into Visual C++ 2.0 and 1.5,
further adding to the success of OLE Custom Control.
Between 1995 and 1996, the Internet took the world like a blitzkrieg, causing Internet Mania. Everyone had to become
Web-enabled. Companies found themselves making Web sites because they saw the Internet as the great advertisement
media for the year 2000 and beyond. Unfortunately, in previous years the Internet had been a relatively static
environment. This is due in part to the Internet's roots with the big-iron diehards who grew up with the IBM Mainframes,
the VAXs, and the UNIX boxes. However, PC computers have become household devices for the common person. Users
have become accustomed to graphical interaction with their machines thanks to the Macintosh, Microsoft Windows, and
X-Windows/Motif. Thanks to SUN and their invention of the Java programming language and the Java applet, the
Internet is no longer a static environment. . Web pages exploded to life with multimedia, sound, and dynamic interaction.
Microsoft, realizing the potential and the hype surrounding the Internet Explosion, decided they needed to get with the
program and take a role of leadership in this emerging environment. Microsoft boldly announced they were going to
"Activate" the Internet in 1996 with ActiveX technologies (a little late, but better late than never). Thus, from these
ActiveX technologies, the ActiveX Controls were born. ActiveX Controls were nothing really new, just an extension of
their mother, the OLE Control. ActiveX Controls are simply OLE Controls implemented smarter and enhanced to be
utilized across the Internet.
Now that you know a little of the history behind an OLE Control, this section explores just what an OLE Control is. An
OLE Control is an embeddable Component Object Model object that is implemented as an in-process server dynamic
link library. It supports in-place activation as an inside-out object.
Note
The title of the book OLE ControlsInside Out, by Adam Denning, Microsoft Press is a
play on words because OLE Controls are activated from the inside out. This book is also an
excellent reference.
As an OLE In-Proc Object, an OLE control is loaded into the address space of its container. As you are probably aware,
every WIN32 process has a 4-gigabyte address space.
Note
The lower 2 gigabytes is where the application is loaded and the upper 2 gigabytes is where the system is loaded. An
OLE Control is loaded in the lower 2 gigabytes with the application. Therefore, they share the same resources with the
application; hence the term in-process.
An OLE Control is also a server. Why is it a server? Well, it provides two-way communication between the "container
application" and the control. It can also respond to user-initiated events such as mouse movements, keyboard input, and
programmatic scripting input, and it can pass that input to the container application for action.
OLE Controls are also in-place activated. This means that they can be placed in the active state by the user or the
container and edited or manipulated. This is a functionality OLE Controls inherit from OLE Documents. Like a dynamic
link library (DLL), the OLE Control is a library of functions. In fact, an OLE Control might be considered a "super
DLL." More than just a "super DLL," an OLE Control is a detached object that can fire and respond to events, process
messages, has unique properties, and possesses multi-threaded capabilities. OLE Controls are also known as OCXs
because of their file extension, but they are actually a DLL. OCXs can contain several controls. Unlike DLLs, OCXs
respond to user input and support two-way communication or notification, between themselves and their container.
An OLE Control can have its own data set and can act as an OLE Automation component because you can manipulate its
properties and methods. OLE Controls can be both 16- and 32-bit as well as Unicode. OLE Controls, like OLE
Automation objects, can have properties set at both compile time and runtime, and OLE Controls also have methods that
can perform certain operations. The difference between OLE Controls and OLE Automation objects is that they are self-
contained objects. They provide two-way communication between the control and the container. In addition, OLE
controls do not have to have a user interface. As such they can provide hidden services such as a timer, communications,
or mail.
OLE Controls cannot stand alone; they must be embedded in an OLE Container. OLE Controls provide prepackaged
components of functionality that are reusable and customizable. OLE Controls are at the top of the OLE Architecture.
Thus they are built on several OLE technologies. In addition, OLE Controls can be used in a wide variety of
development tools, such as Delphi, Visual C++, Borland C++, Gupta, Visual Basic, Oracle Developer 2000, and
PowerBuilder. OLE Controls can also be used in a variety of non-programming environments, such as Microsoft Word,
Microsoft Excel, Lotus, HTML, and Internet Explorer. OLE Controls are a very powerful reusable component.
The beauty of OLE Controls is that they are programmable and reusable. They expose themselves to the outside world
and can be utilized in a variety of programming and non-programming environments. An OLE Control is like an OLE
Compound Document, but it is extended by using OLE Automation through IDispatch to support properties and
methods. What makes OLE Controls unique is events. OLE Controls have three sets of attributes that are exposed to the
outside world:
Properties
Methods
Events
Properties
Properties are named attributes or characteristics of an OLE Control. These properties can be set or queried. Some
examples of properties are color, font, number, and so on.
Usually OLE Controls provide access to their properties through property sheets. Property sheets are separate OLE
Automation entities. This feature is not limited to design/compile time, but also can be displayed at runtime to allow the
user to manipulate the control's properties, events, or methods. Property sheets are a user interface component that is a
tabbed dialog. OLE Automation provides the mechanism by which controls communicate with their property sheets.
OLE Controls have what are called stock properties. These are properties common to all OLE Controls. If you are using
the Base Control Framework provided in the ActiveX SDK or the ActiveX Template Library, you will have to
implement the stock properties and their pages yourself. However, if you are using Microsoft Foundation Classes, you
can take advantage of these stock properties because they are already built in. These are general (see Figure 5.3), color
(see Figure 5.4), font (see Figure 5.5), and picture properties (see Figure 5.6).
Figure 5.6. A property sheet with the various Picture stock properties.
OLE Controls also have persistent properties. These properties are stored in the container and set at design or compile
time. Controls also have the ability to save persistent information about their properties at runtime, and thus in effect can
save their state. This means that the controls can also load their persistent properties at initial load time.
Events
Events are a notification triggered by the control in response to some external action on the control. Usually this is some
input by the user such as a mouse click or keyboard input. That event is then communicated to the control's container by
the controls. This is done through a communications mechanism known as Lightweight Remote Procedure Call (LRPC).
LRPCs are the scaled-down brother of the Remote Procedure Call (RPC).
RPCs are an interprocess communications mechanism used to provide two-way communications between applications.
This can be on the same computer or between computers across a network. RPC is the mechanism that Network OLE,
also known as Distributed COM (DCOM), uses to exchange objects across process and computer boundaries. RPC is
much more than just a communications method. It allows a function in a process on one computer to evoke a function in
a process on another computer. This can even be computers across an enterprise-wide network or the Internet.
Lightweight Remote Procedure Calls, unlike their big brother RPCs, are only for communications between processes, or
within processes on a single computer. LRPCs are the mechanism by which an OLE Control dispatches, through the
IDispatch interface, control notifications to the container, and the reverse, from the container to the control. This
communication is based on posting messages or events to window handles to transfer data between processes. It is also
known as marshaling.
Methods
Methods are functions performed by the control to access the control's functionality. This allows some external source
the ability to manipulate the appearance, behavior, or properties of the control. These are actions such as GetColor,
SetColor, CutToClipBoard, PasteFromClipboard, and so on. Methods are inherited from OLE Automation. A method is
the interface in which an application or a programmer can set or receive values from an OLE Control.
Methods are a lot like member functions in C++. They provide accessor functions that grant access to an OLE Control's
properties and data. An OLE Control's properties are like a C++ class's member variables. Methods are both stock and
custom, as are properties. Stock methods provide access to stock properties, such as color, font, and picture. Likewise,
custom methods provide access to custom properties. With methods, you can change a control's appearance or initialize
it with a value. Using Visual Basic or Visual C++, you can program a link between it and another application or control.
Like all other COM objects, OLE Controls are manipulated through interfaces. In the original OLE Control and OLE
Container specification, OLE Controls were required to support certain interfaces, whether they needed or utilized them
or not. This left some controls bloated with code and overhead that they did not need.
Presently, the only interface a control is required to implement is the IUnknown. This is mentioned so that you realize
that a new standard has been published. In December of 1995, Microsoft published the OLE Controls and OLE
Container Guidelines Version 2.0. This was an extension of Version 1.1. With the advent of ActiveX Controls, the
standard was changed to the 1996 standard for ActiveX Controls and ActiveX Containers and is again an extension to
the previous standard. The next section discusses the specifics of an ActiveX Control.
An OLE Control exposes interfaces. Likewise, a container exposes interfaces to the OLE Control. OLE Controls and
OLE Containers link through interfaces (see Figure 5.7).
Figure 5.7. OLE Controls and their Containers communicate through interfaces using LRPCs.
There are approximately 26 interfaces for OLE Controls and their Containers. The next section on ActiveX Controls
discusses the new interfaces. This is not considered an all-inclusive list, as there are a few other interfaces that are used,
but these represent the main interfaces.
In Table 5.1, notice that each object supports the IUnknown. This is now the only interface required to be supported by
an OLE Control. However, if you implemented only the IUnknown, you would have a control that did pretty much
nothing. The idea is to implement only the interfaces needed to support the control. In Figure 5.8 you can see how an
OLE Control's interfaces relate to the container interfaces. In addition, when you write the code for your control, you
must be cognizant of the interfaces the control supports, as you must also be cognizant that all OLE Containers do not
support all interfaces. In order to be compatible with as many containers as possible, you must check for the support of
your interfaces by the container and degrade your control's functionality gracefully in the event an interface is not
supported. This can be likened to error checking, except that you still want your control to function, but with degraded
capability or through an alternative interface.
Figure 5.8. How the OLE Control interfaces relate to the OLE Container interfaces.
The most important interfaces are IOleControl and IDispatch. IDispatch is the mechanism through which OLE Controls
communicate. IOleControl encapsulates the basic functionality of an OLE Control. Table 5.1 shows the COM interfaces
an OLE Control or an OLE Container may support in order to facilitate the operations between them.
Table 5.1. COM interfaces for facilitating operations between controls and containers.
OLE Control Control Site Client Site Container IClassFactory2 IOleControlSite IOleClientSite IOleInPlaceUIWindow
IOleObject IUnknown IOleInPlaceSite IOleInPlaceFrame IDataObject IAdviseSink IUnknown IViewObject IDispatch
IPersistStorage IUnknown IOleInPlaceActiveObject IOleCache IPersistStreamInit IOleControl
IConnectionPointContainer IConnectionPoint IProvideClassInfo IProperNotifySink ISpecifyPropertyPages
IPerPropertyBrowsing ISimpleFrameSite IDispatch IUnknown
The important thing to remember is that the interfaces a control supports define that control. However, you should
implement only the interfaces your control requires to function. This idea will become more apparent in the following
section on ActiveX Controls.
ActiveX Controls
An ActiveX Control is a superset of an OLE Control that has been extended for the Internet environment. This does not
mean that ActiveX Controls can be utilized only in the Internet environment; quite the contrary, they can be utilized in
any container that can support their interfaces. ActiveX Controls must still be embedded in a container application.
When an end user encounters a page with an ActiveX Control, that control is downloaded to the client machine if it is
not already there and used. This is, of course provided that the user's browser supports ActiveX controls. The two most
prevalent browsers that support ActiveX Controls are Microsoft Internet Explorer and Netscape, with the help of the
Ncompass plug-in.
The major difference between the OLE Control and the "superset" ActiveX Control is that the standard is different. In
the new standard an ActiveX Control must support at least the IUnknown interface and be self-registering. It is a simple
COM object. Obviously the control must have more interfaces than just IUnknown, or it would have no functionality.
The idea is that the control support only the interfaces it needs, so it can be as lightweight as possible. In contrast, in the
previous standard an OLE Control was required to support a whole armada of COM interfaces, whether the control
needed them or not. This made some controls bloated with code that was not utilized or needed. In the world of Internet
development this code bloat is unacceptable.
The minimum interface for an ActiveX Control to support is the IUnknown. As already discussed, the IUknown is an
interface that supports three methods; QueryInterface, AddRef, and Release.
All COM interfaces are inherited either directly or indirectly from the IUnknown, hence all other interfaces have these
three functions also. With a pointer to the IUnknown, a client can get a pointer to other interfaces the object supports
through QueryInterface. In short, an object can use QueryInterface to find out the capabilities of another object. If the
object supports the interface, it returns a pointer to the interface. Listing 5.1 demonstrates the use of the pointer to a
control's IUnknown interface to QueryInterface to find out the class information using MFC.
Listing 5.1. The use of the pointer to a control's IUnknown interface and then utilizing QueryInterface to get the
class information.
3: int MyClass::DoControlWork()
4: {
5: LPUNKNOWN lpUnknown;
6: LPPPROVIDECLASSINFO lpClassInfo;
7:
8: lpUnknown = GetControlUnknown();
9:
11: {
14: }
15: else
16: {
17: if(SUCCEEDED(lpUnknown->QueryInterface(IID_IProvideClassInfo,
19: {
22: // {
24: // as getting the class info and examining the class attributes
25: // {
26:
27: lpClassInfo->Release();
28: }
29: else
30: {
33: }
34: }
36: }
In addition, the object can manage its own lifetime through the AddRef and Release functions. If an object obtains a
pointer to an object, then AddRef is called, incrementing the object's reference count. Once an object no longer needs the
pointer to the interface, Release is called, decrementing the object's reference count. Once the reference count reaches
zero, then an object can safely destroy itself.
Although the IUnknown is a must implement, you should also take a look at the other interfaces an ActiveX Control
might want to implement. Table 5.2 shows the potential COM interfaces an ActiveX Control may want to support.
In addition, the control may want to implement its own custom interfaces. By implementing only the interfaces it needs,
the ActiveX Control can be as lean as possible. The previous OLE Control standard required that in order to be
compliant with the standard, the control had to implement certain interfaces. With ActiveX Controls, this is no longer the
case; you are only required to implement IUnknown.
Interface Purpose IOleObject Principal mechanism by which a control communicates with its container.
IOleInPlaceObject Means by which activation and deactivation of an object is managed. IOleInPlaceActiveObject
Provides communications between an in-place active object and the outermost windows of the container. IOleControl
This interface allows support for keyboard mnemonics, properties, and events. IDataObject Allows for the transfer of
data and the communication of changes in the data. IViewObject Allows the object to display itself. IViewObject2 An
extension of the IViewObject interface. It allows you to find the size of the object in a given view. IDispatch Is an
interface that can call virtually any other COM interface. It is used in OLE Automation to evoke late binding to
properties and methods of COM objects. IConnectionPointContainer This interface supports connection points for
connectable objects. IProvideClassInfo This interface encapsulates a single method by which to get all of the information
about an object's co-class entry in its type library. IProvideClassInfo2 This interface is an extension to the
IProvideClassInfo to provide quick access to an object's IID for its event set. ISpecifyPropertyPages An interface that
denotes an object as supporting property pages. IPerPropertyBrowsing This interface supports methods to get access to
the information in the property pages supported by an object. IPersistStream This interface provides methods for loading
and storing simple streams. IPersistStreamInit Designed as a replacement for IPersistStream. Adds an initialization
method InitNew. IPersistMemory Allows the method to access a fixed sized memory block for an IPersistStream object.
IPersistStorage This interface supports the manipulation of storage objects to include loading, saving, and exchanging.
IPersistMoniker An interface to expose to asynchronous objects the ability to manipulate the way they bind data to the
object. IPersistPropertyBag This interface allows the storage of persistent properties. IOleCache An interface to control
access to the cache inside an object. IOleCache2 An interface that allows the selective update of an object's cache.
IExternalConnection An interface that allows the tracking of external locking on an embedded object. IRunnableObject
An interface that enables a container to control its executable objects.
Be Self-Registering
In order for an ActiveX Control, or any other COM object, to be utilized, it must be registered in the System Registry.
The System Registry is a database of configuration information divided into a hierarchical tree. This tree consists of
three levels of information: Hives, Keys, and Values. The system registry is a centralized place where you can go to find
out information about an object (see Figure 5.9).
Note
The System Registry in Windows 95 can be viewed through a program called regedit.exe.
This program can be found in the \WINDOWS directory of Windows 95 and the
\WINNT\SYSTEM32 directory in Windows NT 4.0. If you are using Windows NT 3.51, the
System Registry can be viewed with a program called regedit32.exe which is found in the
same directory as specified for Windows NT 4.0 above.
Figure 5.9. The Windows 95 System Registry as seen through the Regedit program.
If the control is not registered in the registry, then it is unknown, and therefore unusable, by the system.
Thus, it is a requirement for ActiveX Controls to be self-registering. This means an ActiveX Control must implement
and export the functions DllRegisterServer and DllUnregisterServer. In addition, it is a requirement for ActiveX Controls
to register all of the standard registry entries for automation servers and embeddable objects. Listing 5.2 demonstrates
the use of DllRegisterServer to support self-registration of the control using MFC. This code is generated for you by
Visual C++'s Control Wizard.
1: /////////////////////////////////////////////////////////////////////////
3:
4: STDAPI DllRegisterServer(void)
5: {
6: AFX_MANAGE_STATE(_afxModuleAddrThis);
7:
8: if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
9: return ResultFromScode(SELFREG_E_TYPELIB);
10:
11: if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
13:
15: }
Listing 5.3 demonstrates the use of DllUnregisterServer to support self-unregistration of a control using MFC. This code
is generated for you by Visual C++'s Control Wizard.
1: /////////////////////////////////////////////////////////////////////////
3:
4: STDAPI DllUnregisterServer(void)
5: {
6: AFX_MANAGE_STATE(_afxModuleAddrThis);
7:
8: if (!AfxOleUnregisterTypeLib(_tlid))
9: return ResultFromScode(SELFREG_E_TYPELIB);
10:
11: if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
13:
16: }
Listings 5.2 and 5.3 show how you support registration and unregistration, and Listing 5.4 shows how you register your
control and its capabilities. Notice in Line 15 of Listing 5.4 the variable dwMyControlOleMisc. It contains the status bits
of your control. This iv very important because it contains the capabilities of your control. These capabilities can be
looked up in the System Registry to find out what capabilities your control contains without instantiating the object.
Listing 5.4. How to register your control and your controls capabilities in MFC.
1: /////////////////////////////////////////////////////////////////////////
2: // CMyCtrl::CMyCtrlFactory::UpdateRegistry -
5: {
6: if (bRegister)
7: return AfxOleRegisterControlClass(
8: AfxGetInstanceHandle(),
20: else
22: }
The possible status bits that can be set for a control are shown in Table 5.3. These bits identify the capabilities of the
control. Take a moment to become familiar with them, as they will become even more important in the discussion of
OLE Containers in Chapter 7, "Microsoft Internet Explorer 3.0 and Its Scripting Object Model."
Table 5.3.The OLE miscellaneous status bits symbolic constants and what they mean to controls and objects.
Symbolic Constant Meaning OLEMISC_RECOMPOSEONRESIZE Identifies an object that upon re-sizing by the
container will re-scale its presentation data. OLEMISC_ONLYICONIC Identifies an object that only exists in the iconic
state. OLEMISC_INSERTNOTREPLACE Identifies an object that initializes itself from the currently selected container
data. OLEMISC_STATIC Identifies that an object is static and contains no native data, only presentation data.
OLEMISC_CANTLINKINSIDE This flag identifies items such as OLE 1.0 Objects, static objects, and links. These
objects cannot be a linked source that when bound to runs another object. OLEMISC_CANLINKBYOLE1 Identifies
that an object can be linked by the containers that conform to OLE 1.0 specification. OLEMISC_ISLINKOBJECT
Identifies that an object is a linked object. This is only important for OLE1 objects. OLEMISC_INSIDEOUT Identifies
that an object can be in-place activated without the need for toolbars or menus. OLEMISC_ACTIVATEWHENVISIBLE
Identifies that an object can only be activated in the visible state. The OLEMISC_INSIDEOUT flag must also be set.
OLEMISC_RENDERINGISDEVICEINDEPENDENT This flag identifies that the object's presentation data will remain
the same regardless of the target container. OLEMISC_INVISIBLEATRUNTIME Identifies controls that are invisible at
runtime, such as Internet Explorer's Timer Control or Internet Explorer's PreLoader Control. OLEMISC_ALWAYSRUN
Tells a control that a control should be set in the running state even when not visible. OLEMISC_ACTSLIKEBUTTON
Identifies controls that can act like buttons. OLEMISC_ACTSLIKELABEL Identifies controls that can change the label
provided by the container. OLEMISC_NOUIACTIVATE Identifies whether a control supports User Interface activation.
OLEMISC_ALIGNABLE This bit identifies that a control may be aligned with other controls for containers that support
control alignment. OLEMISC_SIMPLEFRAME Identifies that the control supports the ISimpleFrameSite interface.
OLEMISC_SETCLIENTSITEFIRST In the new OLE Container specification, this flag identifies controls that support
the SetClientSide function being called after the control is created but before it is displayed. OLEMISC_IMEMODE In
the Double Byte Character Set versions of Windows, identifies that the control supports the Input Method Editor Mode,
for internationalized controls.
These miscellaneous status bits are especially important when used in conjunction with Component Categories as an
accurate picture of what your control can or cannot do. This picture of what the control can do, can be obtained from the
System Registry.
Component Categories
Previously, in order to be registered on the system, an OLE Control was registered through entries in the registry with
the Control keyword. To your benefit, controls can be utilized for multiple purposes. Therefore, a way was needed to
identify a control's functionality as opposed to just listing the interfaces it supports. This is where Component Categories
come in.
Component Categories are a way of describing what a control does. They provide a better method for containers to find
out what a control does without creating it and having to query for its methods using an IUnknown pointer and
QueryInterface. Creating a control object involves a lot of overhead. A container would not want to create a control if the
container itself does not support the functionality the control requires.
Component Categories are not specific to ActiveX but are an extension of the OLE Architecture. Each Component
Category has its own GUID (Globally Unique Identifier) and a human readable name stored in a well-known place in the
System Registry. When a control registers itself, it does so using its component category id. In addition, it also registers
the component categories it supports and the component categories it requires its container to support.
For backward compatibility, the control should also register itself with the Control keyword for containers that do not
support the new component categories. The control should also register the key ToolBoxBitmap32. This key identifies
the module name and resource id for a 16*15 bitmap. ToolBoxBitmap32 provides a bitmap to use for the face of a
toolbar or toolbox button in the container application. If a control can be inserted in a compound document, it should
also register the Insertable key.
Component Categories can be mixed and matched depending on their type. Microsoft maintains a list of Component
Categories. Any categories that are new should be submitted to Microsoft for inclusion in the list. This promotes
interoperability. The following component categories have been identified:
SimpleFrameSite Containment
A Simple Frame Site Container Control is a control that contains other controls, for example a 3D group box that
contains a group of check boxes. The GUID for this component category is: CATID - {157083E0-2368-11cf-87B9-
00AA006C8166} CATID_SimpleFrameControl. In order to support a Simple Frame Site Container, the OLE Container
application must implement the ISimpleFrameSite interface and the control must have its status bit set to
OLEMISC_SIMPLEFRAME.
A control or container that supports simple data binding supports the IPropertyNotifySink interface. Data binding is how
controls affiliate their persistent properties and how containers exchange property changes from their User Interface to
the control's persistent properties. This allows the persistent storage of their properties and at runtime binds the data to
the control synchronizing property changes between the control and the container. The GUID for this component
category is: CATID - {157083E1-2368-11cf-87B9-00AA006C8166} CATID_PropertyNotifyControl.
Note
Although a control that supports simple data binding is meant to provide binding to a
datasource, such binding should not be required for the functionality of the control. Even
though a lot of the functionality the control has is lost, that control should degrade gracefully
and still be able to function, although potentially limited, independent of any data binding.
Advanced Data Binding is similar to Simple Data Binding except it supports more advanced binding techniques, such as
asynchronous binding and Visual Basic Data Binding. The GUID for this component category is: CATID - {157083E2-
2368-11cf-87B9-00AA006C8166} CATID_VBDataBound.
These component categories are for components that specifically support the Visual Basic environment. Controls or
containers may want to support alternative methods in case a container encounters a control, or a control encounters a
container that does not support the Visual Basic Private Interfaces. The GUID for this component category is: CATID -
{02496840-3AC4-11cf-87B9-00AA006C8166} CATID_VBFormat, if the container implements the IBVFormat
interface for data formatting to specifically integrate with Visual Basic or CATID - {02496841-3AC4-11cf-87B9-
00AA006C8166} CATID_VBGetControl if the container implements IVBGetControl so that controls can enumerate
other controls on a Visual Basic Form.
Internet-Aware Controls
Internet-aware controls implement one or more persistent interfaces to support operation across the Internet. All these
categories provide persistent storage operations. The following are GUIDs for components that fall into this category:
The RequiresDataPathHost category means that the object requires the container to support the IBindHost interface
because the object requires the capability of saving data to one or more paths.
All of the rest of the categories listed above are mutually exclusive. They are used when an object only supports a single
persistence method. If a container does not support a persistence method that a control supports, the container should not
allow themselves to create controls of that type.
Windowless Controls
Windowless Controls are controls that do not implement their own window and rely on the use of their container's
window to draw themselves on. These types of controls are non-rectangular controls such as arrow buttons, gauges, and
other items modeled after real-world objects. In addition, this includes transparent controls. The GUID for this
component category is: CATID - {1D06B600-3AE3-11cf-87B9-00AA006C8166} CATID_WindowlessObject.
Components that do not support a category should degrade gracefully. In the case where a control or container is unable
to support an interface, the control should either clearly document that a particular interface is required for the proper
operation of the component or at runtime notify the user of the component's degraded capability.
By using self-registration, components can be self-contained, which is necessary for Internet operations. By using
DllRegisterServer and DllUnregisterServer and the Component Categories API functions to register itself and the
component categories it supports, a control can further its interoperability in a variety of environments.
Code Signing
In the Internet environment, users must download the components to their local machine and utilize them. This is an
extreme hazard to the local machine by allowing the implementation of this foreign code on their machine.
This is where a new security measure called code signing comes in. Browsers typically warn the user that they are
downloading a potentially unsafe object; however, it does not physically check the code for authenticity to ensure it has
not been tampered with nor does it verify its source.
Microsoft has implemented Authenticode, which embodies the Crypto API. This allows developers to digitally sign their
code so that it can be checked and verified at runtime. This function is built into the browser and displays a certificate of
authenticity (see Figure 5.10) if the control is verified.
Figure 5.10. The certificate the user is shown at runtime after the code has been authenticated.
Presently, the code signing specification and the certification process are being reviewed by the World Wide Web
Consortium (W3) and the current specifications are subject to change. Internet Explorer and all Microsoft controls
naturally support code signing and Authenticode, but as of yet Netscape does not. Netscape has gone to W3 with a
proposal to extend its own "digital certificate" standard. In the spirit of cooperation, Netscape eventually will support the
code signing specification, or at a minimum Microsoft will embrace both standards.
Code signing works with DLLs, EXEs, CABs, and OCXs. When a developer creates these items, they attain a digital
certificate from an independent Certification Authority. They then run a one-way hash on the code and produce a digest
that has a fixed length. Next, the developer encrypts the digest using a private key. This combination of an encrypted
digest coupled with the developer's certificate and credentials is a signature block unique for the item and the developer.
Here's the way code signing works on the client machine. When a user downloads a control, for example, from the
Internet, the Browser application such as Internet Explorer or Netscape calls a WIN32 API function called
WinVerifyTrust.
Note
WinVerifyTrust then reads the signature block. With the signature block, the WinVerifyTrust can authenticate the
certificate and decrypts the digest using the developer's public key. Using the public key, the function then rehashes the
code with the hash function stored in the signature block and creates a second digest. This digest is then compared with
the original. If they do not match, this indicates tampering and the user is warned (see Figure 5.11). On the contrary if
the digest had matched, instead of the warning in Figure 5.11, the user would have gotten the certificate of authenticity
as seen in Figure 5.10.
Figure 5.11. The warning the user is shown at runtime to tell the user of a potential danger because the code
cannot be authenticated.
Despite Code Signing, the user is in control and may choose to heed or ignore the warning. If the hashes check out, then
a certificate is displayed by the browser.
The Code Signing mechanism provides some security for end users and developers alike. It is a deterrent to malicious
tampering with executable code for the intent of information warfare such as viruses, and it is also a deterrent for those
who might pirate the code developed by others. Please be aware again that this is a proposed standard and has not yet
been officially accepted, although there is nothing I can see at this time that can compete with it. It is safe to say that no
matter what Microsoft will continue to support it and in addition, continue to refine it. The bottom line is you will need
to continue to monitor the standard.
Performance Considerations
ActiveX Controls are designed to work across the Internet. As such they are Internet-aware. Unfortunately, the Internet is
low bandwidth and highly subjected to server latency. This means that ActiveX Controls must be lean and mean, or to
put it more plainly, highly optimized. Because ActiveX Controls implement only the interfaces they need, they are
already partially optimized. ActiveX Controls are optimized to perform specific tasks. However, there are several things
you can do to help optimize your controls.
Tip
These performance considerations and optimizing techniques apply to OLE Controls as well
as ActiveX Controls. You may have already developed OLE Controls to the old standard,
but you can still apply most of these principles to those controls.
When you draw items, you have to select items such as pens, brushes, and fonts into the device context to render an
object on the screen. Selecting these into the device context requires time and is a waste of resources when the container
has multiple controls that are selecting and deselecting the same resources every time they paint. The container may
support optimized drawing. This means that the container handles the restoration of the original objects after all the items
have been drawn. IViewObject::Draw supports optimized drawing by using the DVASPECTINFOFLAG flags set in
DVASPECTINFO structure. You must use this to determine if your container supports optimized drawing when
implementing API functions. MFC encapsulates this check for you in the COleControl::IsOptimizedDraw function. You
can then optimize how you draw your code by storing your GDI objects as member variables instead of local variables.
This prevents them from being destroyed when the drawing function finishes. Then if the container supports optimized
drawing, you do not need to select the objects back because the container has taken care of this for you.
If your control has a window, it may not need to be activated when visible. Creating a window is a control's single
biggest operation and therefore should not be done until it is absolutely necessary. Therefore, if there is no reason for
your control to be activated when visible, you will need to turn off the OLEMISC_ACTIVATEWHENVISIBLE
miscellaneous status bit.
When your control has a window, it must sometimes transition from the active to the inactive state. There is a visual
flicker that occurs when the control redraws from the active to the inactive state. Flicker can be eliminated by two
methods; drawing off-screen and copying to the screen in one big chunk, and drawing front to back. IViewObjectEx API
function provides the necessary functions to use either method or a combination of both. With MFC the implementation
is much simpler. (See Listing 5.5.)
Listing 5.5. Shows how you set the windowless flag in MFC.
1: DWORD CMyControl::GetControlFlags()
2: {
4: }
Optimizing persistence and initialization means basically one thing: Keep your code as lean as possible. Because of the
cheapness of hard drive space and memory, some programmers have gotten lazy in the creation of this code and allowed
it to become bloated and slow. With Internet applications, this is a death sentence. Most people access the Internet with
14.4 modems. A megabyte of data takes almost 9 minutes on a 14.4 modem. Users will get impatient if they have to wait
long periods of time. What can you do? You can do several things.
First of all, make sure you do not leave any non-utilized blocks of code or variables. You should also take out any
debugging or testing blocks out of your code. For example, you have written your code so a message box displays when
you reach a certain segment of code. Take it out! It will only add to your code size. However, if you delimit your
debugging blocks of code using the preprocessor #ifdef _DEBUG and #endif you will not have to worry about the code
being included in the release builds, as the debugging blocks of code will be left out of the compile.
Second, today's compilers have optimizing options on them. In the past these optimizing compilers were not very
efficient and sometimes introduced bugs in an application that had already been tested. But, compilers have gotten much
better. Use them! Let the compiler do some of the work for you. You may have to tweak and play with the optimizations
to find the best combination of options.
Warning
Make sure you perform your compiler optimizations before you send your code to testing. However, any time you touch
the code, it should go back through testing. Therefore, if you should have to tweak the compiler optimizations after it has
been through testing. Make sure you send it back through testing! This may help prevent discovering a bug after release.
You should also turn off the incremental linking option on your compiler when you do a release build. Incremental
linking can add serious bloat to your code.
Note
For an excellent article on keeping your code small, see "Removing Fatty Deposits from
Your Applications Using This 32-bit Liposuction Tools" by Matt Pietrek in Microsoft
Systems Journal, October 1996, Vol 11, No 10. Matt Pietrek has many useful suggestions
and even provides a nice tool to assist you.
The last thing you should take into account is utilizing asynchronous operations to perform initialization and persistence
operations. Asynchronous downloading gives the user the illusion that things are occurring faster than they are. In
addition, you may want to give the user other visual cues that progress is being made, such as a progress indicator or a
message box. However, you will have to weigh the performance issues associated with their addition.
You should consider making your control a Windowless Control if appropriate. Creating a window is a control's single
biggest operation, taking almost two-thirds of its creation time. This is a lot of unnecessary overhead for the control.
Most of the time, a control does not need a window and can utilize its container's window and allow the container to take
on the overhead of maintaining that window. This will allow you to model your controls after real-world objects, such as
gauges, knobs, and other non-rectangular items.
By using the API function IOleInPlaceSiteEx::OnInPlaceActivateEx and setting the ACTIVATE_WINDOWLESS flag,
you can have your control be in the windowless mode. With MFC, you can do the following:
DWORD CMyControl::GetControlFlags()
In addition, there is a whole series of API functions that allow you to manipulate windowless controls. MFC has
encapsulated many of these functions for you also. The books online in Visual C++ has a complete reference for these
functions. In addition, the Win 32 API references have the API level functions.
If you have a window and you are sure your control does not draw outside of that window, you can disable the clipping
in your drawing of the control. This can yield a small performance gain by not clipping the device context. With MFC,
you can do the following to remove the clipPaintDC flag:
DWORD CMyControl::GetControlFlags()
Note
The clipPaintDC flag has no effect if you have set your control as a windowless control.
With the API functions in the ActiveX SDK, you can implement the IViewObject, IViewObject2, and IViewObjectEx
interfaces to optimize your drawing code so you do not clip the device context.
You may set your control to inactive because it does not always need to be activated when visible. You may still want
your control to process mouse messages such as WM_MOUSEMOVE and WM_SETCURSOR. You will need to
implement the IPointerInactive interface to allow you to process the mouse messages. If you are using MFC, you need
only implement the following function as the framework handles the rest for you.
DWORD CMyControl::GetControlFlags()
However, you will need to override the OLEMISC_ACTIVATEWHENVISIBLE miscellaneous status bit with
OLEMISC_IGNOREACTIVATEWHENVISIBLE. This is because the OLEMISC_ACTIVATEWHENVISIBLE forces
the control to always be activated when visible. You have to do this to prevent the flag from taking effect for containers
that do not support the IPointerInactive interface.
In today's software development environment, software engineers are not only designers and programmers, but
increasingly, software engineers are taking on the role of component integrators. End users demand that their software be
developed quickly, be rich in features, and integrate with the rest of the software they use. With the advent of OLE,
CORBA, and OpenDoc, you now have hundreds of thousands of reusable components and objects to choose from. There
is an abundance of dynamic link libraries, controls, automation components, and document objects at your fingertips.
OLE Controls especially provide an off-the-shelf self-contained reusable package of functionality, created by someone
else. OLE Controls provide functionality of all types such as multimedia, communications, user interface components,
report writing, and computational (see Figure 5.12).
This is functionality that you do not have to create. The major key to component integration is to be able to integrate all
of the components with a custom application so that they work in single harmonious union as if they were native to the
application.
However, before you embark on creating this application, you should take care not to "reinvent the wheel."
OLE/ActiveX Controls, the Component Object Model, and the object-oriented paradigm present a unique opportunity
for you to truly have code reuse. In order to achieve this nirvana of code reuse, you should evaluate what components are
already out there. Likewise, before you decide to write your own OLE Controls, you should take a look at what is
already out there and see if you can utilize what is already available as opposed to reinventing the wheel.
When you choose to utilize off-the-shelf components, there are a few things you should consider. You should ask the
following questions: How long has the manufacturer been in business? does the company supply the source code with
the component? (The source code would come in handy if the manufacturer went out of business or had a bug in its
component that it was not going to fix.) what are the licensing fees and distribution costs? is the company web enabled?
what kind of support and money-back guarantee does the manufacturer provide? what tools will the component be
supported in?
These questions can save you a lot of heartache later. Integration of these off-the shelf-components is sometimes tricky.
Make sure you thoroughly research the components you choose.
So that you do not go out and reinvent the wheel, it is important to note that there are several controls that come stock
with Internet Explorer. You can utilize these controls in your Web pages and in your application development efforts.
These controls provide a variety of functionality. The following ActiveX Controls come with Internet Explorer:
Note
A demo of the functionality of each of these controls is available on the Microsoft World
Wide Web site at the following Internet URL:
http://www.microsoft.com/activex/gallery/default.htm. In addition, a number of other third-
party vendors have their controls demonstrated at the same Microsoft WWW Site.
Animated Button
The Animated Button ActiveX Control displays frame sequences of an AVI file using the Microsoft Windows
Animation common control, based on the state of the button.
Chart
The Chart ActiveX Control allows you to display a variety of charts such as a bar chart, pie charts, and graphs.
Gradient
The Gradient ActiveX Control allows you to display a gradient of one palettized color to another, gradually fading the
pixels from one color to another.
Label
The Label ActiveX Control allows you to display text at various angles, sizes, and colors. It will even allow you to
display text around a user-defined curve.
Marquee
The Marquee ActiveX Control allows you to have scrolling, bouncing, or sliding text and URLs within a window, much
like the old cinema marquees.
Menu
The Menu ActiveX Control allows you to embed menu button or pull-down menu functionality in your Web page.
Popup Menu
The Popup Menu ActiveX Control allows you to embed a popup menu in your Web page. This control sends a Click
event when the user selects a menu item.
Popup Window
The Popup window control enables you to display a specified HTML document in a popup window. In addition, this
control can be used to provide tooltips or preview links.
Preloader
The Preloader ActiveX Control holds the position of a URL and stores it in cache. Once it is activated, it downloads
asynchronously in the background the item pointed to by the URL. This control is not visible at runtime.
Stock Ticker
The Stock Ticker ActiveX Control acts just like a stock ticker and displays data across the screen at a set speed. It
utilizes text files or XRT files that are downloaded asynchronously at specified intervals.
Timer
The Timer ActiveX Control fires an event periodically at a set time interval. The timer control is invisible at runtime.
View Tracker
The View Tracker ActiveX Control fires OnShow and OnHide events based on whether the control is in or out of the
viewable area of the screen.
As you can see, there are several controls provided in Internet Explorer that have a lot of useful capability built into
them. Be aware of what is already out there, and it may save you development time when every minute counts.
In order to "Activate the Internet" with ActiveX Controls, as the Microsoft Marketing folks are fond of saying, you have
to have a way of embedding those ActiveX Controls in an HTML file.
The World Wide Web Consortium (W3C) controls the HTML standard. The current HTML standard is version 3.2. Like
most standards, it is continually updated and modified as technology progresses. As the standard progresses, the
controlling agency tries to ensure backward compatibility. This is so that any HTML browser that does not yet support
the newest standard will degrade gracefully and allow the HTML to be viewed.
Note
The current World Wide Web Consortium (W3C) HTML standard is available at the
following Internet URL: http://www.w3.org/pub/WWW/.
The <OBJECT> HTML tag is used to allow the insertion of dynamic content in the Web page such as ActiveX Controls.
The tag is just a way of identifying such dynamic elements. It is up to the browser to parse the HTML tags and perform
the appropriate action based on the meaning of the tag. In Listing 5.6, you can see the HTML syntax for the <OBJECT>
tag. This syntax comes directly from the World Wide Web Consortium (W3C) controls HTML standard Version 3.2.
1: <OBJECT
3: BORDER= number
6: CODETYPE= codetype
8: DECLARE
9: HEIGHT= number
12: SHAPES
18: </OBJECT>
By utilizing the <OBJECT> tag, you can insert an object such as an image, document, applet or control, into the HTML
document.
Table 5.4 shows the acceptable range of values to be utilized by the parameters of the <OBJECT> tag.
Table 5.4. The values for the parameters of the <OBJECT> tag.
Parameter Values ALIGN= alignment type Sets the alignment for the object. The alignment type is one of the following
values: BASELINE, LEFT, MIDDLE, CENTER, RIGHT, TEXTMIDDLE, TEXTTOP, and TEXTBOTTOM.
BORDER= number Specifies the width of the border if the object is defined to be a hyperlink. CLASSID= universal
resource locator Identifies the object implementation. The syntax of the universal resource locator depends on the object
type. For example, for registered ActiveX controls, the syntax is: CLSID:class-identifier. CODEBASE= universal
resource locator Identifies the codebase for the object. The syntax of the universal resource locator depends on the
object. CODETYPE= codetype Specifies the Internet media type for code. DATA= universal resource locator Identifies
data for the object. The syntax of the universal resource locator depends on the object. DECLARE Declares the object
without instantiating it. Use this when creating cross-references to the object later in the document or when using the
object as a parameter in another object. HEIGHT= number Specifies the height for the object. HSPACE= number
Specifies the horizontal gutter. This is the extra, empty space between the object and any text or images to the left or
right of the object. NAME= universal resource locator Sets the name of the object when submitted as part of a form.
SHAPES Specifies that the object has shaped hyperlinks. STANDBY= message Sets a message to be displayed while an
object is loaded. TYPE= type Specifies the Internet media type for data. USEMAP= universal resource locator Specifies
the image map to use with the object. VSPACE= number Specifies a vertical gutter. This is the extra white space
between the object and any text or images above or below the object. WIDTH= number Specifies the width for the
object.
In Listing 5.7, you can see HTML document source code with an embedded ActiveX object in it. In addition, notice the
<PARAM NAME= value> tag. This tag was utilized to set any properties your ActiveX Control may have.
Listing 5.7. The HTML Page with an embedded <OBJECT> tag showing an ActiveX ActiveMovie Control
embedded in the page.
1: <HTML>
2: <HEAD>
4: </HEAD>
5: <BODY>
6:
</u></strong></em></font></p>
8: <OBJECT
9: ID="ActiveMovie1"
10: WIDTH=347
11: HEIGHT=324
12: ALIGN=center
13: CLASSID="CLSID:05589FA1-C356-11CE-BF01-00AA0055595A"
14: CODEBASE="http://www.microsoft.com/ie/download/activex/amovie.ocx#
Version=4,70,0,1086"
22: </OBJECT>
23:
24: </BODY>
25: </HTML>
When a browser such as Internet Explorer encounters this page, it begins to parse the HTML source code. When it finds
the <OBJECT> in line 8, it realizes it has encountered a dynamic object. The browser then takes lines 10-12, the
WIDTH, HEIGHT, and ALIGN attributes, which are in this case are 347, 324, and CENTER respectively, and sets up a
placeholder for the object on the rendered page. It then takes the ID "ActiveMovie1" in line 9 and the CLASSID
"CLSID:05589FA1-C356-11CE-BF01-00AA0055595A" in line 13 and checks to see whether this control has been
registered before in the registry. If the control object has never been registered, it then uses the CODEBASE attribute to
locate the OCX on the server machine and proceeds to download the object into the \Windows\Ocache directory. The
browser then registers the AMOVIE.OCX by calling the function DllRegisterServer to register the control on the local
machine. Now with the control properly registered, the browser can get the CLSID for the object from the registry. In
order to utilize the control, it passes the CSLID to CoCreateInstance to create the object, and this returns the pointer to
the control's IUnknown. It can utilize this pointer and the property information in lines 15-22 to actually render the
object on the page. Figure 5.13 shows the HTML document displayed in Internet Explorer.
Figure 5.13. The HTML document as it appears in Internet Explorer with the ActiveX ActiveMovie Control
embedded in it.
Now you can see that embedding controls to enhance a Web page with dynamic content is fairly easy. It is important that
you as an ActiveX Control designer understand how they are rendered. A more in-depth look at creating Web pages and
using controls and applets in them will be presented in Chapter 12, "Advanced Web Page Creation."
The ActiveX Control Pad is discussed in full in Chapter 11, "Using ActiveX Control Pad." It provides a method of
generating the HTML code that was discussed earlier, to embed ActiveX and other dynamic objects into HTML source
(see Figure 5.14). This is a free tool provided by Microsoft to aid in the production of Internet-enabled applications.
Note
The ActiveX Control Pad can be downloaded from Microsoft at the following Internet URL:
http://www.microsoft.com/workshop/author/cpad.
This tool can be used to quickly embed your control in a page so you can test its functionality. The ActiveX Control Pad
can be a great timesaver, freeing you from having to remember how to write HTML source code. It will even allow you
to test your ability to utilize VBScript (see Figure 5.15) to do OLE Automation with your code.
Figure 5.14. The ActiveX Control Pad with the Active Movie Control properties being edited.
Figure 5.15. The ActiveX Script Wizard to help you create scripts to further "Activate" your controls.
In addition, the ActiveX Control Pad comes with a suite of ActiveX Controls for you to utilize in the development of
your Web pages and your OLE-enabled applications. Some of these controls are the same controls that come with
Internet Explorer; however, there are a few new ones to add to your bag of OLE Controls.
One last place to look for OLE Controls is in your development tools. Visual C++, Borland C++, Visual Basic, Delphi,
PowerBuilder, Access, and almost any other mainstream Windows or Internet development suite come with OLE
Controls nowadays. Become familiar with the development tools you use and take advantage of the components
provided for you. This will make your job much easier and make your users much happier.
This section examines the ways to write ActiveX Controls. Each method is discussed, and you will create an example
OLE control. Presently, there are three ways of creating ActiveX Controls:
Every major PC C++ compiler manufacturer now supports MFC, so it should be possible to create an ActiveX Control
with another vendor's product; however, for the purposes of this chapter, you will do this example utilizing Visual C++
4.2a or greater. In addition, you should be able to use any C++/C compiler to use the ActiveX Development Kit and the
ActiveX Template Library.
The traditionally used programming language for creating OLE Controls is C++ and C; however, Microsoft has
promised a compiled version of Visual Basic, called Visual Basic 5.0, that will be able to create OLE Controls. It has
been rumored that Microsoft is also creating a converter that will convert ActiveX Controls into Java applets. Those of
you who are Borland Delphi PASCAL programmers can now create ActiveX Controls with a third-party add on from
Apiary Inc. called OCX Expert. This Delphi add-on takes VCLs created in Delphi and converts them to 32-bit ActiveX
Controls.
Note
Information on OCX Expert can be obtained from Apiary Incorporated at the following
Internet URL: http://www.apiary.com/.
Visual Basic is a very popular language/development tool because it is very easy to learn and utilize. It has even been
called a quasi-4GL. Many times it is chosen for development endeavors for these reasons. However, C++ provides much
more power and flexibility in the development of applications as a whole, but most importantly, in the development of
user interface elements like OLE Controls.
Developers are often almost fanatically religious about their development tools. As a software engineer, you should be
concerned with the right tool to fit the job. I highly recommends Visual C++ and Microsoft Foundation Classes.
Programming in C++ is now much easier with class libraries such as MFC and Integrated Development Environments
such as Visual C++. Some even consider it a 4GL, but theoreticians might argue that point. Nevertheless, it is a very
powerful tool, one you should consider using. There are also a number of powerful C++ development tools such as
Borland C++, Symantec C++, and IBM Visual Age. There is a virtual plethora of tools for you to choose from. In
addition, Borland Delphi is a powerful tool with which you can develop Windows applications using Object-Oriented
PASCAL, but its principal strength is its integrated development environment. If you are unfamiliar with some of these
tools, you might want to get an evaluation copy of them and try them. This will better aid you in picking the right tool
for the job.
This book covers OCXs created with C++. The authors want to make you aware that there are many new emerging
technologies and product associated with ActiveX Controls.
The next sections examine three ways of creating ActiveX Controls. These sections assume that you have a working
knowledge of C++ and a Windows class library such as Microsoft Foundation Classes (MFC) or Borland's Object
Windows Library (OWL). They make no attempt to teach you C++ or MFC. Although these sections are for intermediate
to advanced programmers, if you are new or curious to or about C++, MFC, or Visual C++, some information is
highlighted to aid you.
Take a look now at an example of an OLE Control created with Visual C++ and MFC.
Visual C++ and MFC comes in three flavors, 16-bit, Win32s/32-bit, and 32-bit. Visual C++ 1.52c and MFC 2.53 for 16-
bit developers and Visual C++ 4.2 and MFC 4.2 for 32-bit developers. For those of you who still desire the Win32s
development platform for the development of 32-bit applications to run under 16-bit Windows, there are Visual C++ 4.1
and MFC 4.1. The newer versions of Visual C++ will no longer support Win32s. This section concentrates on the 32-bit
environment and does not cover Win32s or 16-bit development. Building 16-bit OLE Controls is possible with Visual
C++ 1.52c, but 16-bit development is rapidly being left behind. In addition, the statement about 16-bit development
being left behind can be said of the Win32s world as well.
First, you need to get the ActiveX Software Development Kit. An updated version of the ActiveX SDK was released at
the same time Internet Explorer 3.0 was released. The new ActiveX SDK was updated to include the new technology and
The most current ActiveX SDK is available from Microsoft, at no charge, at the following
Internet URL: http://www.microsoft.com/intdev/sdk/.
Caution
The ActiveX SDK is only intended to run on Windows 95 and Windows NT 4.0 (release)
machines running the release version of Internet Explorer 3.0.
The ActiveX SDK file obtained from the Microsoft WWW site is a self-extracting archive. In addition, if you subscribe
to Level II or higher of the Microsoft Developers Network Library, the most recent ActiveX SDK should be included in
future releases of MSDN.
Tip
If you do not already subscribe to the Microsoft Developers Network Library (MSDN), the
authors highly recommend you do. It is an invaluable source of technical information for
developers of software and hardware for the Windows Family of Operating Systems. You
can obtain information on MSDN by calling Microsoft at 1-800-759-5474 or at the
Microsoft WWW site at the following Internet URL: http://www.microsoft.com/msdn/.
The Microsoft Developers Network is a subscription for four levels of information and products. It contains, depending
on what level you subscribe to: all the Software Development Kits, all the Knowledge Bases, documentation for all of
Microsoft's developer products, how-to articles, samples, bug lists and workarounds, all the operating systems,
specifications, Device Driver Kits, and the latest breaking developer news. It is issued in CD format (see Figure 5.16)
and is released and updated quarterly. The Level II subscription alone comes with over 35 CDs, packed full of
development information that is updated quarterly. The MSDN Library CD directly integrates with the Visual C++ IDE.
Visual C++ comes as a yearly subscription, or you can purchase the single release professional version. The professional
version is version 4.0. With the subscription, you get the updates throughout the year. Right now that is version 4.2. You
can only get 4.2 through the subscription. You can still create OLE Controls with version 4.0, but to get the
enhancements to create ActiveX Controls you must have version 4.2. In addition, Microsoft recently released Visual
C++ Enterprise Edition 4.2. The Enterprise edition of Visual C++ includes additional database tools such as an SQL
debugger and visual database views. The Enterprise Edition 4.2 or greater can be utilized to develop ActiveX Controls.
As previously mentioned, a new version of the ActiveX SDK was just released in September 1996. The Visual C++
development team at Microsoft has also released a Patch for Visual C++ 4.2 and MFC 4.2 to allow developers to utilize
the new features to create ActiveX applications. This patch will be included in the next subscription release of Visual
C++ and MFC 4.3. The patch is called Visual C++ Patch 4.2b. You will need to download this patch and incorporate it
with Visual C++ 4.2 in order to create ActiveX Controls.
Warning
The Visual C++ 4.2b Release is only for use on Visual C++ and MFC versions 4.2. Do not apply this patch to any other
version of Visual C++, or your software and operating system may not operate properly.
Note
The Visual C++ 4.2b Release patch is available from Microsoft, at the following Internet
URL: http://www.microsoft.com/visualc/v42/v42tech/v42b/vc42b.htm.
The patch is a self-extracting archive. Once you get the patch and extract it, make sure you follow the directions in the
readme file. (If you are reading this and Visual C++ and MFC 4.3 have been released and you have them loaded, then
you need not apply the patch.)
Using Visual C++ and MFC for ActiveX and OLE Controls
Previously OLE Controls had to have certain interfaces implemented whether they needed them or not. This means that
controls were larger than they needed to be. This is fine if you are utilizing them on a local machine, but with ActiveX
Controls that need to be downloaded and installed across the low bandwidth, high latency Internet, any excess baggage is
less efficient in achieving this end. In order to get Web Masters to utilize your controls to activate their Web pages, your
ActiveX Controls need to be lean, mean, efficient downloading machines.
Visual C++ comes with a Control Wizard to help you create controls. It is one of the fastest ways to create a control. In
fact, if you are a newcomer to creating controls, it is the best way to learn. Why? Because it creates a framework for you.
You can be up and running very quickly. However, there are a few drawbacks you need to be aware of.
In order to utilize a control created with Visual C++ and based on MFC, the MFC dynamic link library (DLL) must
reside on the client machine. This file is about 1.2M and must be downloaded to the client machine. However, this must
only occur the first time, if the MFC DLL does not reside on the client machine already. So you take a small
performance hit the first time your control is used. Furthermore, it should also be noted that MFC-based controls tend to
be fatter than the controls created by the other two methods.
You will need to weigh the options carefully, considering performance, programmer skill, timetable, and environment.
This is not to say that MFC-based controls are not suitable for use in the ActiveX environment, but simply to make you
aware of the factors associated with choosing this method. If you are building controls for an Intranet, which is high
bandwidth and potentially low latency, the size of the control and the associated DLL are not a major factor. Speed of
development, less complexity, and rich features may be more important. In fact, a basic OCX created with the OLE
Control Wizard is only 23K. 23K, even on the sluggish Internet, is not extremely large, especially in comparison to some
of the large graphic files and AVI files embedded in Web pages. The name of the game is optimization and
asynchronous downloading. These topics will be discussed in Chapter 8.
Help is on the way. The Visual C++/MFC team at Microsoft realize that performance is very important in the Internet
environment. They are feverishly working to make ActiveX Controls created with Visual C++ and MFC leaner and
meaner, as well as working the download of the MFC DLL issue. Visual C++ and MFC may be the best way to create
controls, but you will have to weigh each situation accordingly.
MFC encapsulates the OLE Control functionality in a Class called COleControl (see Figure 5.17). COleControl is
derived from CWnd and in turn from CCmdTarget and CObject.
COleControl is the base class from which you derive to create any OLE Control you want. What's nice is that your
control inherits all the functionality of the base class COleControl (see Table 5.5). You can then customize the control to
the capabilities you want to include in it. As you know, an OLE Control is nothing more than a COM Object. With MFC,
the complexities of dealing with the COM interfaces are abstracted into an easy-to-use class. In addition, MFC provides
a framework for your control so you can worry about the details of what you want your control to do instead of
recreating functionality that all controls have to contain in order to work.
Table 5.5. The member functions of COleControl that are encapsulated by the Microsoft Foundation Classes.
You are probably wondering why all of these member functions are listed here for you. The purpose is to emphasize the
amount of work already done for you by the Microsoft Foundation Classes. In the COleControl class, there are 128
member functions, which when you derive your control from COleControl, your control inherits the capability of using
those pre-defined functions.
In addition, MFC itself provides a whole range of capability already created for you when you utilize it. It also includes a
functionality to do messaging and automated data exchange.
With MFC Version 4.2, Microsoft added some new classes to MFC to facilitate the creation of ActiveX Controls. These
new classes add to MFC's impressive range of functionality. These five new classes are listed in Table 5.6.
Table 5.6. The new classes added to Microsoft Foundation Classes version 4.2 to support ActiveX Controls.
Function Definition CMonikerFile When this class is instantiated as an object, it encapsulates a stream of data named by
an IMoniker interface object. It allows you to have access and manipulate that data stream pointed to by an IMoniker
object. CAsyncMonikerFile Works much the same as a CMonikerFile except it allows asynchronous access to the
IStream object pointed to by the IMoniker object. CDataPathProperty This class encapsulates the implementation of
OLE Control Properties so they can be implemented asynchronously. COleCmdUI This class encapsulates the process
by which MFC updates the User Interface. COleSafeArray This class encapsulates the function of an array of arbitrary
type and size.
In addition to the new classes in MFC, Microsoft also enhanced COleControl to simplify the creation of ActiveX
Controls. These functions add to the already impressive armada of capabilities encapsulated in COleControl. Table 5.7
lists the 31 new member functions added to COleControl.
Table 5.7. The member functions of COleControl that have been added to support ActiveX Controls.
The beauty of Visual C++ and MFC is that they perform the mundane task of creating the framework for your control,
leaving you the task of making your control perform the functionality you want it to create. At the center of this is the
AppWizard, which houses the OLE Control Wizard. Visual C++ 4.2 with the 4.2b patch has augmented the OLE Control
Wizard to specifically support ActiveX Controls. In this section, you examine each feature of the Control Wizard and
create your first ActiveX MFC Control.
You will need to first launch Visual C++. Once you have Visual C++ up and running, select File from the menu and then
New from the pop-up menu. You will then see the New dialog box as shown in Figure 5.18.
Select Project Workspace. This yields the New Project Workspace dialog box (see Figure 5.19). At the New Project
dialog box, you need to select the OLE ControlWizard from the list box on the left, and you need to give your control a
title and a location. In this case, call it "Simple Control" and accept the default location.
Figure 5.19. The New Project Information dialog box in Visual C++.
You are now looking at the first page of the OLE Control Wizard (see Figure 5.20). Here the OLE Control Wizard asks
you a series of questions about what you would like in your control:
In this case, you are going to create only one control, so you select one control for this project. As you have already
learned earlier in this chapter, one OCX can contain several controls.
You also select the choice for the Control Wizard to include licensing support for this control. In addition, you ask the
Control Wizard to document the code it is going to write for you in the control framework with comments.
Lastly, you ask the ControlWizard to generate a basic help file, so you can provide online help for the Web Masters and
programmers who will be utilizing this control. It is extremely important that this control be well documented. You then
select the Next button and go to page 2 of the OLE Control Wizard (see Figure 5.21).
Step 2 of the OLE ControlWizard presents you with more options for this OLE control.
The OLE ControlWizard enables you to control the naming of each of the controls in your project (see Figure 5.21) to
include the class names, source file names, and property sheet names. If you press the Edit Names button (see Figure
5.21) you will get the Edit Names Dialog as seen in Figure 5.22. It does provide a default naming convention, and in this
case, you will accept the defaults provided by the ControlWizard.
Figure 5.22. The Edit Names dialog box in Step 2 of the OLE ControlWizard in Visual C++.
Next are questions regarding what features you want to have in this control. You need to keep in mind the previously
discussed section on optimizations. Does the control need to be active when visible, or is it invisible at runtime like a
timer control or a communications control? This control will need to be active and visible. You want this control to be
available in the Insert Object dialog, so you will choose this option. No doubt you are proud of the controls you create,
so you can include an About dialog box to post your name or your company's name. Lastly, do you want this control to
be a simple frame control and support the ISimpleFrameSite interface? This is so the control can act as a frame for other
controls. For this example's purposes, you will not choose this option.
You now need to take a look and select the advanced options that support ActiveX Enhancements. Click the Advanced
button and go to the Advanced ActiveX Controls Features dialog as depicted in Figure 5.23.
Figure 5.23. The Advanced dialog box of Step 2 of the OLE ControlWizard in Visual C++.
From the Advanced ActiveX Controls Features dialog, you can choose one of six options. Keep in mind the previous
information you have covered on these options.
Windowless activation
Unclipped device context
Flicker-free activation
Mouse pointer notification when inactive
Optimized drawing code
Loads properties asynchronously
Choose all but Windowless Activation, and click the OK button. Then you need to select the Finish button. Here you will
get a summary of the features the OLE Control Wizard will create for you in the New Project Information dialog (see
Figure 5.24).
Figure 5.24. The New Project Information dialog of the OLE ControlWizard in Visual C++.
When you click the OK button of the New Project Information dialog, the OLE ControlWizard will create a basic control
for you and implement all the features you selected in it. This control need only be compiled and it is up and running.
The OLE ControlWizard even added an ellipse in this control's drawing code so it will have something to display. You
now have the framework to start customizing this control. The nice thing is that most of that functionality is already
encapsulated in MFC. To assist you in this endeavor, Visual C++ provides you the Class Wizard. The sky is the limit on
what types of creations are possible now that you have the framework built for you.
Because MFC-based Controls come with the overhead of the MFC runtime dynamic link libraries, the Visual C++
Development team created the ActiveX Template Library. The ActiveX Template Library is a set of template-based C++
classes to create small fast COM objects. These classes eliminate the need for any external DLLs or any C runtime
library code.
In fact, the ATL will produce an in-process server that is less than 5K. Compared to the 22K control plus the 1.4M MFC
DLL, that is a significant decrease in size. However, this reduction in size comes with an increased complexity and an
increase in the required work to create an ActiveX Control. The ATL does provide all the COM connections for you and
a Visual C++ Wizard called the ATL COM AppWizard to guide you in setting up the framework for your control.
The ATL not only allows you to build controls, but also has support for you to build the following COM objects:
In-process servers
Local servers
Service servers
Remote servers that use the Distributed Component Object Model or Remote Automation
COM thread models including Single threading, Apartment-model threading, and Free threading
Aggregatable servers
Various interface types including custom COM interfaces, dual interfaces, and IDispatch interfaces
Enumerations
Connection points
OLE error mechanisms
Because the ActiveX Template Library provides C++ templates, you have a lot of flexibility to customize a COM object,
and in this case this is an OLE Control. As such you utilize the classes by instantiating an instance of the provided class
from the template and use it as the basis for your class. This differs from the traditional method of deriving your control's
classes from the classes in MFC. This is the distinction between a class library and a template library.
The code in the ATL is highly optimized for the task of creating light, fast COM objects. It still has a lot of the flexibility
of the MFC way, and like the MFC way of creating the controls, it keeps you from having to writing a lot of low-level
COM code. It requires a through understanding of OLE, COM, and their interfaces.
In order to use the ActiveX Template Library, you will first have to download it.
Note
The most current ActiveX Template Library is available from Microsoft, at no charge, at the
following Internet URL: http://www.microsoft.com/visualc/v42/atl/default.htm.
The ActiveX Template Library file is a self-extracting archive. In addition, it may also be obtained in future releases of
Visual C++. If you subscribe to Level II or higher of the Microsoft Developers Network Library, the ActiveX Template
Library should be included in the future releases of MSDN. The location and availability is subject to change by
Microsoft.
You will also need Visual C++ 4.1 or greater and the ActiveX SDK. See the previous section's instructions for obtaining
the ActiveX SDK and the required Visual C++ components. You must also be running Windows NT 4.0 or greater (non-
beta), or Windows 95.
The ActiveX Template Library comes in three files: atlinst.exe, docsinst.exe, and sampleinst.exe. The atlinst.exe file
contains the ActiveX Template Library. The docsinst.exe contains the documentation for the ATL including setup
instructions and a very good white paper. Lastly, the sampleinst.exe file contains some sample applications to guide you
in your creation of applications with the ATL.
Caution
The ActiveX Template Library file atltn001.txt file gives instructions for extracting the files
through PKUnZip and the -d option. Ignore these instructions because they are incorrect.
The files for the ATL are self-extracting and self-installing.
One of the nice features the developers of the ActiveX Template Library included is a Visual C++ Wizard to perform
some of the more mundane tasks of creating a framework for a COM object, such as a control. This leaves you the task
of making your control perform the functionality you want it to have as opposed to recreating abilities all ActiveX
Controls need. The ATL COM AppWizard is the mechanism to create that framework. It will get you up and running
quickly, though not as quickly as OLE ControlWizard and MFC.
You will need to first launch Visual C++. Once you have Visual C++ up and running, select File from the menu and then
New from the pop-up menu. You will then see the New dialog box.
Select Project Workspace. This yields the Project dialog box. At the New Project dialog box, you need to select the ATL
COM AppWizard from the list box on the left, and you need to give your control a title and a location. In this case, call it
"ATL Simple Control" and accept the default location. Then press the Create button.
You are now looking at the first page of the ATL COM AppWizard (see Figure 5.25). Here the ATL COM AppWizard
asks you a series of questions about what kind of COM Object you want to create.
At the top of the Wizard is a spin button with an edit control to enter the number of COM objects you want in your
project. Don't worry; you can add more later if you are unsure. However, you will have to do this by hand. You know in
this case you are creating the framework for one control, so choose one.
Right next to the number of objects spin button is the interfaces per object spin button. This enables you to generate up to
three interfaces for each object. Note that you cannot have a different number of interfaces on different objects. If you
want this, you will have to implement it by hand.
When marshaling interfaces are required, you will need to select the Allow merging of proxy/stub code check box. This
option places the proxy and stub code generated by the MIDL Compiler in the same DLL as the server. Even though the
wizard does some of the work for you, note that in order to merge the proxy/stub code into the DLL, the wizard adds the
file dlldatax.c to your project. You need to make sure that precompiled headers are turned off for this file, and you will
need to add _MERGE_PROXYSTUB to the defines for the project.
Why the Support MFC check box was included is unclear. The main purpose of using the ATL is to get away from the
overhead of MFC. You could have just used the OLE Control Wizard with MFC and saved yourself a lot of time and
effort in the first place. However, if for some reason you want to utilize the MFC Class Library, check this option. It will
give you access to the MFC Class Library functions.
The ATL COM AppWizard asks you the type of registry support you want for your control. There are two options:
Simple (Non-extensible) ATL 1.0 and Advanced (Script Based). The Simple option provides the control with basic self-
registration abilities. On the other hand, the Advanced option uses a scripting language. This special scripting language
enables you to utilize replaceable parameters during control self-registration.
You now need to select the type of server you want the ATL COM Wizard to create. Your options are: an in-process
server (DLL), Local (EXE), or a Service (EXE). When creating a service, you are required to use script-based
registration. In addition, when creating a service or executable, you are unable to use MFC or allow merging of
proxy/stub code.
The last choice on the ATL COM AppWizard is the type of interfaces to create. The ATL COM AppWizard can create
either custom interfaces, derived from IUnknown, or it can create dual interfaces derived from IDispatch.
You then select the next button and go to page 2 of the ATL COM AppWizard.
Step 2 of the ATL COM AppWizard presents you with a single option for this ActiveX control. It enables you to edit the
class and the COM names of this control.
Once you are satisfied with the names of your classes and your COM objects, click the OK button. Then click the Finish
button on page 2 of the ATL COM AppWizard. The ATL COM AppWizard will then show the New Project Information
dialog box. This dialog box shows the selections you have made in creating your COM object.
When you click the OK button of the New Project Information dialog, the ATL COM AppWizard will create a basic
COM object for you and implement all the features you selected in it. This control need only be compiled and it is up
and running. You now have the framework to start customizing this control.
The ActiveX Software Development Kit provides another way to produce ActiveX Controls. This is by far the most
difficult way to create a control. It is provided by Microsoft as a "bare bones" method of creating a control. This is not
for the faint of heart and requires extensive knowledge of OLE, COM, and the OLE Control interfaces. Only minimal
functionality is provided in the code base. You will have to hand code all your messaging, which is a best a daunting
task. The only reason to use this method is to try to create the lightest and fastest control possible; however, the time and
complexity of creating a control with this method may not be worth the performance gains. This method is not
recommended unless you absolutely have to use it. The ActiveX Template Library and Microsoft Foundation Class
methods are much easier to implement and much more flexible. Creating and testing a control is no easy task; there are a
lot of factors involved. Tools are not talent, but why not use the tools available to make your job easier? As the ATL and
MFC methods of creating a control improve, the BaseCtl Framework method in the ActiveX SDK will die away. Some
die-hard low-level C programmers or assembly language programmers may want to dive into this low-level approach
head first, but make sure you are prepared. It would take a whole chapter to even begin addressing this method of
creating a control. The next section covers how to get the framework and set it up, but it is up to you to examine the
samples that come with the SDK and explore the quagmire that awaits you in using this method. The authors highly
discourage you from using this method.
To get the BaseCtl Framework, you will first need to get the ActiveX Software Development Kit. An updated version of
the ActiveX SDK was released at same time Internet Explorer 3.0 was released. The new ActiveX SDK was updated to
include the new technology and features of Internet Explorer 3.0.
Note
The most current ActiveX SDK is available from Microsoft, at no charge, at the following
Internet URL: http://www.microsoft.com/intdev/sdk/.
In addition, you will also need to obtain the Win32 Software Development Kit. The ActiveX SDK requires the August
1996 or later version of the Win32 SDK. The Win32 SDK is available in the Microsoft Developers Network Library
Subscription Level II. The required Win32 SDK components come with Visual C++. In addition, you will need to have
Visual C++ 4.2b or greater to use the Win32 SDK.
Note
The most current Win32 SDK is available from Microsoft through an MSDN Library
Professional Subscription Level II. Microsoft Sales can be reached at 1-800-426-9400.
Once you have the Win32 SDK, you will need to follow the instructions included with it. Pay particular attention to the
environment variable that must be set. Included in the Win32 SDK is a SETENV.BAT batch file that will set these
Win32 SDK environment variables for you.
If you have Visual C++, your Win32 environment is already set for you.
The BaseCtl Framework comes on the ActiveX SDK. Once the ActiveX SDK installed, the BaseCtl Framework is in the
following location C:\INetSdk\Samples\BaseCtl\Framewrk (assuming that you installed it on your C: drive. You will
have to compile the BaseCtl Framework libraries before you can create a control, or compile any of the samples that
come with the ActiveX SDK.
To compile the BaseCtl Framework the using the Win32 SDK or another compiler, follow the instructions in the
ActiveX SDK. To compile them from the Visual C++ Integrated Development Environment (IDE), follow these
instructions.
dialog.
21. 4. You can now build the project in Visual C++.
Once you have compiled the debug and release versions of the BaseCtrl Framework, you can start creating your control.
You may want to use one of the sample controls as a template, but if you were going to do that you might want to just
use MFC or the ATL.
Summary
This chapter discussed the ActiveX Control, which is a superset of the OLE Control. OLE Controls are nothing more
than COM objects. ActiveX Controls are a leaner and meaner implementation of the OLE Control to facilitate its use on
the Internet. ActiveX/OLE Controls enable you to use prepackaged components of functionality to aid you in creating
useful applications.
Chapter 6
Creating OLE Control Containers
Extended Controls
IOleClientSite
IOleInPlaceSite
IOleControlSite
IOleInPlaceFrame
IOleContainer
IPropertyNotifySink
IClassFactory2
Chapter 6
By Thomas L. Fredell
Note
The SimpleControl used in this chapter is not the same as the one used in Chapter 5. The same
name was inadvertently used in both chapters.
Before OLE Custom Controls, or OCXs/ActiveX controls, the major software component container was Visual Basic. Visual
Basic used a proprietary means of communicating with its components, which were called VBXs, or Visual Basic Custom
Controls. Using a VBX in a C or C++ application basically meant emulating the specific control containment functionality of
Visual Basic.
With the advent of the generic standard of OLE Custom Controls, there are no restrictions on the type of applications that can
act as control containers. The minimum requirements for a functional control container are that: 1) it can provide COM-style
interfaces to a control; 2) it supports the minimum set of control container interfaces expected by an embedded control; and
3) it implements the minimum number of required functions in the aforementioned interfaces.
The complexity of control containers ranges from full development environments like Visual Basic 4, which allow controls to
be integrated into the environment through the toolbar and implement design-mode and run-mode versions, to very simple
control containers, like dialog boxes, implemented using the Microsoft Foundation Classes. This chapter discusses the
characteristics of control containers and implementation of containers using the Microsoft Foundation Classes.
Control and container interaction is based on the same foundation as the interaction of standard OLE document objects and
their containers. Several of the interfaces required for control containers, such as IOleInPlaceSite and IOleInPlaceUIWindow,
are used for interaction between OLE compound document objects and their containers. The standard interfaces are
supplemented by several required interfaces designed specifically for control and container interaction, such as
IOleClientSite and IOleControlSite. All of the required interfaces are listed later in this chapter, with detailed information
about the purpose of the interface methods. There are also several optional container interfaces; containers may elect to
implement or use them if they require the interface functionality.
One of the most important interfaces required for control/container interaction is IDispatch, the OLE automation dispatch
interface. The control exposes an IDispatch interface that its container uses to access the control's properties and methods.
The container also exposes an IDispatch interface to the control that allows the control to pass events to its container.
Each control embedded in a container is allocated a control site by the container. The container lays out the contained control,
manages keyboard interaction, enables the properties of controls to be saved in some persistent format, handles events
generated by controls, and exposes ambient properties to controls. Ambient properties allow OLE controls to retrieve
information about the control site provided by their container.
Some containers wrap their controls with extended controls. Extended controls allow containers to maintain additional
properties and events for controls without changing the mechanism through which they interact with the controls.
The next few sections describe in more detail the issues involved with creating controls, communicating with them, and
wrapping them with extended controls.
The control container can create a control by retrieving the control's IClassFactory interface through the standard OLE
function CoGetClassObject(). The container can then use the ClassFactory::CreateInstance() function to create an instance of
the OLE control. ClassFactory::CreateInstance() may return the error CLASS_E_NOTLICENSED if the control requires the
container to specify control licensing information before it can be created.
The ClassFactory2 interface handles control licensing; it extends the ClassFactory interface with the GetLicInfo(),
RequestLicKey(), and CreateInstanceLic() functions.
GetLicInfo() enables the container to retrieve information regarding the licensing capabilities of the control. The function
fills a LICINFO structure whose members describe the control's licensing information on the current machine:
ULONG cbLicInfo;
BOOL fRuntimeKeyAvail;
BOOL fLicVerified;
} LICINFO;
The LICINFO.cbLicInfo structure member simply specifies the size in bytes of the LICINFO structure. The
LICINFO.fRuntimeKeyAvail member indicates whether or not the control can be instantiated on an unlicensed machine; if it
is TRUE, the control can be created using a key obtained from ClassFactory2::RequestLicKey(). LICINFO.fLicVerified
indicates whether or not a full machine license for the control exists.
RequestLicKey() allows a control container to create a runtime instance of a control on a machine that doesn't have a full
license for the control (see the following example). If RequestLicKey() succeeds, the control's ClassFactory2 implementation
will fill the pbstrKey with a string allocated using SysAllocString(). The container can pass the key to CreateInstanceLic();
the container is responsible for freeing the key using SysFreeString() after it is finished with it.
HRESULT ClassFactory2::CreateInstanceLic(IUnknown*
CreateInstanceLic() uses the key obtained from the control through RequestLicKey() to create an instance of the control (see
the following example). The pUnkOuter argument is a pointer to the controlling unknown for the control if the control is
being aggregated; a control container that uses extended controls would pass its IUnknown implementation as an argument.
pUnkReserved is a currently unused argumentit must be specified as NULL. The riid argument is the type of interface
pointer requested by the container; a container may use the IID_IOleObject to retrieve the control's implementation of the
IOleObject interface. The bstrKey is the key obtained using the ClassFactory2::RequestLicKey() function. Finally, the
ppvObject is a pointer to the interface returned by the control.
The primary communication link between a container and a control is through the OLE automation IDispatch interface.
IDispatch, the foundation of OLE automation, provides a generic means through which function calls may be passed to an
object. For OLE controls, a control-provided IDispatch allows a container to access control properties and methods.
Likewise, a container-provided IDispatch enables controls to access container ambient properties and alert the container of
events.
Note
Communication between a control and container is pretty complex. The next few sections
describe some of details of the communication, but I should forewarn you that the Microsoft
Foundation Classes basically handle all of the details for you.
Fundamentals of IDispatch
The primary IDispatch method used for control/container communication is Invoke(). The prototype for Invoke() is:
Callers of IDispatch::Invoke() use a Dispatch ID, or DISPID, to identify the OLE automation member that they want to
access. The value of wFlags indicates whether a property or method is the target of Invoke(). Parameters to the property or
method are passed to Invoke() as an array of variants, and the return value from the call is used to fill the pvarResult
parameter.
Controls may contain a large number of properties and methods. As you know from the previous chapter, the capabilities of
the control are defined in the control's type information, or type library. Control containers get access to the properties and
methods using a control's IDispatch interface.
To get or set a property value, or to call a control method, you use the control's IDispatch::Invoke() implementation. You
specify the specific property or method using its dispatch ID; you can determine the dispatch IDs statically by using a tool
like the OLE viewer to examine the control's type library, or you can use the control's type information at runtime.
Later in the chapter, the examples use a control called, appropriately enough, SimpleControl. SimpleControl really doesn't do
anything interesting; it exposes two properties, no methods, and two events. One of the properties is a SimpleName property
with a DISPID of 2. SimpleName is a string; setting the value of SimpleName causes the control to fire an
OnSimpleNameChange event. The following code can be used to change the SimpleName property:
VariantInit(&varArg);
V_VT(&varArg) = VT_BSTR;
dispparams.rgvarg = &varArg;
dispparams.cArgs = 1;
dispidNamed = DISPID_PROPERTYPUT;
dispparams.rgdispidNamedArgs = &dispidNamed;
dispparams.cNamedArgs = 1;
if (FAILED(hres))
// Handle error
else
VariantClear(&varArg);
The level of effort illustrated in the code snippet is representative of what's necessary to manipulate control properties or
methods. You will probably notice that most of the work in manipulating a control's properties or methods is just setting up
the parameters. The preceding example is a simple one; if you're dealing with named arguments for method calls, it can get
more complex. For more information, you can check out the documentation for the IDispatch interface in the Win32 SDK
help.
Controls notify their containers of events using their pointer to the container's IDispatch interface. Before a control can do so,
the container must connect its IDispatch to the control as an outgoing interface. To connect its IDispatch, the container must
find the control's connection point for the interface.
Connection points are maintained by controls that fire events. The interface to the set of control points, or more precisely
IConnectionPoint objects, is through the control's implementation of IConnectionPointContainer. The
IConnectionPointContainer interface allows the container to enumerate the control's outgoing interfaces, or to find a specific
outgoing interface using an interface ID or IID. For events, the container needs the IID of the control's IDispatch interface
with the default and source attributes. You can get the IID statically, using the aforementioned OLE Viewer tool, or you can
find it at runtime using the control's type information interfaces. Once the container has acquired the correct
IConnectionPoint interface, it can attach its event IDispatch using the IConnectionPoint::Advise() method.
When a control fires an event, it passes the parameters through the standard IDispatch array mechanism. The control uses the
DISPID in the call to IDispatch::Invoke() to indicate which event occurred.
Ambient properties are control container properties that are analogous to control properties. A container maintains ambient
properties for all of its controls; typically, the container will expose the same values to all controls within a given frame. The
OLE standards define a set of ambient properties with associated IDs that should be implemented by a container. The
container also has means through which it can indicate to controls that the ambient properties have changed.
The implementation of control container properties is very similar to the implementation of control properties; container
ambient properties are provided by an IDispatch interface that can be retrieved from the IOleControlSite interface that hosts
the control. Controls get ambient property values in the same manner that containers get control properties; the container
returns the value of the property in the return value from the IDispatch call.
The following list contains the standard ambient properties that may be provided by an OLE control container:
BackColor
Type: OLE_COLOR
Desc: This specifies the color for the control's interior.
DisplayName
Type: VT_BSTR
Desc: The control can use this property to retrieve a container-specified name that it should display in error
messages.
Font
Type: OLE_FONT
Desc: This specifies the standard font that the control should use. The control can interpret this property in
whatever fashion it chooses.
ForeColor
Type: OLE_COLOR
Desc: This specifies the color for text and graphics in a control.
LocaleID
Type: VT_I4
Desc: This property specifies the locale identifier for the container's UI.
MessageReflect
Type: VT_BOOL
Desc: The MessageReflect property indicates whether or not the container reflects Windows messages back to
the control.
ScaleUnits
Type: VT_BSTR
Desc: The container specifies the name of its coordinate units using this property.
TextAlign
Type: VT_I2
Desc: The container uses this property to specify the text alignment in the control. Valid values for the
property, with their meaning, are 0[en]general alignment, numbers to the right and text to the left; 1[en]left
justify text; 2[end]center justify text; 3[en]right justify text; and 4[en]fill justify text.
UserMode
Type: VT_BOOL
Desc: This indicates the current user interaction mode. If it is FALSE, the user interaction mode is "Design
Mode"; otherwise, the user interaction mode is viewing or interacting.
UIDead
Type: VT_BOOL
Desc: The control can use this to determine whether or not the container is allowing user input. A situation in
which it is very useful for a control to disallow control interaction is during a container's "Debug" mode
(assuming, of course, that the container has such a mode).
ShowGrabHandles
Type: VT_BOOL
Desc: The container uses ShowGrabHandles to indicate to controls whether or not they should display grab
handles when they are UI active.
ShowHatching
Type: VT_BOOL
Desc: This property allows the container to indicate the desired hatching feedback for UI active controls.
DisplayAsDefaultButton
Type: VT_BOOL
Desc: Button-like controls use this property to determine if they should display themselves using a visual
default button indication (like a thick border).
SupportsMnemonics
Type: VT_BOOL
Desc: Containers indicate support for control mnemonics using this property.
AutoClip
Type: VT_BOOL
Desc: This property indicates whether or not the container will perform automatic clipping for
the control.
Some Ambient properties, such as UserMode, may apply to all of the controls within a control container. Other ambient
properties, such as BackColor, may depend on the window to which the control is connected.
Extended Controls
Extended controls may be implemented by control containers to provide a wrapper for contained controls. Extended controls
allow containers to associate whatever additional information or events they may need to maintain controls. Wrapping a
control with an extended control simply involves performing standard COM aggregation. When a container creates a control
instance, it passes in a controlling unknown that implements IDispatch for extended properties and events. If a request for a
control property is received by the extended control, it passes the request through to the control's interface implementation.
The advantage of wrapping controls with extended controls is that the container can treat its properties and events and the
control's properties and events without specialized code. Visual Basic is one of the best examples of a container that
implements extended controls due to the seamless integration with its user interface.
Visual Basic uses extended controls extensively. Each control embedded in a Visual Basic Form has extended properties that
store information such as a control's Top and Left coordinates within a form.
Visual Basic does an excellent job of integrating extended properties and events with standard control properties and events.
Earlier in this chapter, you were introduced to the SimpleControl OLE control. SimpleControl has only two properties,
SimpleName and SimpleName2, and two events, Click and OnSimpleNameChange. However, after you've added a
SimpleControl to a VB form, a whole bunch of properties appear for the SimpleControl object. Figure 6.1 illustrates the
properties box for the SimpleControl object in Visual Basic. Notice that SimpleControl's properties are integrated with the
other Visual Basic extended properties. From the user's perspective, there is no difference between the properties maintained
by the control and the properties maintained by Visual Basic.
Visual Basic also has a tool called the Object Browser. The Object Browser displays the methods and properties exposed by a
Visual Basic control or by a referenced type. A user can add referenced types to Visual Basic using type libraries and Visual
Basic's Tools|References menu option. Figure 6.2 illustrates the display from the Object Browser when SimpleControl is
added as a control to a Visual Basic project.
If SimpleControl is added to a Visual Basic project as a reference instead of a control, only the properties and methods that
are part of SimpleControl are visible in the Object Browser. Figure 6.3 shows the Object Browser when SimpleControl is
loaded as a reference.
As indicated previously, Visual Basic also adds events to its controls. SimpleControl has only Click and
OnSimpleNameChange events; Figure 6.4 illustrates SimpleControl's events and the events added by Visual Basic.
Unlike ambient property guidelines, there is no standard for the set of extended properties or events implemented by a control
container. Visual Basic provides a good model for extended property integration; it includes properties that handle generic
issues like storing control layout information (Top, Left, and so on), and it integrates those properties seamlessly with its user
interface.
There are a substantial number of OLE Interfaces that must be implemented by an OLE control container. The following
sections describe the interfaces and the expected behavior of the container upon receiving a method call on the interface.
Some of the methods are indicated as optional methods; the optional methods are not essential for the functioning of controls
in a control container. Containers can return E_NOTIMPL or S_OK as appropriate for the optional methods.
IOleClientSite
Controls use IOleClientSite to obtain information about their container, including information that can be used to interact
with other controls in the container.
The control uses this method to acquire a pointer to the container's IOleContainer interface. A control may use the
IOleContainer interface to navigate to other controls contained within the control's container.
HRESULT IOleClientSite::ShowObject();
The control uses this method to ask its container to show it; this ensures that the control and container are visible:
The control calls this to notify its container when its window is about to become visible or invisible:
HRESULT IOleClientSite::SaveObject();(optional)
This method is typically used to save the object that is connected to a client site. The embedded object uses SaveObject() to
request its container to save it to persistent storage; the object will probably call this method during the call to its
IOleObject::Close() method.
[icc]IMoniker **ppmk);(optional)
GetMoniker() is used by an embedded object to request a moniker, which is used to support OLE linking, from its container.
HRESULT IOleClientSite::RequestNewObjectLayout();(optional)
A loaded but inactive control uses RequestNewObjectLayout() to ask its container to allocate more or less space to display
the control. In the implementation of RequestNewObjectLayout(), the container can query the control for the desired size by
calling the control's GetExtent() method.
IAdviseSink
Control containers only need to implement IAdviseSink if they want to receive notifications of changes to controls that
support IDataObject or IViewObject. Containers may also choose to implement this if they support insertion of controls as
standard embedded OLE objects.
If a control supports IDataObject, it can notify its container of data changes through this method:
If a control supports IViewObject, it can call this container method to indicate that the view of the control has changed:
This container member will be called when a control's moniker changes if the control supports linking:
void IAdviseSink::OnSave();
The control uses OnSave() to notify its container that it has been saved:
void IAdviseSink::OnClose();
IOleInPlaceSite
This interface is used to manage interaction between a control container and a control's client site. The client site is the
display site for the control; consequently, the interface is derived from the IOleWindow interface. IOleInPlaceSite provides
methods that may be used to manage the activation and deactivation of a control, retrieve information about the position in
the parent window where the control should place its in-place activation window, manage scrolling of the control, the
control's undo state, and control borders.
The control uses this to retrieve the handle to its in-place window:
A control can notify its container of a request for context-sensitive help by calling this container method:
HRESULT IOleInPlaceSite::CanInPlaceActivate();
This is used by the control to determine whether or not the container will allow it to active in place:
HRESULT IOleInPlaceSite::OnInPlaceActivate();
The control calls this to notify its container that it is in the process of becoming in-place active:
HRESULT IOleInPlaceSite::OnUIActivate();
The control uses this method to notify its container that it is about to be activated in place. In response and if appropriate, the
container should remove whatever user interface is part of its own activation. If a different control is being deactivated as this
control becomes active, the container should notify the other control of its state change using its UIDeactivate() method.
[icc]LPOLEINPLACEFRAMEINFO lpFrameInfo);
A control calls this on activation to retrieve pointers to the IOleInPlaceFrame and IOleInPlaceUIWindow interfaces provided
by its container, the position and clipping rectangles for the control, and an OLEINPLACEFRAMEINFO structure listing
accelerators supported by a container during an in-place session.
This is called by a control to request its container to scroll. After the container has finished scrolling, it should check if the
control's visible rectangle has been affected. If it has been affected, the container should call the control's SetObjectRects(),
on the IOleInPlaceObject interface, to give the control a new clipping rectangle.
The control uses this to notify its container that it is deactivating its user interface components; the container should
correspondingly reinstall its user interface. The fUndoable flag indicates whether or not the control can undo changes that
occurred; to undo the changes, the container can call the control's OleInPlaceObject::ReactivateAndUndo() method.
HRESULT IOleInPlaceSite::OnInPlaceDeactivate();
Controls call the container's OnInPlaceDeactivate() method to indicate that they are fully deactivated. After a control has
called this method, it is no longer possible for the container to undo changes.
HRESULT IOleInPlaceSite::DiscardUndoState();
This method is used by the control to indicate to its container that there is no longer any undo state; therefore, the container
should not call the control's OleInPlaceObject::ReactivateAndUndo() method.
HRESULT IOleInPlaceSite::DeactivateAndUndo();
The control calls this if the user invokes undo immediately after activating it. In response, the container should call the
control's IOleInPlaceObject::UIDeactivate() method to activate itself, remove the control's UI, and undo changes to the
control's state.
This is called by a control to indicate a size change; the container should call the control's
IOleInPlaceObject::SetObjectRects() to inform the control of the new size and position of the in-place window and new
clipping rectangle.
IOleControlSite
Control containers implement this interface to communicate with embedded controls. The following methods are part of the
IOleControlSite interface.
HRESULT IOleControlSite::OnControlInfoChanged(void);
The control calls this method to indicate to the container that the control's control information has changed. The control
information is stored within the CONTROLINFO structure; the container can retrieve the updated information from the
control using the GetControlInfo method on the control's IOleControl interface. The CONTROLINFO structure contains
control keyboard accelerators and keyboard behavior flags.
The control calls this to determine whether or not it should remain in-place active even if some type of deactivation event
occurs:
This is called by the control to obtain the IDispatch pointer to the extended control implemented by the container. Extended
controls are used by the control container to maintain additional properties for a control, such as its X and Y location within
the control container. Control containers like Visual Basic use extended controls to implement standard control properties
like Top, Left, Height, and Width.
The control uses this to convert OLE standard HIMETRIC units in a POINTL structure to the units preferred by the container
in a POINTF structure. This method may also be used to do the reverse, that is, convert coordinates from control into
standard HIMETRIC units.
UI Active controls use this method to defer keystroke processing to their container. After a control becomes UI Active, the
container transfers keystrokes to the control using the TranslateAccelerator() method on the control's
IOleInPlaceActiveObject interface.
This is called by the control to indicate to the container whether it has gained or lost input focus; the container can manage
Default and Cancel button state accordingly:
HRESULT IOleControlSite::ShowPropertyFrame();
The control uses this method to request the container to display a property page frame for the control. The container can take
the opportunity to create a property frame that includes pages for extended control properties; this ensures that both extended
and standard control properties are maintained within a single, consistent user interface.
IOleInPlaceFrame
Controls that have associated frame-level tools, like toolbars or menu items, use this container interface to manage container
user-interface changes on control activation. Containers can choose to implement optional methods based on their user-
interface characteristics. For example, if a container has toolbars, it may choose to implement the toolbar-oriented negotiation
functions like GetBorder() and RequestBorderSpace().
The control uses this to retrieve the handle to its in-place window.
HRESULT IOleWindow::ContextSe
nsitiveHelp(BOOL fEnterMode);(optional)
A control can notify its container of a request for context-sensitive help by calling this container method:
Controls can use this container method to retrieve the outer rectangle, relative to the frame window, where the control can
install its toolbar:
HRESULT IOleInPlaceUIWindow::RequestBorderSpace(LPCBORDERWIDTHS
[icc]pborderwidths);(optional)
A control calls RequestBorderSpace() with a rectangle indicating the desired space for a toolbar before attempting to install
its toolbar UI. If the container accepts the request by returning S_OK, the control can call SetBorderSpace() to ask the
container to allocate the requested space.
HRESULT IOleInPlaceUIWindow::SetBorderSpace(LPCBORDERWIDTHS
[icc]pborderwidths);(optional)
The container's SetBorderSpace() method is called when the control requests the allocation of space for the control's toolbar.
[icc]LPCOLESTR pszObjName);
Controls call SetActiveObject() to establish a communication link to the container's frame window:
[icc]LPOLEMENUGROUPWIDTHS lpMenuWidths);(optional)
This method is called by controls to build up a composite menu containing the container's and control's menu items:
[icc]HWND hwndActiveObject);(optional)
SetMenu() is called by controls to request the container to install a composite menu built up by previous calls to
InsertMenus():
The control calls this method to allow the container to remove its menu elements from the composite menu:
A control can call SetStatusText() to request the container to display status text from the control in the container's status line:
The control can call EnableModeless(FALSE) to ask its container to disable any modeless dialog boxes that it may be
displaying. After the container has done so, the control may display its own modal dialog. When it is finished, the control
should call EnableModeless(TRUE) to re-enable the container's modeless dialogs.
This method is used to translate keystrokes intended for a container's frame window when a control is active in place.:
IOleContainer
Controls can use a container's implementation of IOleContainer to retrieve information about other controls in the container
or to perform object linking functions.
Controls that support linking use this to ask their container to parse a display name and create a moniker. Containers only
need to implement this method if they support links to controls or other embedded objects.
If a control and its container support linking, the control will call LockContainer(TRUE) to keep the container running until
all link clients have been updated. After the clients have been updated, the control should call LockContainer(FALSE) to
remove external locks on the container and allow the container to terminate.
A control can use this method to enumerate all of the controls and objects in its container. It's important to note that the
enumerator may not actually return all visible controls in the container because some of them may be standard Windows
controls.
There are a number of container interfaces that are not required but that may be implemented or supported by containers that
require their functionality. The following sections describe the interfaces and the expected behavior of the control or
container upon receiving a method call on the interface.
ISimpleFrameSite
Containers can choose to implement support for the ISimpleFrameSite interface if they want to support controls that contain
other controls. An example of a control that might make use of this interface is a group box that handles certain interaction
characteristics of its contained controls. The purpose of this class is to allow controls to filter messages to controls that they
contain while allowing them to defer messages for processing by the root control container.
This method gives a control the opportunity to process a message that is received by a contained control's window before the
contained control does any processing:
A control can use this method to defer message processing to the control's container after the control and its contained control
have had an opportunity to process the message:
IPropertyNotifySink
Containers implement the IPropertyNotifySink interface if they want to receive notifications about control property changes.
This is useful, for example, if a container maintains its own property-editing user interface. Because this is an outgoing
interface, the container must connect this to the control using the connection point mechanism.
The control uses this method to notify the container that the property with the dispatch ID dispid has changed:
The control uses OnRequestEdit() to notify its container that one of its properties is going to change. The container can
respond with S_OK or S_FALSE. The result S_OK allows the control to proceed with the change; S_FALSE indicates that
the container won't allow the control to make the change.
IClassFactory2
Support for IClassFactory2, described earlier in this chapter in the section titled "Creating an OLE Control Instance," allows
a control container to support runtime licensing. It is implemented by the control, not the control container. If the control
implements it, the container may use it as an alternative to IClassFactory to instantiate a control.
The AppWizard, Microsoft Foundation Classes, and the ClassWizard, all of which are integrated pieces of the Microsoft
Developer's Studio, make it trivial to create a dialog box that is a very functional OLE control container.
To create a dialog-based control container, perform the following steps from the Microsoft Developer's Studio:
1. 1. Create a new project named DlgContainer using the MFC AppWizard. The application type should be executable,
or exe.
2. 2. On the first wizard page, select Dialog Base" and click Next.
3. 3. On the second wizard page, select the OLE Controls option.
4. 4. Click Finish and OK to allow the AppWizard to create your project.
5. 5. Go to the Resources tab in the project workspace, expand the list of dialog resources by double-clicking the Dialog
folder, and double-click to edit the dialog resource with the IDD_DLGCONTAINER_DIALOG identifier.
6. 6. Select Insert, Component from the menu bar. The Component Gallery dialog box will appear. Click on the tab
marked "OLE Controls." Figure 6.5 shows the Component Gallery dialog.
1. 7. Select a control to insert into the project. I've selected the "Simple Control", indicated with a red "thumbs-up"
bitmap, that was generated using the MFC OLE Control classes. Click the Insert button to add the OLE control to
your project.
2. 8. After you have clicked the Insert button, a dialog box will appear that asks you to confirm the name of the control
wrapper class that will automatically be generated. Figure 6.6 illustrates the Confirm Classes dialog. Select OK to
accept the "CSimpleControl" class name. The component gallery will generate C++ header and source files for the
CSimpleControl class. Select Close to return to the resource editor.
The "Simple Control" that is being used here only has simple BSTR-based properties; if a
control had been inserted that supports fonts or pictures, two additional classes would be
createdCFont and CPicture classes.
1. 9. On the resource editor toolbar, you will notice an additional item, a red "thumb's up" that is identical to the control
bitmap in the Component Gallery. Select the "thumb's up" and draw a "Simple Control" on the Dialog box.
2. 10. Select the new control on the Dialog box, and right-click or press Alt+Enter to display the control properties.
Figure 6.7 illustrates the resource editor display after showing the properties. The first page includes properties from
the Developer's Studio, the second and third pages are directly provided by "Simple Control," and the last page
contains all of the control properties presented by the Developer's Studio in list format.
1. 11. Save the modified dialog resource (File|Save from the menu), then press Control+W to display the Control
Wizard.
2. 12. Go the "Member Variables" tab in the Control Wizard, select the CDlgContainerDlg class, and then the
IDC_SIMPLECONTROLCTRL1 control ID. Click the Add Variable buttona dialog box will appear prompting for
the name of the variable. Enter m_simpctrl for the variable name; the variable category should already be control and
the variable type should be CSimpleControl.
That's all that it takes to create an MFC project that includes a dialog box that contains an OLE control! It's truly remarkable
considering the amount of effort that would be needed to support the same functionality if you implemented the equivalent
dialog from scratch.
The Developer's Studio is doing some pretty interesting things here; first, to create the OLE Control page in the Component
Gallery, it's scanning the registry to find controls, extracting their toolbar bitmaps, and finally adding them to what looks like
a ListView control for user selection.
Second, the Developer's Studio is generating a C++ class that wraps the OLE control. Each of the properties of the control is
exposed through Get/Set class methods; the OLE control methods are likewise exposed as class methods. Later in the
chapter, you'll learn about some of the magic behind the implementation of the MFC classes that enable them to take standard
C++ function calls and convert them in to IDispatch calls that OLE controls understand.
Third, event information from the control is scanned and included in the ClassWizard Message Maps tab. Figure 6.8 shows
the ClassWizard with message maps for SimpleControl.
To handle the OnSimpleNameChange event fired by the SimpleControl included in the aforementioned example, all you
need to do is select the IDC_SIMPLECONTROL1 object ID and the OnSimpleNameChange message, then click the Add
Function button. The ClassWizard integrates window message handling and control event handling into the same easy-to-use
user interface. That's very impressive when you consider that the method of retrieving standard window-type events is
completely different from retrieving Event Sink-based OLE control messages.
Having read the descriptions of the interfaces required for OLE control containers, and the information about the interaction
between control and container, you should be very impressed with the Microsoft Developer's Studio and MFC! The
Developer's Studio integrates OLE controls directly into the development environment and alleviates literally all of the work
required to implement a control container.
Creating a non-dialog resource-based MFC SDI Control Container is somewhat more complex than creating a dialog-
resource-based control container. MFC includes several types of CView-derived classes. Some of them, like CFormView,
use dialog resources to lay out controls. Others, like the base CView class, don't use dialog resources for layout. Adding
controls to CView classes is more complex than adding controls to resource-based views because: 1) it requires that you
create the control explicitly in code; and 2) it doesn't automatically integrate control events into the ClassWizard.
Luckily, creating the control in code is pretty simple. Unfortunately, handling control events involves significantly more
manual code editing. The explanations will cover control events after discussing the basic details of creating a SDI control
container.
To create a SDI control container, perform the following steps from the Microsoft Developer's Studio:
1. 1. Create a new project using the MFC AppWizard for executables; name the project "SDIContainer".
2. 2. In step 1 of the AppWizard, select "Single document" for the type of application. Click Next twice to go to step 3 of
the AppWizard.
3. 3. In step 3 of the AppWizard, make sure that the check box next to "OLE controls" is checked. Click Finish to
generate the application.
4. 4. Select Insert|Component from the Main Menu. The Component Gallery dialog box will launch. Select
SimpleControl Control from the OLE Controls tab, then click Insert. Click Confirm in the class confirmation dialog,
then click Close in the Component Gallery.
After completing these steps, you will notice that the ClassView tab of the project browser contains a new
classCSimpleControl. The Developer's Studio created this class in the same manner that the CSimpleControl class was
created in the previous Dialog Box control container example.
Now the manual code editing begins; open SDIContainerView.h in the code editor and make the changes shown in bold in
the following code snippet:
//
/////////////////////////////////////////////////////////////////////////////
#include "SimpleControl.h"
...
protected:
CSimpleControl m_simpctrl;
protected:
//{{AFX_MSG(CSDIContainerView)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
DECLARE_EVENTSINK_MAP()
};
...
You'll notice that the basic change is adding the SimpleControl.h header file to the view implementation header file, as well
as a new member, m_simpctrl, to access and create an instance of the SimpleControl OLE control. You may not recognize
the addition of the DECLARE_EVENTSINK_MAP() macro. That macro sets up the event sink for the view object; if you go
back to the previous dialog box control container example, you'll notice that this was added without any intervention on your
behalf.
Next, use the ClassWizard to create the CSDIContainerView::OnCreate function to handle the WM_CREATE message for
the View class. In the body of CSDIContainerView::OnCreate, add code to create the SimpleControl OLE control instance.
The required changes are marked in bold in the following code snippet:
/////////////////////////////////////////////////////////////////////////////
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
this, ID_SIMPLECTRL);
return 0;
The call to CSimpleControl::Create() initializes the OLE control and attaches it to the CView object (or specifically, in this
case, the CSDIContainerView object which is derived from CView). One of the parameters to the call, ID_SIMPLECTRL,
hasn't been defined yet. ID_SIMPLECTRL is an arbitrary identifier that will be associated with the SimpleControl instance;
the identifier will be used when you make additions to the CSDIContainerView's Event Sink map. You're also missing the
implementation of the Event Sink map, which you previously declared in the header using
DECLARE_EVENTSINK_MAP(). To add the identifier and Event Sink map, make the following additions to
SDIContainerView.cpp:
//
#include "stdafx.h"
#include "SDIContainer.h"
#include "SDIContainerDoc.h"
#include "SDIContainerView.h"
#ifdef _DEBUG
#undef THIS_FILE
#endif
#define ID_SIMPLECTRL 0
/////////////////////////////////////////////////////////////////////////////
// CSDIContainerView
IMPLEMENT_DYNCREATE(CSDIContainerView, CView)
BEGIN_MESSAGE_MAP(CSDIContainerView, CView)
...
END_MESSAGE_MAP()
BEGIN_EVENTSINK_MAP(CSDIContainerView, CView)
END_EVENTSINK_MAP()
Now you have a functional SDI OLE control container application that contains a single OLE control. Properties and
methods of the control can be accessed using the member functions of the CSimpleControl class; the only major piece of
functionality that's missing is the ability to get events from the control. You've already established the foundation for the
event dispatch by including the necessary macro declarations in the CSDIContainer header and implementation files.
The SimpleControl OLE control only exposes two eventsClick and OnSimpleNameChange. In the previous dialog box
control container example, events from the SimpleControl control object were listed directly in the ClassWizard.
Unfortunately, such is not the case for SDI or MDI MFC applications; you must add the code manually. As previously
indicated, you've already added the necessary event sink macros. Now you need to add entries to the event sink map that
correspond to the events you want to capture.
Events are added to the event sink using the ON_EVENT() macro. The details of the ON_EVENT() macro are described later
in this chapter; for now, the explanation covers only what is necessary to handle the OnSimpleNameChange event.
To use the ON_EVENT() macro, you need to know the DISPID or member id of the event that you want to capture, and the
types of parameters passed to the event. You can get that information using the OLE Object View utility (OLE2VW23.EXE),
which can be launched from the Tools menu in the Developer's Studio. To get the parameters and id of the event, open the
type information for the SimpleControl control by using the Object|View File|View Type|Library menu option, and select
SimpleControl.ocx from the File Open dialog box. Figure 6.9 illustrates the information from Object View.
The following is the function prototype reported by the Object Viewer for the OnSimpleNameChange event:
VOID OnSimpleNameChange(
String OldName,
Translated into a C++ prototype, with the function attribute required for MFC message handlers, that is:
The dispatch ID, or DISPID, for the event is 1; it's listed in the Object Viewer as memid = 0x00000001.
Go ahead and add a member function that handles the event to the CSDIContainerView class. You'll call the member
function OnSimpleNameChange() in accordance with the aforementioned prototype. The changes to the SDIContainerView.h
...
DECLARE_MESSAGE_MAP()
DECLARE_EVENTSINK_MAP()
};
...
...
AfxMessageBox("CSDIContainerView::OnSimpleNameChange");
...
Now you can add the ON_EVENT() entry to the EVENTSINK map. Add the following changes, marked in bold, to
SDIContainerView.cpp:
...
BEGIN_EVENTSINK_MAP(CSDIContainerView, CView)
ON_EVENT(CSDIContainerView, ID_SIMPLECTRL, 1, \
END_EVENTSINK_MAP()
...
The arguments to the ON_EVENT() macro are pretty straightforward; the name of the class that gets the event, the ID of the
control that generates the event (this was the ID that was used to create the control in CSDIContainerView::OnCreate()), the
dispatch ID of the event, the name of the CSDIContainerView member function that is called when the event occurs, and the
arguments to the event function encoded as strings. The reason why they are encoded as strings will be discussed later in the
chapter.
Now test your newly implemented event sink. To do so, you need to write some code that causes the OLE control to fire the
OnSimpleNameChange event. Go to the resource tab on the project workspace and edit the main menu, IDR_MAINFRAME.
Add a separator after Paste on the Edit menu, and then add a menu item called Change SimpleName, with C as the hot key.
Change the ID of the option to IDM_CHANGE_SIMPLENAME (you can do this in the Menu Item Properties dialog). Next,
add a message handler to CSDIContainerView using the ClassWizard; the default name for the menu message handler
(assuming that you correctly chose the Object ID IDM_CHANGE_SIMPLENAME and the COMMAND message) will be
OnSimpleNameChange. Change it so that it is OnMenuSimpleNameChangethen click the Edit Code button.
Make sure that your implementation of the message handler for IDM_CHANGE_SIMPLENAME is as follows:
void CSDIContainerView::OnMenuChangeSimpleName()
m_simpctrl.SetSimpleName("ANewName");
Setting the SimpleName property of the SimpleControl instance causes the OnSimpleNameChange() event to fire; MFC
ensures that the CSDIContainerView::OnSimpleNameChange() function is called to handle it.
The Microsoft Foundation Classes do some very interesting things to ease the use of controls in a control container. As
previously indicated, the Microsoft Developer Studio contains integrated tools that automatically generate classes to wrap the
complexity of the control/container interface. Two of the most interesting and enlightening examples of MFC's control
container code are evident in the MFC implementation of property get/sets and the implementation of event handling.
The Microsoft Foundation Classes use a unique and intricate, but easy-to-use, mechanism to implement property Get/Set
methods for controls embedded in a container. The foundation (no pun intended!) of the mechanism is the
COleDispatchDriver class. COleDispatchDriver contains a public function named InvokeHelperV that is used to translate
standard C++ style function calls into Dispatch type calls.
There are some interesting problems that MFC tackles herethe arguments to the property Get/Set method are on the stack,
which is the convention for C++ function calls. The parameters are also standard C/C++ data types, such as longs.
Furthermore, the fact that exposed C++ member functions can be called as standard C++ member functions is remarkable if
you consider that the target for the function call is an OLE control, which uses OLE automation as the means for property
and method calls.
C++ format function calls are not, of course, what the IDispatch::Invoke() function expects. The convention for calls using
IDispatch::Invoke involves passing parameters as variants in a parameter array to a function called using a DISPID or
dispatch id.
To demonstrate the MFC answer to the thorny problem of mapping C++ style calls to IDispatch::Invoke() calls, I created a
dialog-based MFC application using the App Wizard. I then added a grid control to the main dialog using the component
browser; after saving the modified dialog resource, I used ClassWizard to create a member variable to wrap the Grid in the
main dialog class. In the process of creating the member variable, the ClassWizard scanned the OLE type information from
the control, and generated gridctrl.cpp and gridctrl.h files.
The gridctrl.h header file contains the definition for a CGridCtrl class, derived from CWnd, that implements the properties
and methods exposed by the control as C++ class member functions. An excerpt of the class definition from gridctrl.h
follows:
...
protected:
DECLARE_DYNCREATE(CGridCtrl)
public:
return clsid;
...
// Attributes
public:
...
short GetRows();
void SetRows(short);
short GetCols();
void SetCols(short);
...
// Operations
public:
void AboutBox();
...
};
The class contains the CLSID of the wrapped control, and a function to access the CLSID, as well as function declarations
that correspond to properties and methods exposed by the control.
The GetRows and SetRows functions provide functionality that would be provided inside a container like Visual Basic using
the standard assignment operator. In Visual Basic, ctrl.Rows = 10 would therefore correspond to the C++ function call
ctrl.SetRows(10).
The interesting part of this equationthe implementation of the property Get/Set functionsis in the automatically
generated gridctrl.cpp file.
The following excerpt from the gridctrl.cpp file illustrates the implementation of some of the aforementioned property
Get/Set methods and general method calls:
// CGridCtrl
...
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl properties
...
short CGridCtrl::GetRows()
short result;
return result;
short CGridCtrl::GetCols()
short result;
return result;
...
/////////////////////////////////////////////////////////////////////////////
// CGridCtrl operations
...
long result;
VTS_I2;
Index);
return result;
VTS_I2 VTS_I4;
Index, nNewValue);
long result;
VTS_I2;
Index);
return result;
VTS_I2 VTS_I4;
Index, nNewValue);
...
The manifest constants in the parms[] arrays are expanded to strings during precompilation. The output from the precompiler
for CGridCtrl::GetRowHeight follows:
long result;
"\x02";
Index);
return result;
The precompiled output is interesting because it makes the definition of the parms[] array somewhat more clear. It's also
interesting because it shows how the mapping from C++ function name to the DISPID required by IDispatch::Invoke occurs;
the ClassWizard fills the first parameter to InvokeHelper with the DISPID, in this case the constant 0x1f, of the
corresponding GetRowHeight IDispatch-exposed method.
From the definition of the Get/SetRowHeight functions, it's clear that MFC is using the InvokeHelper function to somehow
translate the C++ function calls to IDispatch::Invoke() calls.
The CGridCtrl's reference to the InvokeHelper() function is a reference to the CWnd::InvokeHelper() function. In turn,
CWnd::InvokeHelper() is little more than a wrapper for the COleControlSite::InvokeHelper() function, which is pretty trivial;
it sets up a variable argument list and forwards the function call to the COleControlSite::InvokeHelperV() function.
Unsurprisingly, the COleControlSite::InvokeHelperV() call still isn't the end of the road; MFC aficionados will attest to the
fact that the path of execution through MFC code can be very complex. COleControlSite::InvokeHelperV() ensures that an
IDispatch interface pointer has been retrieved for the control object, wraps the pointer using an instance of
COleDispatchDriver, and then, finally, calls COleDispatchDriver::InvokeHelperV() to actually make the IDispatch call.
The implementation of the COleDispatchDriver::InvokeHelperV() is very interesting. Each element of the variable argument
list, prepared by InvokeHelper(), is converted to a variant and inserted in an instance of the DISPPARAMS structure, which
is expected by the standard IDispatch::Invoke() method. During the iteration through the variable argument list, the
aforementioned parms[] array is used to set the correct type of the variant and to calculate the correct amount to increment
the list pointer using the va_arg() macro.
After the parameter conversion is complete, IDispatch::Invoke() is called using the IDispatch control interface pointer
maintained by the COleDispatchDriver class. When Invoke() returns, MFC deallocates temporary memory used for the
DISPARAMS structure, checks the return code for the Invoke call, and, if an error occurred during the call, throws a
COleDispatchException containing all of the available information from the OLE exception structure.
It should be obvious at this point that the amount of work that MFC shields the casual control container from is just amazing!
If MFC weren't used to implement the container, each property call would need to be implemented using the standard
IDispatch mechanism, which is tedious at best from C++.
Speaking from the perspective of the Microsoft Foundation Classes, control event handling is more or less the opposite of
using a control's properties and methods. To use a control's properties and methods, MFC does the work to translate a C++
function call into a call to IDispatch::Invoke(). When handling events, MFC has to take a IDispatch::Invoke() call from a
control connected to a container's IDispatch event sink, and convert it into a call to a container's C++ member function.
Event handlers are declared within a CCmdTarget-derived class using the DECLARE_EVENTSINK_MAP() macro.
DECLARE_EVENTSINK_MAP() establishes the existence of a table that contains information that MFC requires to
dispatch events. It must be supplemented by BEGIN_EVENTSINK_MAP(), ON_EVENT(), and
END_EVENTSINK_MAP() macros in the class implementation file.
Earlier, you saw an example where I added the code necessary to instantiate an OLE control in a non-resource-based MFC
view class. After adding code to create the control, I added event handling to handle the SimpleControl's
OnSimpleNameChange event. Recall the following code snippet:
BEGIN_EVENTSINK_MAP(CSDIContainerView, CView)
ON_EVENT(CSDIContainerView, ID_SIMPLECTRL, 1, \
END_EVENTSINK_MAP()
ON_EVENT() is the macro that establishes an entry in a table of event handlers. When dispatching events, MFC will scan
this table to find a function that matches an incoming event. The definition of ON_EVENT(), from AfxDisp.h, follows:
It contains all of the information that is necessary for MFC to map an IDispatch::Invoke() call to a C++ class function call.
As with the aforementioned InvokeHelper() function, used for property set/get calls, the event parameters are encoded as a
string within ON_EVENT() macro use.
When MFC receives an event from a contained control, MFC attempts to find an entry in the aforementioned event sink map.
If MFC finds an entry that matches the event dispatch ID, it checks the parameters in the string parameter signature recorded
with the entry. After doing so, it performs an intricate conversion of a DISPPARAM array from an array to arguments on the
stack, suitable to be passed to a C++ member function.
The MFC "Event Forwarding" mechanism is one of the most useful elements of the class library; like the MFC property
implementation, it shelters the container implementer from the details of managing parameters from IDispatch by converting
calls to standard C++ function invocations.
Microsoft's ActiveX internet strategy plays heavily on existing technology such as COM and OLE or ActiveX controls. As
you learned from the previous chapter, OLE controls require a large number of interfaces to be implemented. Lots of
implementation generally means large controls, and large controls are a problem when users have low bandwidth modem
connections. Part of the goal of ActiveX is to simplify the requirements for controls; as the requirements change, it's quite
conceivable that the requirements for control containers will also change.
I have no doubt that Microsoft will continually update the Microsoft Foundation Classes to reflect the changing standards for
OLE or ActiveX controls. When implementing a control container in the future, it's a safe bet to say that you will probably
want to use the Microsoft Foundation Classes, unless you have specific requirements that aren't met by MFC.
Summary
This chapter has presented some of the issues involved with the creation of OLE control containers. OLE controls use
literally all of the various OLE technologies; consequently, they are very complex to implement. As you can tell from the
number of required interfaces for OLE control containers, it is also very complex to implement a control container.
The Microsoft Foundation Classes radically decrease the amount of code necessary to generate a control container. As a
matter of fact, implementing a dialog-resource-based control container is trivial! It's basically a point-and-click affair; as the
developer, you have to worry about the logic, not the infrastructure, when you use MFC for control containers.
Chapter 7
Microsoft Internet Explorer 3.0 and Its Scripting Object Model
by Weiying Chen
IE 3.0 Components
HTML Viewer
Hyperlink
History
Navigator
Document
Form
Link
Anchor
InternetExplorer Object
Summary
Chapter 7
by Weiying Chen
Internet Explorer(IE) 3.0 has an innovative architecture. Its major component (WebBrowser control) is an
ActiveX control, which can be used in any container application for Internet browsing. In addition, the Internet
Explorer application is an automation server, which can be used from within any container application to create
an instance of Internet Explorer 3.0. IE 3.0 provides a scripting object model accessed through scripting
languages such as VBScript and JavaScript. IE 3.0 also provides an ActiveX scripting interface to enable third-
party vendors to write their own scripting engines to work inside IE 3.0.
This chapter discusses the IE 3.0 components, ActiveX scripting, the scripting object model, and the ability to
add Internet browsing to any application.
IE 3.0 Components
The first major component in IE 3.0 (iexplore.exe) is a shdocvw.dll. This DLL is also called the Microsoft
WebBrowser control. The WebBrowser control uses the HTML viewer and Hyperlink object.
The WebBrowser control is an ActiveX control, which can be used inside any control container application, such
as Visual Basic, Access, or Visual C++.
This control is a document object container. It provides a single frame in which the user can view and edit all
types of ActiveX document objects. It is also a Scripting host, hosting the Scripting engine, such as VBScript and
JavaScript.
The frame, the second major component, is developed specifically to house the WebBrowser control. This is the
executable that users perceive as the standalone IE 3.0 product.
HTML Viewer
HTML viewer an in-proc server. It is a Document object and can be used in any DocObject container application,
such as IE 3.0.
Hyperlink
A hyperlink acts as a link to an object at another location(target). The location can be within the application itself
or in a different application. The user can click on the link and navigate to an object at another location. A
hyperlink is made up of target's location which is identified by a moniker, a displayable name for the target, a
string for the location within target, and a string containing additional parameters.
Microsoft defines the OLE hyperlink interface to abstract the hyperlink features. The WebBrowser control
implements this interface to support the hyperlink to any document. For more information on hyperlink, refer to
Chapter 21, "Hyperlink Navigation."
IE 3.0 is a Scripting host, which creates a Scripting engine and calls on the Scripting engine to run the scripts.
The scripts can be written by the scripting language such as VBScript or JavaScript. Some popular examples of
Scripting engine are VBScript and JavaScript. Figure 7.2 illustrates the relationship between IE 3.0 and
VBScript, and the scrollbar highlighted in the figure is placed on the web page using the <SCRIPT> tag that
follows:
<script language="vbscript">
dim i,j
for i=1 to 3
document.write("<TR>")
for j=1 to 2
document.write("<TD>")
document.writeln("CLASSID=")
The page in Figure 7.2 has six ActiveX controls, displayed as a table in the HTML page. The layout is done by
VBScript using the write, writeln method exposed by the document object in the IE 3.0 scripting object model.
The Scripting host must invoke the following methods to interact properly with any Scripting engine. The
following pseudocode, which includes a little self-explanation, illustrates the fundamental steps necessary on the
Scripting host side.
CoCreateInstance(CLSID_VBScript,, . . .)
//Load the script to feed the script engine or create a null script
//import each top-level named entity such as forms and pages into
IActiveScript::AddNamedItem()
IActiveScript::SetScriptState(SCRIPTSTATE_CONNECTED)
A Scripting engine must implement a few fundamental steps to communicate with the Scripting host. These steps
are demonstrated in the following pseudocode.
IActiveScriptSite::GetItemInfo
IConnectionPoint
IDispatch.Invoke
Microsoft defines an interface that allows a Scripting engine to be used in the Scripting host. This is known as the
ActiveX Scripting interface. Besides the interface required for the Scripting engine, a particular registry key
called OLEScript without any values must be present as an immediate subkey for the string representation of the
Scripting engine's CLSID.
VBScript in Figure 7.3 is the ProgID for the VBScript engine. The ProgID is used to register the human-readable
string associated with the COM object. VBScript engine is a COM object which implements IUnknown interface.
The value associated with the ProgID is used in to display the name of the COM object.
There are two immediate subkeys under VBScript ProgID, one is the OLEScript without any values. This subkey
is required for any Scripting engine. The other subkey is the CLSID, its value contains the string representation
of the Scripting engine's CLSID. The string representation of the CLSID is denoted as {CLSID}.
Figure 7.4 illustrates the registry entry associated with the VBScript engine's {CLSID}.
Figure 7.4 is the registry information stored under {CLSID}. There are two subkeys under "Implemented
Categories" root key.
{F0B7A1A1-9847-11CF-8F20-00805F2CD064}
{F0B7A1A2-9847-11CF-8F20-00805F2CD064}
indicates that the VBScript engine is an Active Scripting Engine with Parsing.
The subkeys under "Implemented Categories" root key are from Component Category. Component Category is a
new way to categorize COM objects.
IE 3.0 Scripting Object Model defines a set of objects which is accessible through any scripting languages such
as VBScript and JavaScript. The IE 3.0 object model is compatible with the object model used in JavaScript in
Netscape Navigator. ActiveX Scripting defines the interface for the scripting engine and scripting host, whereas
the Scripting Object model defines a set of objects accessible by any scripting language.
In this scripting object model shown in Figure 7.5, window is at the top of the object model. It consists of
document, history, and other properties, methods, and events. The document consists of form, link, anchor, write,
and so forth. The form consists of action, submit, and elements.
The elements within the form object refer to the HTML intrinsic controls or objects inserted in HTML through
the <object> tag. The HTML intrinsic control is built in the Web browser and placed in the <input> tag. Figure
7.6 illustrates the elements, its methods, properties, and events.
The following section uses VBScript and JavaScript to demonstrate how to use some of the objects, its events,
properties, and methods.
Window
The window object refers to the Internet Explorer window. The properties and methods can be invoked by the
script directly. The following example demonstrates a perfectly legal way of calling alert now() without
referencing the window object. Normally, you will use window.alert now().
<script language="vbscript">
sub window_onload
alert now()
end sub
</script>
Tables 7.1 through 7.3 list the definitions of the window object's properties, events, and methods.
Property Meaning get or set name Name of the current window get parent Window object of the window's parent
get self Window object get top Window object of the topmost window get location Window's location object get
defaultStatus Default status text in the browser's status bar get and set status Status of the browser's status bar get
and set frames The array of frames of the current window get history History object of the current window get
navigator Navigator object of the current window get document Document object of the current window get
Event Meaning Parameter Usage OnLoad Fires when the page is loaded none OnLoad="foo" OnUnLoad Fires
when the page is unloaded none OnUnLoad="foo"
The following example demonstrates how to change the browser's status by changing the window's status
property.
<html>
<form name="statusform">
</form>
<script language="vbscript">
sub btnChange_onClick
status = document.statusForm.txtStatus.value
end sub
</script>
</html>
As far as window's onload event, the initialization code should be placed in this event. The cleanup code on
exiting the window should be placed in the OnUnLoad event.
History
The History object exposes the properties and methods on the current history list. Tables 7.4 and 7.5 enumerate
its properties and methods.
Property Meaning get or set length Length of the history list get
Method Meaning Return Value Usage back Jumps back n steps in the history list none back 3 forward Jumps
forward n steps in the history list none forward 3 go Goes to the nth item in the history list none go 3
Navigator
The Navigator object returns the browser's information. Table 7.6 lists all the properties exposed by the
Navigator object. These properties can be accessed in the scripting language by prefixing navigator in front of
these properties.
Property Meaning get or set appCodeName Browser's code name get appName Browser's name get appVersion
Browser's version get userAgent Browser's user agent get
There are no methods exposed, and events fired by the navigator objects, s
Document
The Document object refers to the HTML document in the browser. It can be called by prefixing document in
front of the properties and methods in the script. Tables 7.7 and 7.8 enumerate the exposed properties and
methods.
Property Meaning get or set linkColor Current color of the link get and set alinkColor Current color of the active
link get and set vlinkColor Current color of the visited link get and set bgColor Current color of the background
get and set fgColor Current color of the foreground get and set location Location object get lastModified Date
which the document was last modified get title Document title get cookie Cookie for the current document get
and set referrer URL of the referring document get anchors Array of anchors in a document get links Array of
links in a document get forms Array of forms in a document get
Among these document properties, cookie is very useful to pass information between HTML pages. There is an
example at http://www.microsoft.com/vbscript/us/vbssamp/cookies/extcookie.htm showing you how to use
cookies.
The following example uses referrer to tell the user where they came from (the original page).
<html>
<body onLoad="FindWhereFrom()">
<script language="Javascript">
function FindWhereFrom()
if ( foo="http://www.microsoft.com/")
</script>
</body>
</html>
The methods write and writeln are useful for generating dynamic Web pages. The following example
dynamically constructs an HTML table with an array of controls by using write and writeln methods. Figure 7.2
shows this HTML page.
<html>
<table>
dim i, j
for i=1 to 3
document.write("<TR>")
for j=1 to 2
document.write("<TD>")
document.writeln(" activex.microsoft.com/controls/mspert10.cab""")
document.writeln(" CLASSID=")
document.writeln("""clsid:DFD181E0-5E2F-11CE-A449-00AA004A803D "">")
document.writeln("</OBJECT>")
document.write("<TD>")
next
document.write("</TR>")
next
</script>
</table>
</html>
The location object represents the current URL. The properties exposed by the location object can be accessed
within the scripting language by prefixing location in front of the properties. Modifying the properties of this
object will force the browser to navigate to the newly constructed URL.
Property Meaning get or set href Location's complete URL get and set protocol URL's protocol portion get and
set host URL's host and port portion get and set hostname URL's hostname portion get and set port URL's port
portion get and set pathname URL's pathname portion get and set search URL's search portion get and set hash
URL's hash portion get and set
Form
The form object refers to a form within the HTML document. The document object keeps track of forms as an
array or by name. The form can be accessed either by index or by name. For instance, for the following page:
<form name="test">
</form>
<form name="test1">
</form>
form "test" is the first form in the document. The following syntax can be used to refer to the "test" form via
giving index.
document.form[0]
document.test
Tables 7.10, 7.11, and 7.12 enumerate the exposed properties, methods and events of the form object.
Property Meaning get or set action Address of the form's action get and set encoding Encoding for the form get
and set method How the form data is sent to the server set target Name of the target window set elements Array
of elements in the form get
Method Meaning Return Value Usage submit Submit the form none form.submit
Event Meaning Parameter Usage onSubmit Fires when the form is submitted none form.OnSubmit=string
The submit method can be used to validate the client side information before sending to the Web server. For
instance, the following example in Listing 7.1 demonstrates how to validate the user's input before sending the
information to the Web server.
</form>
<script language="vbscript">
sub btnSend_onClick
dim value
value = document.validform.txtNumber.value
if IsNumeric(value) then
else
document.validform.submit
end if
else
end if
end sub
</script>
In Listing 7.1, document.validform is used across the html page to refer to the same form. The following code
will simply the process.
dim theForm
After the above definition, theForm can be used to directly refer to the form in the HTML page.
In Listing 7.1, the information will be validated first when the "submit" button is clicked. If the information
provided is not in the expected range, less than 1 or greater than 10, message " please enter a number between 1
and 10" will be displayed. If there is not any information provided, message "please enter a numeric number" will
be displayed. This greatly improve the performance, and avoid sending information to the server side to do the
validation , and sending back to the client etc.
Link
A link object is constructed for every link in the HTML document. It can be accessed in the scripting language
through the indexed array by prefixing the document object reference. For instance, the following example will
set the Link to the first link on the page:
theLink = document.link(0).href
Tables 7.13 and 7.14 show the properties exposed and events fired by the link object. There are no methods
exposed by the link object.
Property Meaning get or set href Link's complete URL get protocol URL's protocol portion get host URL's host
and port portion get hostname URL's hostname portion get port URL's port portion get pathname URL's
pathname portion get search URL's search portion get hash URL's hash portion get target URL's target portion
get
Event Meaning Usage OnMouseMove Fires when the mouse move over a link OnMouseMove(shift, button, x, y)
OnClick Fires when the mouse is clicked on a link OnClick OnMouseOver Fires when the mouse moves over a
link OnMouseMove
Anchor
The anchor object is constructed for every anchor tag in the HTML document. It is accessed through the indexed
array as the link object.
Table 7.15 shows the property exposed by the anchor object. There are not methods exposed and events fired by
the anchor object.
Property Meaning get or set name Name of the anchor get and set
For more information on the Internet Explorer scripting object model, please refer to
http://www.microsoft.com/vbscript.
The major component of IE 3.0 is the WebBrowser control. The reason that IE 3.0 has an innovative architecture
is that the WebBrowser control can be used in any container application to provide the Internet browsing
capability. WebBrowser control provides the functionality such as data downloading, hyperlinking and URL
navigation, history information, and parsing and displaying HTML-encoded documents.
The following example illustrates how seamlessly the WebBrowser control integrates with container applications.
Here, the language is Visual Basic 4.0 Enterprise Edition. The development environment is NT 4.0 and IE 3.0
release version is used.
The code for this project, shown in Listing 7.2, is fairly self-explanatory. The whole VB project for this sample is
contained on the CD, called lst91.vbp.
WebBrowser1.Navigate txtAddress
End Sub
WebBrowser1.GoBack
End Sub
WebBrowser1.GoForward
End Sub
WebBrowser1.GoHome
End Sub
WebBrowser1.Refresh
End Sub
'dialog box
WebBrowser1.GoSearch
End Sub
WebBrowser1.Stop
End Sub
Debug.Print "beforeNavigation"
End Sub
Debug.Print "CommandStateChange"
End Sub
Debug.Print "DownloadBegin"
End Sub
Debug.Print "DownloadComplete"
End Sub
Debug.Print "FrameBeforeNavigate"
End Sub
Debug.Print "FrameNavigateComplete"
End Sub
Debug.Print "FrameNewWindow"
End Sub
Debug.Print "NavigateComplete"
End Sub
Debug.Print "NewWindow"
End Sub
Debug.Print "ProgressChange"
End Sub
Debug.Print "PropertyChange"
End Sub
Debug.Print "Quit"
End Sub
Debug.Print "StatusTextChange"
End Sub
Debug.Print "TitleChange"
End Sub
In order to create any application using this control, make sure to select the Microsoft Internet control checkbox
in the Visual Basic 4.0 custom control list. Tables 7.16, 7.17, and 7.18 enumerate the properties, methods
exposed, and events fired by the WebBrowser control.
Property Meaning get or set Application Application that uses the Web browser control get Busy Whether
downloading or navigation is going on get Container Control container get Document Active document get
Height Browser control height get and set Width Browser control width get and set Left Distance between the
internal left edge get and set of the control and the left edge of the container Top Distance between the internal
top edge get and set of the control and the top edge of the container Type Type name of the contained document
get LocationName Title of the page or the UNC the browser get
is displaying LocationURL URL of the resource that the browser get
is displaying Parent Parent of the browser control get TopLevelContainer Whether the given object is a top-level
container get
Method Meaning Usage GoBack Go one item back in the history list goback GoForward Go one item forward in
the history list goforward GoHome Go to the current home or start page gohome GoSearch Go to the current
search page (this search page gosearch
is specified by the Internet control panel and
the IE option dialog box) Navigate Navigate to URL or a full path navigate URL Refresh Reloads the page that is
displaying refresh Refresh2 Reloads the page that is displaying refresh [0...3]
with specified level: 0: Normal refresh 1: Refresh if the page has expired 3: Full refresh Note: 2 is not defined.
Stop Cancel any pending navigation or download stop
Event Meaning BeforeNavigate Fires before navigating to a different URL CommandStateChange Fires when the
enabled state of a command changes DownloadBegin Fires when a download starts DownloadComplete Fires
when a download successfully ends FrameBeforeNavigate Fires when navigating to a different URL
FrameNavigateComplete Fires when a navigation successfully ends FrameNewWindow Fires when a new
window is to be created NavigateComplete Fires when successfully navigated to a new location NewWindow
Fires when a new window is to be created ProgressChange Fires when the progress of download is updated
StatusTextChange Fires when the status bar text is changed TitleChange Fires when the page title is changed
For more detailed information on these properties, methods, and events exposed by this control, please refer to
http://www.microsoft.com/intdev/sdk/docs/iexplore/.
InternetExplorer Object
In addition to using the Web browser control inside a container application, IE 3.0 is also built as an OLE
automation server. The ProgID for this automation server is InternetExplorer.Application. By calling as follows,
an instance of Internet Explorer 3.0 will be created.
createobject("InternetExplorer.Application")
The methods and properties supported by the InternetExplorer object are a superset of the Web browser control.
In other words, the InternetExplorer object exposes all the properties, methods, and events the WebBrowser
control has. In addition, it supports more.
Summary
The Internet Explorer 3.0 provides an innovative architecture, which allows developers to add Internet browsing
to any application. The ActiveX scripting provides the interface so that scripting engines can be used in the IE
3.0. Furthermore, the IE 3.0 scripting object model provides support for programmatically controlling navigation,
Chapter 8
VBScript
by Jon Czernel
Language Fundamentals
The VBScript Data Type: Variant
Variable Declarations
Variable Assignments
Arrays in VBScript
Operators
Functions
Conditional Execution
Basic Input/Output
Summary
Chapter 8
VBScript
by Jon Czernel
Microsoft has for years pushed BASIC as the de facto, entry-level development standard in the personal computer
arena. In fact, the origins of the Microsoft empire can be traced back to its early adoption and development of BASIC
for the first personal computers available. Many developers today, in fact, will say that one of the several Microsoft
BASIC products were responsible for their entry into the development arena, either by hobby, profession, or both.
VBScript continues Microsoft's support of a language that has evolved into a fairly powerful development tool.
Microsoft has announced that it will allow third parties to use VBScript as the scripting engine in their applications, and
it has openly encouraged third parties to port VBScript to "alternative" (non-Windows) platforms through purchase of
their VBScript source code.
This chapter examines the fundamental language constructs of VBScript, a strict subset of Visual Basic 4.0 (VB) and
Visual Basic for Applications (VBA). In particular, the later sections of this chapter concentrate on the use of VBScript
in HTML documents in conjunction with ActiveX Controls. For the sake of brevity, the authors assume that the reader
has some development experience in at least one structured development language, such as VB, C/C++, or Object
Pascal. Instead of concentrating on syntax, the chapter presents real-world VBScript examples wherever appropriate.
The authors also assume that the reader has experience in basic HTML development and is familiar with the concept of
ActiveX (OLE/OCX) controls.
It begins with language fundamentals, discussing the core constructs of the language, without the overhead of
HTML and ActiveX.
For those with VB/VBA development experience, the section titled "Comparing VBScript to Visual Basic," is
for you. This is a "must-read" section for experienced VB developers.
The remaining sections detail the use of VBScript in an HTML document, including the use of VBScript to
"glue" together various ActiveX controls in the creation of an interactive Web page.
Note
The complete richness of VBScript is impossible to reveal in a single chapter. For more
information on VBScript, refer to the Microsoft VBScript Web site at:
http://www.microsoft.com/vbscript.
May your adventures in VBScript be both challenging and fulfilling! The first section begins with a discussion of
VBScript fundamentals.
Language Fundamentals
This section discusses the fundamental architecture of VBScript. This section should be used as both a quick language
reference and brief language tutorial. Like most language references, the section begins with the most basic elements,
and then works its way up to larger, more encompassing constructs.
Unlike most other languages, VBScript allows only one data type: variant. A variant is a variable type that can hold
any type of fundamental data type, including integers, floating points, characters, strings, and date/time values. Variant
data types may also represent instances of objects.
With variants, you don't have to worry about ensuring that your variable is adequately prepared to handle unexpected
data. However, although the variable itself might be able to contain any type of data, your routines will often be
required to check for the type of data that is stored in a variable to ensure proper script execution.
VBScript assigns a subtype to variables dynamically during program execution. This subtype classification flags the
variable as containing a particular type of value, such as an integer or a string. VBScript also includes the function
VarType, and functions such as IsArray(), IsDate() and IsEmpty(), that enable you to determine, at runtime, the
currently assigned subtype of a variable.
Table 8.1 enumerates the variant subtypes available, and the value returned by the VarType function when a variable of
the given subtype is passed as a parameter. Alternative methods of determining a variable subtype, such as IsEmpty()
or IsDate(), are also mentioned.
Note that a variable subtype is dynamic during code execution, and it may change as code is executing, depending on
the contents of the variable. For this reason, VBScript also includes mechanisms that enable you to convert (cast) a
variant variable from one subtype to another. Table 8.2 lists some of the most important conversion functions built into
VBScript.
Function: Returns Asc() The ASCII code for the first character in the given string. CBool() A Boolean. CByte() A
Byte. CDate() A Date/Time. CDbl() A Double Precision number. Chr() The character represented by the given ASCII
code. CInt() An integer. CLng() A long integer. CSng() A single precision floating-point value. Hex() The hexadecimal
representation of the given number, in string format.
Oct() The octal representation of a given number, in string format.
Warning
Remember that although variants provide a great deal of flexibility, it is still possible to raise type mismatch errors at
runtime by casting a variable that contains an invalid value for the specified cast operation. For example, the function
CDate will produce a type mismatch error if T contains anything besides a valid date/time value, such as
"SomeGuyNamedJoe". Before forcing a variable cast, ensure that the variable you'll be casting contains a valid
argument by using one of the Is.. functions, such as IsDate().
1. 1. A variable name must begin with an alphabetic character. The remainder of the name may contain any
alphanumeric characters, including underscores ("_").
2. 2. The length of a variable cannot exceed 255 characters.
3. 3. Periods may not be embedded in a variable name.
4. 4. A variable name must be unique within the scope in which it is defined.
Variable Declarations
Variables in VBScript are declared explicitly using the Dim statement. Variables that are not explicitly declared are
implicitly declared when they are first encountered in any statement.
Tip
It is best to explicitly declare your variables before they are used for the first time, as
opposed to allowing VBScript to implicitly declare them when they are first used. Explicit
declaration makes your code easier to debug and maintain. To force explicit declaration of
variables, include the statement Option Explicit at the beginning of each script block.
The following block of code illustrates the declaration of several variables in VBScript:
...
Dim NewName
...
The scope of variables within an HTML document is discussed later in this chapter, in the section "VBScript Scoping
Rules in HTML."
Tip
During this discussion of variables, no mention has been made so far of constants. This is
for a good reason; there is no special provision for constants in VBScript. To implement
constants, simply use standard variables. It is recommended, however, that you adhere to a
specific naming convention when using a variable as a constant, such as ensuring that the
entire variable name is capitalized, so that constants can be easily distinguished from
regular variables.
Variable Assignments
To assign a value to a variable, a single equal sign is used, in the format <variable> = <value>.
Interestingly, unlike most other languages, in VBScript the assignment operator is the same as the equivalency
operator. The following block of script illustrates several different data types being assigned to variables:
...
ReturnDate = "9/1/98"
...
Arrays in VBScript
This discussion of variables in VBScript ends with the creation and assignment of arrays in VBScript. Arrays are
declared using either the Dim or ReDim statement, as shown in the following block of code:
...
...
...
...
The first Dim statement declares two arrays, Lookup and AnotherLookup. The size of these arrays cannot be
changed.
The array AnotherLookup is given three dimensions. Arrays may be given a maximum of 60 dimensions.
The second Dim statement declares a dynamic array called Reference. Subsequent ReDim statements may be
used to resize the array.
The last statement resizes the array Reference to 40 elements. The Preserve keyword forces the original contents
of the array, elements 0 to 29, to be retained.
Note
In all arrays, each dimension begins with element zero (0). In the example Dim Test(5), six
indices are actually created, from Test(0) to Test(5). As a further note, the Microsoft online
documentation for VBScript, as of this writing, incorrectly states that the upper bound of a
dimensioned array is equal to the specified array size minus one.
Tip
The variant subtype of an array is the same for all elements in an array. To determine the
subtype of an array, you can use the VarType() function, and subtract 8192. This
seemingly arbitrary subtraction will yield the "base" subtype number, as described earlier
in Table 8.1.
Operators
VBScript provides all of the standard operators that you would expect in a development environment. Tables 8.3
through 8.5 list all operators used in VBScript, listed in order of precedence.
Operator Name Example = Equality If A = B then print "Equal" <> Inequality If A <> B then print "Not Equal" < Less
Than If A < B then print "Less Than" > Greater Than If A > B then print "Greater Than" <= Less Than Or Equal To If
A <= B then print "LTOE" >= Greater Than Or Equal To If A >= B then print "GTOE" Is Object Equivalency If A is B
then print "Same"
Operator Name Not Logical negation And Logical conjunction Or Logical disjunction XOr Logical exclusion Eqv
Logical equivalence Imp Logical implication
Procedures in VBScript, like those found in other popular programming languages, fall into two categories:
subprograms and functions.
Procedure names adhere to the same rules as variable names. That is, they must begin with an alphabetic character,
cannot contain embedded periods, and cannot exceed 255 characters in length. Procedures must be defined before they
are referenced.
Note
Subprograms
Subprograms are declared using the Sub...End Sub statements. Immediately following the subprogram name are zero or
more parameters, as shown in the following example:
...
Sub PrintStatus
...
End Sub
...
'returns ReturnedVal.
End Sub
...
To call a subprogram, simply use the name of the subprogram in your code, such as:
...
...
Note in the preceding block of code that when the Call statement is used, parentheses around the parameter list are
required.
Functions
Functions are declared using the Function..End Function statements. Functions differ from subprograms in that they
return a variant value. The following block of code illustrates the declaration of a function:
...
TAX = 0.4
Next
End Function
...
Note that the most critical statement in the preceding block of code is the assignment of the function name,
CalcAmtDue, to a value before termination of the function.
To use a function in VBScript, simply use the function name as an expression in any statement, such as:
or:
Tip
To exit a function prematurely, before the End Function is encountered, use the Exit
Function statement. Similarly, to terminate a subprogram before the End Sub, use the Exit
Sub statement.
Conditional Execution
Like other languages, VBScript includes all of the conditional execution statements that are required in a structured
development environment.
If...Then...Else
The first conditional execution statement that this section briefly discusses is If...Then...Else. The general syntax for
this statement follows:
If expr Then
[Else
End if
...
Else
CompleteTrans = False
End if
Else
CompleteTrans = True
End if
...
Select Case
A second conditional execution statement, useful in many situations when If...Then...Else blocks would prove to be
cumbersome and difficult to read, is the Select Case statement, similar to the switch statement in C. A brief grammar
for this statement follows:
Case expr1
[Case expr2
[Case exprn
[Case Else
End Select
...
Case 0
MsgBox "Delete."
Case 1
MsgBox "Add."
Case 2
MsgBox "Edit."
Case Else
End Select
...
VBScript provides three essential looping mechanisms, two of which will be discussed in this section:
A third conditional looping statement available in VBScript, While...Wend, will not be discussed in this section,
because Do...Loop provides all of the capabilities of the While...Wend statement, and more.
For...Next
To repeat a body of statements a fixed number of times, use the For...Next statement. The general syntax for this
statement is:
Next
The following code snippet illustrates nested For...Next statements initializing the values of a two-dimensional array:
...
For i = 0 to 9
For j = 0 to 19
Array(i, j) = i * j
Next
Next
...
Tip
To exit out of a For...Next statement prematurely, execute the Exit For statement.
Do...Loop
The Do...Loop statement allows a block of VBScript to be executed until a stated condition evaluates to True. The
conditional evaluation may occur before the VBScript block is executed in a pretest loop, or after it is executed at least
once in the form of a post-test loop.
The pretest form of Do...Loop follows. Note that in this form it is possible that the script sandwiched between the Do
and Loop statements may never be executed, depending on the evaluation of the initial expression:
...
Do [{While|Until} expr]
Loop
...
...
NewVal = 0
Loop
...
The post-test form of the Do...Loop statement, which allows VBScript within the structure to be executed at least once,
is shown following:
...
Do
...
...
NewVal = 0
Do
...
Note
It is possible in VBScript to create an endless loop using the Do...Loop statement. Before
distributing your VBScript-enabled Web pages to the world, test your code carefully to
ensure that you won't lock up the browsers of surfers in other countries, potentially causing
an international incident.
Basic Input/Output
VBScript provides two extremely useful mechanisms for both displaying dialog boxes and requesting input from the
user. These built-in functions are described in this chapter.
The MsgBox() function allows a standard dialog box to be displayed. If required, the button clicked to close the dialog
box may be determined. The syntax for MsgBox is
MsgBox(prompt[,buttons][,title][,helpfile,context_id])
prompt is the text that you would like to be displayed in the body of the dialog box.
buttons indicates the buttons, dialog box icon, and modality of the dialog box. See Table 8.6 for a list of values
to use with this parameter.
title is the text that is displayed in the caption bar.
helpfile is the name of the local help file that should be opened if the user presses F1.
context_id is the help context ID associated with the dialog box with respect to the specified help file.
Table 8.6 lists the options that are summed to produce the buttons parameter, defining the general characteristics of the
MsgBox() function.
Value Result Button SelectionDefault: Ok 0 Ok button is displayed. 1 Ok and Cancel buttons are displayed. 2 Abort,
Retry, and Cancel buttons are displayed. 3 Yes, No, and Cancel buttons are displayed. 4 Yes and No buttons are
displayed. 5 Retry and Cancel buttons are displayed. Default Button SelectionDefault: First 0 Assume the first button
to be the default button. 256 Assume the second button to be the default button. 512 Assume the third button to be the
default button. 768 Assume the fourth button to be the default button. Dialog Box IconDefault: None 16 Use the
Critical Message icon. 32 Use the Warning Query icon. 48 Use the Warning Message icon. 64 Use the Information
Message icon. ModalityDefault: Application Modal 0 Create an Application Modal dialog box. 4096 Create a System
Modal dialog box; suspend all applications until the dialog box is closed.
If you are required to determine the button pressed by the user, the returned value from MsgBox() is provided. To
determine the button selected, use Table 8.7.
Value: Selected Button: 1 Ok 2 Cancel (or ESCape) 3 Abort 4 Retry 5 Ignore 6 Yes 7 No
...
If ret = 7 Then
'No
Exit Sub
End If
...
Figure 8.1. Simple MsgBox() example that uses Yes/No Buttons and a Query icon.
The InputBox() function, like MsgBox(), is also a built-in routine that makes simple user interaction possible. Whereas
MsgBox() is used for simple button selection and user notification, InputBox() allows a text entry to be made by the
user. The syntax for InputBox() is shown following:
InputBox(prompt[,title][,default][,xpos][,ypos][,helpfile, context_id])
prompt is the text that you would like displayed in the body of the dialog box.
title is the text displayed in the caption bar.
default is a default text value.
xpos, ypos are the horizontal and vertical starting positions of the dialog box, respectively. These values are
always given in twips. By default, the dialog box will appear centered horizontally, and just above center
vertically.
helpfile is the name of the local help file that should be opened if the user presses F1.
context_id is the help context ID associated with the dialog box with respect to the specified help file.
The return value of InputBox() represents the value typed in by the user, or an empty string ("") if Cancel was selected.
...
End If
...
This example code produces the dialog box shown in Figure 8.2.
Several important functions built into VBScript that allow for string manipulation and date/time manipulation have not
been discussed in this chapter. Table 8.8 lists the most important string manipulation functions that will more than
likely be used in any VBScript code that you develop. Instead of providing the syntax for each function, an actual
example of how the function is used is shown.
Example Returns Explanation InStr("To All", "All") 4 Used to find the character position of the first occurrence of the
second string in the first string. Left("To Whom It", 2) "To" Returns the specified number of characters from the given
string, starting with the leftmost character. Right("To Whom It", 2) "It" Returns the specified number of characters
from the given string, starting with the rightmost character. Len("Jordan") 6 Returns the length of the given string.
LTrim(" Some ") "Some " Strips all leading spaces from the given string. RTrim(" Some ") " Some" Strips all trailing
spaces from the given string. UCase("Kerrie") "KERRIE" Capitalizes all characters in the given string. Mid("Some
Junk", 3, 2) "me" In this example, returns two characters starting with the third character.
For those developers already familiar with Visual Basic or Visual Basic for Applications (VBA), the use of VBScript
will prove to be extremely straightforward. VBScript is simply a subset of Visual Basic; its primary design goal was to
provide a "light" interpreted language that provides all of the fundamental features, but none of the icing, of Visual
Basic.
Some of the most significant differences between the two languages are described following:
In VBScript, constants (CONST) cannot be declared, nor are any intrinsic constants available, such as those
used in the parameter list of MsgBox() and InputBox().
All arrays declared in VBScript are always based at index 0. The statement Dim Test(5) actually produces six
indices, from Test(0) to Test(5). The Option Base directive is unavailable.
The statements Goto, Gosub, and Return are no longer provided. Consequently, labels are not allowed. This is
probably a desirable omission, as it forces more structured development.
All parameters passed to procedures are passed by value, not by reference. There is no way to alter this
behavior. This implies that variables passed to procedures cannot be changed in the procedure itself, unless they
are globally declared.
The Format function, used to format numeric, date/time, or text values, is not provided. For some, this exclusion
might present significant opportunities when presenting data to users in a formatted, consistent fashion.
[1b] In For...Next statements, the VBScript interpreter will trigger an "Expected end of statement" error if a
variable name is specified in the Next statement, such as Next i. The Next statement in VBScript should be used
by itself, without a variable specification.
All functions that provided access to external functions or files, such as Windows API calls or file input/output,
have been removed. This was necessary to eliminate the possibility of rogue VBScript applications.
Tip
For more information on the differences between the two languages, see
http://www.microsoft.com/vbscript/us/vbslang/vsgrpNonFeatures.htm.
In addition to language differences, the development environment (IDE) of VBScript is a throwback to the days of
using an ASCII editor to create and debug your code. The development process, when using VBScript in an HTML
document, usually consists of these steps:
Tools such as the Microsoft ActiveX Control Pad make this process much simpler, especially when integrating
ActiveX controls into your HTML document. However, even with this tool, for those who are accustomed to syntax
highlighting, automatic syntax checking, and an integrated debugger with stepping, breakpoints, and watch variable
facilities will be dissatisfied with the current VBScript development environment options.
Thus far this chapter's discussion has been based on VBScript itself, with little mention of the use of VBScript in an
HTML document. The remainder of this chapter emphasizes how VBScript is utilized in HTML.
VBScript is included in HTML using the <SCRIPT></SCRIPT> tag. The following example illustrates a skeleton
block used to embed VBScript in HTML:
...
<BODY>
<!--
-->
</SCRIPT>
</BODY>
...
The LANGUAGE attribute in the <SCRIPT></SCRIPT> block is assigned to "VBScript" using the
LANGUAGE attribute.
Within the SCRIPT tag, VBScript should be placed in an HTML comment block <!-- ... -->, allowing browsers
that do not understand the SCRIPT tag to correctly parse the remainder of the HTML document without
displaying your VBScript code.
It is recommended that all script be included in one contiguous <SCRIPT></SCRIPT> block embedded in either the
<HEAD></HEAD> section or the end of the <BODY></BODY> section of a given HTML document. Although
VBScript will not be affected by its placement, it is best, for readability and maintenance purposes, to group all script
together in one consistent location.
There are two levels of scope defined in VBScript that affect the visibility and lifetime of variables: script and
procedure.
The script level of scope involves all VBScript statements that are not included in any procedures (functions or
subprograms), but that are included in the current HTML document, in any <SCRIPT></SCRIPT> blocks. Any
variables declared at the script level can be seen by any script included at the procedure level. Variables are eliminated
from memory when the current script is terminated by loading a new HTML document.
The procedure level of scope includes all variables declared within a procedure. These variables are eliminated from
memory when the procedure in which they are declared is terminated by either an Exit or End statement.
Tip
The use of these scopes implies that there is no way to retain the value of variables
between several HTML documents. If you require this capability, use HTML Cookies.
It is important to remember that VBScript is interpreted "on the fly", as your browser that supports VBScript, such as
Microsoft Internet Explorer 3.0, parses through an incoming HTML document. Therefore, include VBScript that is
required to immediately modify the attributes of a document at load-time in your <HEAD></HEAD> section, outside
of any procedures. Including this code in the <HEAD></HEAD> section is simply a common convention, not a
necessity; it could be placed in the <BODY></BODY> sections, but this is not recommended.
Any declared procedures are interpreted and stored in the client's local address space, ready for later execution. Any
code at the script level (not embedded in procedures) is executed as encountered while an HTML document is parsed.
As in most other modern-day development environments, all non-script level procedures are executed based on the
event-driven model. That is, a procedure is "triggered" based on an action that the user or some other entity (the object
model, for example) triggers.
The next section begins to explain how objects and their actions are tied to VBScript procedures.
The Internet Explorer (IE 3.0) Object Model allows VBScript to be used to programmatically control both the current
HTML page and the Web browser itself. For example, VBScript and the IE Object Model can be used to:
Present a dialog box to the user using the alert method defined by the window object.
Specify and load a page pointed to by a specified URL using the open method defined by the window object.
Change the background color of the current document using the bgColor property of the document object.
This object model, along with the programmatic power of VBScript, brings a tremendous amount of flexibility to the
client side of any HTML document.
Tip
A significant portion of development in VBScript and HTML will require interaction with
the IE 3.0 object model. For more information on this technology, refer to Chapter 7,
"Microsoft Internet Explorer 3.0 and Its Scripting Object Model."
Through the IE 3.0 Object Model, some HTML elements, such as buttons and forms, may trigger the execution of
specified VBScript procedures. In this section, you begin to create a real-world HTML page that demonstrates the
initiation of a VBScript procedure based on an event.
The first example will enable the user to toggle the caption on an HTML button to read either "On" or "Off". Please
refer to Listing 8.1; a complete explanation follows.
<HTML>
<HEAD>
<H2>
</H2>
<!--
here!
-->
onClick="Button_Click" Language="VBScript">
</Form>
<SCRIPT LANGUAGE="VBScript">
<!--
'toggle state
Sub Button_Click
If Toggle = 0 then
Document.frmTest.Button.Value = "On"
Toggle = 1
else
Document.frmTest.Button.Value = "Off"
Toggle = 0
end if
End Sub
-->
</SCRIPT>
<TITLE>Test VBScript!</TITLE>
</HEAD>
</HTML>
A form on an HTML page is created, frmTest, which consists of a single button, referred to as Button.
The onClick attribute of the button points to the VBScript procedure that will handle the button's click event,
Button_Click. The language attribute specifies VBScript as the language used in the event handler.
The SCRIPT block in the HTML document contains the Button_Click event handler.
When the user clicks on the button, you simply modify the Value attribute to indicate the current toggle state,
either "On" or "Off". Note the use of the Document object, from the IE 3.0 Object Model.
This example illustrates, at a rudimentary level, how the IE 3.0 Object Model allows VBScript to be used as the event
handler for an HTML-defined button.
The purpose of this section was to illustrate the use of VBScript in an HTML document without the use of ActiveX
controls. However, as the thrust of this book revolves around ActiveX, this chapter will not elaborate on the use of
VBScript to control standard HTML elements; this topic falls outside of the scope of this chapter. The next section
continues the discussion of VBScript, where you will utilize ActiveX components in HTML.
As stated previously, VBScript can be used to programmatically define how an object on an HTML document must
react to a triggered event. In the previous section, you examined how VBScript might be integrated along with a
standard HTML object, a button in your example, to control what occurs when the user clicks on a button.
In this section, you will utilize readily available ActiveX components into an HTML document and react to various
component-related events using VBScript.
To add an ActiveX control to an HTML document is only slightly similar to adding a standard HTML element, such as
a button, to an HTML document.
ActiveX controls and their associated properties are embedded in an <OBJECT></OBJECT> tag. The specified
ActiveX control will appear in the HTML document wherever this tag occurs. The following code snippet illustrates
the definition of an ActiveX control, in this case an IE 3.0 Label, one of the controls that ship with Microsoft Internet
Explorer 3.0:
CLASSID="CLSID:99B42120-6EC7-11CF-A6C7-00AA00A47DD2">
</OBJECT>
The opening <OBJECT> tag specifies the ID of the object, which represents the name that the remainder of the
HTML page will use to refer to this object. The recommended width and height are also specified.
The CLASSID, or "CLSID", specifies the identification of the ActiveX control, as designated in your system
registry.
The remainder of the lines, before the closing </OBJECT> tag, specify override values for several parameters,
or properties, of the control.
The problems with adding ActiveX controls to HTML pages without the assistance of any other utility should be fairly
clear to those with experience with a "point and click" development environment such as Visual Basic. These problems
are summarized following:
How can you easily determine the CLASSID? For each ActiveX control, you need to know the full CLSID that
must be specified in the CLASSID attribute of the OBJECT tag.
How do you know what properties (parameters) apply to the object that you're using? ActiveX controls typically
have tens of properties, or parameters, that can be overridden in the OBJECT tag using the PARAM NAME
attribute. How can you easily determine what properties are available when using a text editor to create your
HTML page?
How can you determine what methods, or events, a control triggers? Remember, you're using a straight text
One possible solution that applies even with add-on development tools might be to browse through Web-based
documentation that describes properties and methods for each object. Microsoft's Web site contains excellent
descriptions of each of the ActiveX controls bundled in IE 3.0. However, this still does not approach the ease of
development found in environments such as Borland Delphi or Microsoft Visual Basic.
The solution to most, but not all, of these dilemmas is described in the next section.
A good friend of mine once said that, by nature, developers are lazy people. When a programmer finds a task difficult
to accomplish, he (or she) will either seek a tool that someone else has developed to help get the job done more quickly
or develop a tool themselves that shaves either milliseconds or days off a programming task.
This is a good thing. Microsoft, the makers of the ActiveX technologies that make programmers' lives so very
interesting, have also released two invaluable tools that dramatically assist in the process of using ActiveX controls in
HTML documents: The ActiveX Control Pad and the ActiveX Control Lister. Both of these tools can be found in the
ActiveX SDK.
Tip
If you do not have access to this SDK, visit the Microsoft ActiveX SDK Web site at
http://microsoft.com/intdev/sdk. Follow the links to the download area for more details.
As mentioned earlier, any time an ActiveX control is added to a Web page using the <OBJECT> tag, the class ID is
required. Normally the determination of this value would require the perusal of the Windows system registry.
The ActiveX Control Lister makes this process much simpler by presenting a dialog box, after scanning your system
registry, that contains all of the ActiveX controls installed on your system. Additionally, you can right-click on any of
the controls in this list box to bring up a pop-up window that will allow you to copy the essentials for the <OBJECT>
tag, including the CLSID, into the Windows clipboard for easy pasting into a text HTML file. What a lifesaver!
Figure 8.3 represents a typical ActiveX Control Lister display, along with the pop-up window that appears when the
right mouse button is clicked.
When Copy is selected from the pop-up menu, the minimal <OBJECT> tag requirements for the selected control are
pasted into the clipboard. For example, the following text was pasted into the clipboard when the Microsoft Forms 2.0
Image ActiveX control was selected:
<Object
Classid="clsid:4C599241-6926-101B-9992-00000B65C6F9"
Width=80
Height=30 >
</Object>
If you select Options from this pop-up menu, you can configure the contents and format of the information that is
copied into the clipboard, such as character case, values for the initial width and height attributes, and whether or not
the text should appear on one line or multiple lines.
This utility is a "must have" for any Web page developer who utilizes ActiveX controls.
The ActiveX Control Pad is described in detail in Chapter 11, "Using ActiveX Control Pad," so this section doesn't go
into any significant detail regarding this tool. However, you should understand the fundamental operation of this utility,
especially as it applies to VBScript.
At first glance, it may appear as though the ActiveX Control Pad is simply a text editor, with perhaps the ability to
automatically present a basic HTML framework in new HTML documents. However, it is much, much more than that.
In essence, the ActiveX Control Pad provides the developer with an HTML "Integrated Development Environment"
that is especially useful when integrating ActiveX controls, the Internet Explorer 3.0 Object Model, and VBScript or
JavaScript.
Here is a brief list of the most important features found in this product:
Multiple HTML documents may be loaded at the same time in its MDI framework.
Properties for ActiveX controls may be easily modified through the ActiveX Control Editor, a part of the
ActiveX Control Pad. This editor is similar to control editors found in environments such as Visual Basic and
Delphi, allowing properties for the chosen control to be easily viewed or modified.
Once a control is inserted, the Control Pad automatically handles the HTML dirty work, including complete
contents of the <OBJECT></OBJECT> block. If you need to modify the properties of an object, simply click on
a button that appears to the left of each opening <OBJECT> tag to redisplay the ActiveX Control Editor.
The Script Wizard, perhaps the most important product feature, allows you to view your HTML document in an
"object-oriented" fashion. All ActiveX objects and associated events in the current page appear in a neatly
organized dialog box. Either VBScript or JavaScript may be inserted to handle tasks as necessary.
Note
Another important attribute of this product, HTML Layouts, will not be discussed in this
section, but is discussed in Chapter 11. HTML Layouts give the Web page designer much
more control over how controls will be positioned on the screen.
Figure 8.4 illustrates a typical operation utilized in the ActiveX Control Pad: changing properties for an object, in this
case a label, using the Control Editor.
In some cases, a picture is definitely worth a thousand words. Compared to a straight text editor, the advantage of using
the ActiveX Control Pad to modify your HTML documents should be apparent.
Of course, simply inserting ActiveX controls into your Web documents and manipulating their properties is only one
quarter of the battle. The remaining three quarters revolve around controlling these controls, and allowing them to
interact with each other through VBScript or JavaScript.
The ActiveX Control Pad, through the Script Wizard, gives you an easy way to:
Modify the parameters of controls (objects) already added to your document, supplementing the use of the
Control Editor.
View all of the available objects and associated events associated with each ActiveX object that has been added
to the current HTML page.
View all procedures (VBScript or JavaScript) that have been defined in all HTML script blocks, as well as
available methods for all object blocks, including objects defined in the IE 3.0 Object Model.
Add event handlers for any one of the predefined events that are listed under each object that has been added to
the current HTML document.
Link events to either available methods that apply to the IE 3.0 Object Model, or custom made subprograms.
Figure 8.5 illustrates an event handler being designed for the Click event of the lblWelcome object, an ActiveX label
control. Note the other objects and events that are displayed in the upper left panel, and the possible actions listed in the
upper right panel.
Now that you have a rudimentary understanding of what you can accomplish with the ActiveX Control Pad, this
chapter on VBScript will wrap up by designing an interactive Web page that utilizes ActiveX Controls with underlying
logic created in VBScript.
In this section, you create a Web page that utilizes several ActiveX controls, tying together those controls and
controlling some elements of the IE 3.0 Object Model through VBScript. The interactive page that will be
demonstrated here is a typical user entry screen that might appear when a Web surfer is requesting information on one
or more products from a typical Web site.
For those who have developed interactive forms in standard HTML, you may ask why the example uses ActiveX
labels, edit boxes and other controls over elements that are built into the HTML standard. This is for two reasons. First,
the purpose of this book is to discuss ActiveX controls, not necessarily built-in HTML elements. Second, ActiveX
controls give the developer much more flexibility with respect to their attributes than built-in HTML elements.
Note
The CD-ROM included with this book contains the HTML source for each phase of
development included in this section, beginning with INTER.HTM, then following with
INTER2.HTM, and INTER3.HTM.
Note
The goal of this section is to illustrate the use of VBScript fundamentals described in the
first half of this chapter. Therefore, I will not discuss the use of the ActiveX Control Pad,
which was used to generate these pages.
The Framework
The first phase in designing any HTML page is to lay out the basic components of the page. You'll use VBScript and
the methods available in the IE 3.0 Object Model to create your basic framework, shown in Listing 8.2.
<HTML>
<HEAD>
<SCRIPT LANGUAGE="VBSCRIPT">
<!--
Dim TodayNum
window.document.bgColor = "#FFFFFF"
window.document.write("<P><H4>")
window.document.write("</H4></P>")
Case 1
NameOfDay = "Sunday"
Case 2
NameOfDay = "Monday"
Case 3
NameOfDay = "Tuesday"
Case 4
NameOfDay = "Wednesday"
Case 5
NameOfDay = "Thursday"
Case 6
NameOfDay = "Friday"
Case 7
NameOfDay = "Saturday"
End Select
End Function
-->
</SCRIPT>
</HEAD>
<BODY>
</BODY>
</HTML>
The explicitly declared variable TodayNum is a globally accessible variable; it exists at the script level because
it was not declared in a procedure.
The <SCRIPT></SCRIPT> block is contained within the <HEAD></HEAD> HTML block. This is standard
HTML scripting practice.
Instead of including introductory text directly in the HTML code, the example instead used the write method of
the Document object. This method is defined by the IE 3.0 object model.
Instead of using the HTML <BODY> tag to specify a background color, the example wrote directly to the
current document using the bgColor property of the Document object. This object is defined in the IE 3.0 object
model.
The function NameOfDay() uses the Select...Case statement to return the string representation of the passed day
number. Because this function returns a string, it is used directly in the first window.document.write statement
as a string expression.
The image shown in Figure 8.6 depicts your current Web page.
Now that you have a very primitive framework for your page, you can begin adding several ActiveX controls that will
enable the user to enter all the information the program will need to process his request for information. You will also
construct a simple HTML table, each cell of which will be used to contain an ActiveX control. Remember that without
the use of a table, ActiveX objects will appear in order as they are encountered in your HTML document. Using tables
is a simple way to ensure that the controls in your form are properly positioned.
Note
Most of the ActiveX controls used in this example are shipped with the ActiveX Control
Pad, and they are not part of the standard Internet Explorer 3.0 control set. Information
pertaining to the properties, methods, and events that apply to these controls can be found
in the ActiveX Control Pad help system.
Tip
For many ActiveX controls, including the ones shipped with the ActiveX Control Pad, the
CodeBase property should be defined, indicating a URL (a Web site, ftp site, or gopher
site, for example) where the control can be downloaded. If a Web page is loaded that uses
an ActiveX control that the user does not already have on her system, this mechanism will
provide a means to retrieve and register the needed control automatically.
Listing 8.3, taken from the <BODY></BODY> portion of your HTML document, contains your basic table and all of
the ActiveX controls that will be contained in the cells of the table.
<BODY>
<TABLE BORDER=0>
<TR>
<TD>
First Name:
</TD>
<TD>
CLASSID="CLSID:8BD21D10-EC42-11CE-9E0D-00AA006002F3">
</OBJECT>
</TD>
</TR>
<TR>
<TD>
Last Name:
</TD>
<TD>
CLASSID="CLSID:8BD21D10-EC42-11CE-9E0D-00AA006002F3">
</OBJECT>
</TD>
</TR>
<TR>
<TD>
Company:
</TD>
<TD>
CLASSID="CLSID:8BD21D10-EC42-11CE-9E0D-00AA006002F3">
</OBJECT>
</TD>
</TR>
<TR>
<TD>
Mail Address:
</TD>
<TD>
CLASSID="CLSID:8BD21D10-EC42-11CE-9E0D-00AA006002F3">
</OBJECT>
</TD>
</TR>
<TR>
<TD>
Product Interest:
</TD>
<TD>
CLASSID="CLSID:8BD21D20-EC42-11CE-9E0D-00AA006002F3">
</OBJECT>
</TD>
</TR>
<TD>
Level of Interest:
</TD>
<TD>
CLASSID="CLSID:8BD21D30-EC42-11CE-9E0D-00AA006002F3">
</OBJECT>
</TD>
</TR>
</TABLE>
<PLAINTEXT>
</PLAINTEXT>
<CENTER>
CLASSID="CLSID:D7053240-CE69-11CD-A777-00DD01143C57">
</OBJECT>
</CENTER>
</BODY>
Tip
If you're wondering where the author got the PARAM NAME values, remember that this
page was designed using the ActiveX Control Pad, which gives the developer all available
parameters for each ActiveX control.
The OBJECT ID for each of the ActiveX controls used in your <BODY> is given a name that follows
conventions established in the Visual Basic world, whereby the object identifier may be used to indicate the
control from which it is based. For example, txtCompany is used as an ID for a text entry box, and lstProducts is
used as an ID for a list box.
The default values for each control in the parameters added through the ActiveX Control Pad were used, with
two exceptions. The ListBox control, used to allow one or more products to be selected, had its MultiSelect
property changed to 1-Multi, and its ListStyle property set to 1-Option. In addition, the Style property for the
combo box control, used to set a level of purchasing interest, was set to 2-DropDownList.
The controls included in this section of HTML appear as they are encountered by the Web browser, just like all
other HTML elements.
As of now, there is no code behind the controls, so the form essentially does nothing besides display itself when
loaded. This will be remedied in the next section.
In Figure 8.7, you can see what your current Web page looks like when viewed through the eyes of a Web browser.
Now that you've created your primary controls, you need to place VBScript behind them to:
Add a list of possible products and levels of interest to the list box and combo box controls that you've added to
your page.
Act upon the Tab key so that focus moves to the next control on the page.
Act upon the Click event for the Submit button. In this case, you need to first validate all user entries, then
display a message box to the user allowing her to accept or cancel her submission.
Your first task is to populate both the product list and level of interest list when the Web page is loaded. To do this, you
will use the OnLoad event of the Window object, which is part of the IE 3.0 object model. The AddItem method for the
two controls allows you to add items to the list box and combo box. The following subprogram should be placed at the
end of your existing <SCRIPT> block:
Sub Window_onLoad()
end sub
The next task, acting upon a Tab key, is fairly simple and does not require the use of VBScript. All that is required is a
modification of one of the default parameters for each of the controls that you've added. The following parameter
should be added to each text box control; the other controls already include this behavior by default:
Your final task, input validation, does, of course, require the inclusion of additional VBScript. You will simply check
the value of each of your controls in the Click event of your Submit button (cmdSubmit), ensuring that information has
been entered into each field.
The subroutine shown in Listing 8.4 should be added to the end of the existing <SCRIPT> block.
Sub cmdSubmit_Click()
MsgBox "You must enter your First Name!",48, "Input Error" 'Warning
exit sub
end if
MsgBox "You must enter your Last Name!", 48, "Input Error" 'Warning
exit sub
end if
MsgBox "You must enter your Company Name!", 48, "Input Error"'Warning
exit sub
end if
exit sub
end if
OneSel = False
For i = 0 to (lstProducts.ListCount - 1)
OneSel = True
Exit For
End If
Next
Exit Sub
End If
MsgBox "You must select your Level of Interest!", 48, "Input Error"
exit sub
End If
For i = 0 to (lstProducts.ListCount - 1)
End If
Next
'...
document.clear
document.write
document.write("<P><H4>")
document.write("" )
document.write("</H4></P>")
TodayNum = TodayNum + 1
document.close
Else
MsgBox "You may now modify your entries as necessary. When you are
[icc] ready to re-submit, press the Submit button.", 0, "Correct Your Entries"
End if
End sub
For all TextBox controls, the Value property is used to obtain the contents of the box. However, addressing the
name of the text box control would result in the same functionality, as value is the default property for this
control.
To determine if at least one product was selected, the program has simply iterated through all of the list items,
examining the Selected property. Validation continues if at least one of these values is True.
If a field does not validate correctly, the Exit Sub statement is executed to simply terminate the validation
process. A message box (MsgBox) using the Warning icon (48) is displayed to the user indicating the source of
a validation error.
When all validation is successful, information is summarized and displayed to the user in a message box that
contains both Yes and No buttons. If Yes is selected, the current window is cleared, and the user is told that his
request has been processed. In addition, an approximate time and day that a reply can be expected is calculated
and displayed. Once again, this is done using the IE 3.0 object model, scripted with VBScript.
This concludes the development of your basic interactive Web page. To complete this page, you might add appropriate
code for your particular system to properly submit information to a server, either through CGI or some other server API
mechanism.
Summary
VBScript is a fairly limited but functional scripting language that is appropriate for use as a low-overhead Internet
scripting tool. As the world of the Internet changes on a minute-to-minute basis, one of the best sources of information
will be on the Internet itself, where companies like Microsoft and Netscape share new development strategies and
distribute their scripting product directions and documentation. When developing an HTML page using ActiveX
controls, you will more than likely find yourself jumping back and forth between your HTML development
environment, such as the ActiveX Control Pad, and your Internet browser of choice, allowing you to retrieve the latest
technical documentation available on the Web at design time.
The following list summarizes the most important issues discussed in this chapter:
In its first incarnation, VBScript will be most prevalent on the Internet, used as an ActiveX and general HTML
scripting tool.
The only data type available in VBScript is the variant. Variants are classified into subtypes, which include
strings, Booleans, and integers.
Variables and arrays may be explicitly declared using the Dim statement. Variables used without explicit
declarations are declared implicitly. Arrays declared using the ReDim statement are dynamic and may be
resized at runtime using subsequent ReDim statements.
VBScript allows the declaration of two types of procedures: subprograms (Sub) and functions (Function). The
only difference between the two is that functions return a variant value. Remember that variables are always
passed by value, not by reference, in VBScript.
VBScript includes numerous program flow control statements, including If..Then..Else, Select Case, For..Next,
and Do..Loop.
Two built-in functions, MsgBox() and InputBox(), may be used to easily display a dialog box to the user, or to
request a simple user input.
For Visual Basic and/or VBA developers, VBScript will be easy to pick up, as it is a subset of these more
powerful languages. VBScript removes significant functionality from the more traditional BASIC family. Any
functions that may have used a means to destroy or alter the user's environment have been eliminated. In other
words, you can't write a virus in VBScript.
VBScript is included in HTML using the <SCRIPT></SCRIPT> block, with the LANGUAGE = "VBSCRIPT"
attribute. VBScript is executed "on the fly" as it is parsed by a Web browser.
There are two levels of variable scope in VBScript: script and procedure level.
The Internet Explorer 3.0 Object Model allows direct access to the contents of an HTML page using VBScript.
This object model should be studied by anyone who is using VBScript in HTML, as it provides a method to link
standard HTML elements, such as buttons, to VBScript procedures using standard event definitions, such as
OnClick.
The ActiveX Control Pad brings welcome relief to the development of HTML creation that utilizes ActiveX
Controls. This tool provides scripting abilities and "point and click" control editing that makes ActiveX
development for HTML bearable.
Each ActiveX control has associated with it one or more events that may trigger the execution of VBScript
procedures, such as obj_OnClick and obj_KeyPress. The real power and use of VBScript lies in controlling
these embedded ActiveX components, as well as interacting with the IE 3.0 object model.
SAMS Publishing
filename: axu8MB.DOC
MS Pages: 26
Listing Pages: 12
Figure Count: 7
------------ -----
Total Pages: 41
Chapter 9
JavaScript
What Is JavaScript?
Origins of JavaScript
JavaScript Objects
JavaScript Events
Browser Objects
Window Object
Frame Object
Document Object
Location Object
History Object
Form Object
Hidden Object
Button Object
Radio Object
Checkbox Object
Select Object
Submit Object
Link Object
Anchor Object
Built-In Objects
Date Object
String Object
Math Object
Summary
Chapter 9
JavaScript
by Rob McGregor
The Web is alive with interactivity these days, and one of the reasons for this is JavaScript. Although ActiveX
components are the new kids on the block in bringing interactive content to the Web, JavaScript has already been
around the block a few times. JavaScript has the capability to tie together Java applets and ActiveX components
with highly interactive Web pa