Полезная информация

Chapter 2
What Can ActiveX Do for You?

What Can ActiveX Do for You?

Gone are the days of hacking together simple stand-alone applications that had all the interoperability of two semi-trailer trucks screaming toward each other at 100 miles an hour. With the advent of OLE (Object Linking and Embedding) and more recently ActiveX and the Internet, applications are expected to be flexible, modifiable, and extendible (with "clairvoyant" running a close fourth).

Spending a little time up front working out the design and architecture of your application can and will make all the difference in the world. Almost any application worth its salt has resulted from some forethought, regardless of whether that information is crammed into the head of one of your developers, scribbled on restaurant napkins, or written in formal documentation. I recommend the latter two since removing your developer's head to take to a strategy meeting is probably not an option.

The basic principles of OLE and ActiveX are going to determine most of the specific component architecture and design. For example, ActiveX Controls and Documents are developed within a specific set of parameters and rules so that they will interact with Containers correctly. Automation Servers and Controllers have to conform to OLE Automation rules. And COM Objects have to support the basic fundamentals of COM. But what about component relationships and lifetimes? What about access to interfaces and support for security? What if your component is going to be utilized by users who speak another language?

Defining the Needs and Requirements of Your Application

A specification is important to establish the basic requirements of the component you are asked to create. Before you can proceed, you must have a clear understanding of what kind of component or application is needed and why you are creating it. Appropriate questions to ask are, "What created the need for the component, and how is it going to be used?" If the person or persons can't describe the problem, they probably don't understand the problem. The last thing anyone needs is an incomplete picture of the problem, which tends to create delays and promote last minute changes that can cause unexpected results. Try to get as much of the specification as possible on paper.

After you determine the need, you can move on to designing the component. Again, it is critical to get as much information as possible. Does the problem require a single component or multiple components? Do the components need the capability to interact together? And, if so, is speed an issue? What about the skill level of your developers? How are they able to cope with change or possibly new and unfamiliar development methods? What are the support and maintenance requirements?

What is the problem? And what is going to be the solution? Take for example the need for creating in-house purchase requests. Is it simple enough to say, "We will create an ActiveX Document and use our Web browsers to interact with the Document"? Probably not. What about e-mail integration? What about training for how to use the Web browser and the purchase request application? What if the purchase request application must integrate with a legacy application in-house? What if the development tool that you normally use can't create ActiveX Documents? What if there aren't any developers on staff who can handle the effort? Most of these issues can be boiled down to a single question, "What is the level of effort versus the amount of gain?" That is, is it worthwhile to pursue a development project that is difficult to understand, implement, and maintain? Or is it worthwhile to use a simpler approach and live with its limitations?

All these issues and more will affect the kind of component you create and how you will develop it. As a developer of ActiveX components, it is your responsibility to know the answers to these questions. You have the specific domain knowledge, that is, the capabilities and limitations of yourself and the tools that you use. This book will hopefully aid you in making the correct choices when choosing and implementing a development plan.

What Type of ActiveX Component Do You Need?

The first thing you must decide is what kind of component best fits your requirements.

Automation Servers and Controllers

Automation Servers and Controllers probably have the greatest amount of flexibility. The Servers' IDispatch interface can be used from just about every major application available from Microsoft and hundreds of other manufacturers. Because it won't suffer from the same versioning requirements placed on strict COM interfaces, the interface also lends itself well to prototyping and modeling component interactions.

A dual-interface Automation Server created in-process is the fastest type of Automation Server. Dual-interface refers to the fact that a Server contains two interfaces one based on IDispatch and the other based on COM. The COM interface is actually the fastest of the two. An in- process Server means that the application resides in the same memory address space as the application that created it. This allows the invocation of methods defined within the Server to be performed significantly faster because there is no burden of having to cross process boundaries every time a method is called. For the same reason, Server load times are fast because of the minimal number of steps involved in creating the Server and getting its IDispatch or COM interface pointer.

Nothing inherent to Automation architecture promotes the use of User Interface (UI), and nothing prevents its use either. You have complete freedom and control over how the Servers are implemented and used. Your Automation Servers could potentially contain UI in the form of a dialog or a form.

Automation Servers also lend themselves well to the increasingly popular multitiered applications architectures that have appeared in recent years. The separation of UI from function is perfect for Automation Servers because you have complete freedom over how your Servers are implemented and used. Creating Servers with thin UI layers that utilize other Servers with no UI to accomplish a task is at the heart of multitiered applications development. Automation Servers should be used in the same fashion as any other DLL. The only difference between an Automation Server and a standard Windows DLL is the fact that the Server uses only a restricted set of data types whereas a DLL can use any type.

Designing Automation Servers that work without the need for UI also makes them prime candidates for Distributed COM (DCOM) and other distributing technologies.

ActiveX Controls

You should use ActiveX Controls primarily as they were intended to be used: as UI components to enhance or support a dialog, form, or document. Controls can be expensive to load because they can potentially require a large number of interfaces, depending on the functionality the Control supports. The OC 96 specification added the QuickActivate interface to help with Control load times, but the improvement was not significant. In addition, the OC 96 specification identified a number of interfaces that are considered optional or conditional, depending on the type of Control you are implementing. It is wise to review the specification to determine what can and cannot be removed from your implementation in order to improve its performance and overall size.

When creating Controls, be sure to make them as lean as possible. If the Control will not be commercially distributed, remove the "AboutBox" code. Also, see whether you can get away with relying on the property editor of the application's development tool that will be used rather than supporting property pages. Avoid large amounts of persistence, and save the data only if you must. The real message here is to implement only those features that are truly useful and helpful for your Control implementation.

COM Objects

COM Objects (Custom Interfaces) are far more flexible than any other component type when it comes to interface design. They are also the fastest interfaces in terms of execution times, although that is with the caveat that the COM interface is in-process to the application using it. COM Objects can use any data type within their interface definitions and do not suffer from the same restrictions as Automation Servers. This situation does present a problem when crossing process boundaries because the Object will then require its own proxy-stub marshaling code.

Proxy-stub marshaling is what takes place when an application resides in a process space other than the application it is communicating with. It is necessary to translate function calls and data to a context that can be understood by both applications. This is the responsibility of the proxy-stub code and is true for all types of OLE components. Going out-of-process with any type of component will have a profound effect on the performance of the application because a lot more work is taking place in order to perform the specific set of operations.

Automation Servers rely on built-in proxy-stub marshaling code, whereas COM interfaces are required to create their own. This problem is not insurmountable, but it does add to the development time and effort, maintenance of the code, distribution of the Object, and overall performance of the application, so it needs to be considered when deciding on what type of component to develop. If you are going to go out-of-process with the COM Object, you should probably consider using an Automation Server because the performance will be comparable between the two, that is unless you are going to come up with your own marshaling code that significantly outperforms the built-in mechanisms.

COM Objects are useful for the cases where the limited set of data types available to Automation Servers has a significant impact on the type of interface that can be created. An example of a COM Object might be a simple implementation that performs calculations of a large volume of user-defined data. Instead of copying the data and passing it to the COM Object, it might be more useful to pass a pointer to the data and allow the COM Object to manipulate the data directly. Automation Server data type restrictions would not allow for the creation of this kind of interface. COM does, however. Although in this case, the COM Object can execute only in-process because it needs direct access to the data.

Selecting the Right Tool for the Right Job

Microsoft is going hog-wild with its tools development. Every product coming out these days seems to have the capability of building one kind of ActiveX component or another. Applications like Visual C++, Visual Basic (VB), J++, Access, FoxPro, Microsoft Word, and Microsoft Excel, just to name a few, can create anything from ActiveX COM Objects to ActiveX Documents. This book addresses creating ActiveX components by using Microsoft Visual C++ (VC++). Deciding whether to use VC++ for your development is usually based on one issue: limitations. All other products and tools capable of creating ActiveX components are going to suffer from some form of limitation. VC++ is the most powerful and flexible tool for creating ActiveX components, and now that you have decided VC++ is the way to go, you need to decide on a development strategy.

When creating your ActiveX component with Visual C++, you have four options, which are all described in the following sections.

Microsoft Foundation Classes

The Microsoft Foundation Class Library (MFC) is the easiest choice of all the tools available for ActiveX development. The VC++ IDE (Integrated Development Environment) is designed specifically with MFC in mind and provides very useful application and ClassWizards for developing your application. MFC is robust and will probably cover 90 percent of your application's needs. Unfortunately, like every other software project that you have probably worked on, the last 10 percent is where you spend 90 percent of your time.

Going outside the bounds of what MFC defines can be difficult and, in some cases, impossible. Take for example the requirement to have an Object that is single instance only. No matter how the Object is created by the client application, you always want the same instance returned. Providing this kind of functionality is impossible with MFC without modifying the built-in Class Factory classes, and these are not normally exposed to the developer.

Supporting dual-interfaces in Automation Servers is not impossible, but it does cause enough changes in your code so that the ClassWizard can no longer be used to completely maintain your methods and properties. Some work will have to be done by hand. MFC does provide a number of features and functions when developing ActiveX components, but be prepared to live by its rules. Occasionally, you can bend the rules, but you can almost never break them. The following chapters discuss how to successfully bend the rules in MFC and implement both single instance and dual-interface servers.

A good rule of thumb when working with MFC is to avoid using the built-in classes as much as possible by utilizing the basic Windows API instead.

Avoiding use of the MFC classes to solve your application problems has two benefits. The first is that your application will generally run faster; the second is that moving to an alternative development tool such as ATL or BaseCtl will prevent a large amount of code rewrite. A large portion of the MFC classes have equivalent Windows API functions, especially in the area of GDI and drawing, and is not that much of a departure from MFC. Basic storage classes, such as lists and arrays, could be better provided by a general purpose class library, such as the Standard Template Library (STL), which can be used in combination with all of the ActiveX development frameworks you will be seeing in this book.

ActiveX Template Library

ActiveX Template Library (ATL as it has come to be known), is a newcomer to the ActiveX arena. It first appeared in the summer of 1996 and quickly became a favorite among developers. Based on the amount of development taking place by using ATL and the fact that, unlike the BaseCtl framework, it is actually a supported product, Microsoft and the industry have obviously seen ATL as a viable platform for creating ActiveX components and it should be around for a long time.

The initial implementation, versions 1.0 and 1.1, focused on the creation of small and fast Automation Servers and COM Objects. With the introduction of 2.0, ATL expanded its coverage to include ActiveX Controls and other ActiveX components. The level of integration with the VC++ IDE originally consisted only of an AppWizard used to create the basic ATL project, which, by the way, was more complete than its MFC counterpart. Also the ClassWizard could be used to maintain the Objects, methods, and properties as it can with MFC. ATL version 2.0 and VC++ 5.0 are now fully integrated, supplying the same level of tool support, such as AppWizards, ObjectWizards, and ClassWizards.

An added bonus to ATL is that it can be integrated into existing MFC applications without dire consequences or enormous amounts of work. This capability gives you complete freedom to develop your component without the restrictions that MFC imposes, while still being able to use nice MFC classes and features (like structures, arrays, and lists, to name a few).

BaseControl Framework

BaseControl (BaseCtl) Framework and the ActiveX SDK is without a doubt the most difficult route to choose for ActiveX component development. The BaseCtl was first developed by the Visual Basic 4 (VB 4) development group in late 1995 and early 1996 in response to growing demands for better performance when using OCXs and VB. BaseCtl (then referred to as the "MarcWan" framework because of its primary developer, Marc Wandschnieder at Microsoft) was intended as a bare-bones framework to be used to create lightweight OLE Controls.

In an effort to quell the demand for tools to create OLE Controls, the framework was put into the hands of various Control developers and vendors who were in contact with Microsoft and the VB group. At the Internet PDC, Microsoft packaged the BaseCtl Framework as part of the ActiveX SDK, and the rest, as they say, is history.

The BaseCtl has no integration with the VC++ environment. In fact, the version of the BaseCtl framework that ships with the ActiveX SDK is little more than sample programs from which you can create new applications. Another version of the baseCtl framework that has been available to members of the VB 4 and 5 beta testers actually contains an AppWizard. The AppWizard used to create the base set of source files is written in VB and is ad hoc at best. The BaseCtl relies on a series of Object and library files that have to be built by you, the developer, before they can be used. All of the source files that come with the SDK and those generated by the AppWizard depend on command-line compilation. With a little bit of effort on your part, the projects can all be converted to VC++ projects, including the Object and library files that come with the SDK. The documentation for the BaseCtl is rudimentary and somewhat cryptic.

Basic Control development with the BaseCtl framework can be difficult, as well. A fair number of the functions and capabilities that you're used to in MFC aren't present in the BaseCtl. A number of the function names are different, and the architecture for persistence is completely different. BaseCtl is meant to get the job done with as little code as possible, and unfortunately it's obvious.

For those of you who have already written Controls in MFC and want to port them to the BaseCtl, I have only one thing to say, "Roll up your sleeves because it's going to get messy." With the BaseCtl, you're expected to dig into the guts of the framework and build a lot of the function yourself.

One thing the BaseCtl has going for it is a fair number of samples. When installing the BaseCtl, it is recommended that you install the samples as well. Chances are that if you need to do something, it's in one of the samples. VC++ has a nice feature called "Find in Files." Take advantage of it. Another nice feature of the BaseCtl is the capability to access all the source code in the BaseCtl framework directly, so if you find a bug (and there are a couple), you can fix it yourself and move on.

Also, you have a lot more freedom to model your Control as you want. For example, you have two Controls that you want to develop; one is a Number Control, for basic numeric data input, and the other is a Currency Control, for basic currency data input. Both can rely heavily on the C++ inheritance model at the code and interface levels by creating a BaseNumeric Control.

You don't have this kind of freedom with MFC. BaseCtl should not be taken lightly, and you can expect a lot of work when implementing a component with it. Even worse, the results may not justify the work. In one case, after converting an existing MFC Control to the BaseCtl, a 40 percent improvement was realized in the average load time of the two versions of the Control. You might think "Wow--40 percent! That's pretty good." Unfortunately, the load times were already so low for both Controls that you literally had to have hundreds of Controls on the form before the improvement was noticeable.

Create Your Own Framework

The last method for Control development is to just sit down and do it. Get code from the class libraries, samples, books, and so on, and come up with your own framework, tools, or whatever you want. But you can expect the work to be hard and time-consuming. To get an idea of how much work is actually involved, stop for a minute and take a look at some of the source files in MFC, ATL, and the BaseCtl. Literally thousands of lines of code have been implemented over the course of months and even years. Due to the constantly changing nature of OLE and ActiveX requirements, it is wiser to choose an existing platform rather than trying to reinvent the wheel.

The key to successful Control development is not in the framework that you choose to develop in, but in how you apply it.

Basic ActiveX Component Architecture

Before moving on to the actual implementation of each type of ActiveX component, you need to review some of the basic concepts and architecture surrounding each component.

Even though you can develop a wide variety of ActiveX components--controls, servers, documents, and so on--one thing is true for all of them: Underlying every component is the Component Object Model or COM architecture. COM defines the standard that all ActiveX components rely on when interacting with other ActiveX components.

In addition to COM, all ActiveX components are further defined or restricted by the operating system in how they are created and used. The type of ActiveX component you create will further define or restrict your options when creating components. A wide variety of choices are available to you as a developer and it is important to understand the ramifications of each.

ActiveX Automation Servers

Probably the easiest to implement and most flexible form of ActiveX component is the Automation Server. An Automation Server is an application that contains one or more IDispatch-based interfaces. An interface is a collection of related methods and properties and an IDispatch interface is the name of the COM interface that is used to generically invoke those methods and properties. For more information on IDispatch interfaces please see the VC++ books online. The capability to define unique methods and properties for each server and have them be accessible through a generic mechanism is the real power of Automation Servers.

An automation server may or may not be directly creatable by other applications via a CreateObject or similar call. It is possible to have what are referred to as nested objects that represent a hierarchy of objects. A single creatable automation object is responsible for the creation and distribution of other automation objects. For example, an application may expose a Document automation interface that can be created and manipulated by another application but that only exposes a Page interface as a method call to the document object. The lifetime of the Page object is less than or equal to the Document object and cannot exist on its own. (The terms object and server are used synonymously throughout this book.)

Three of the chapters in this book focus on creating ActiveX Automation servers using MFC, ATL, and the BaseCtl framework. Each of the tools has its own set of strengths and weaknesses and, depending on your specific application requirements, will determine which tool you should use.

MFC is great for rapid development and ease of modification. Servers created with MFC will be the largest and slowest of the three types. Deviating from the standard MFC implementation of Automation Servers can also be a limiting factor when using this tool. MFC's greatest strength is its integration with the VC++ IDE and the speed with which an implementation can be up and running. In only minutes developers can create a server and implement its methods and properties, assuming that they are familiar with the tools available.

ATL by itself can create small, lightweight servers that are easy to use and modify. ATL is limited to the creation of COM-based objects only and is lacking in generic or utility class support. Combined with MFC and the Standard Template Library (STL), ATL is a very powerful framework for ActiveX development. For those developers who are willing to take the time to learn it, ATL is by far the best route.

The BaseCtl also allows you to create small, lightweight servers. Unfortunately, the BaseCtl lacks support from Microsoft as a product which impacts its capability to be a practical long-term development tool. It has, however, a very straightforward and easy-to-understand architecture that is helpful to those developers who are learning OLE and ActiveX from the ground up and who are not afraid of all the gory details.

Types of Automation Servers
When creating an Automation Server, you must decide how to implement the server relative to how it is going to be used. You can create two basic types of servers: DLL-based and EXE-based.

DLL-based Servers
If the server does not have to run as a stand-alone application and performance is a critical issue, you should implement your server as a DLL. A DLL-based server is typically referred to as an in-process server because of how it normally executes relative to its controller.

EXE-based Servers If the server application does have a requirement to run as a stand-alone application, you must implement it as an EXE. An EXE-based Automation Server is typically referred to as a local Server or out-of-process Server, and it will execute in its own process space.

Types of Server Execution
Automation Servers also have an execution model that is independent of how the server is written. Automation Servers can execute either in-process, locally or remotely, relative to the application that has invoked or is using it.

In-Process Execution
An in-process Server is called in-process because it executes within the same process space as that of the application that created it. Only DLL-based automation Servers can execute as in-process, but that is not a guarantee. This is very important to note when using nested objects or shared objects. If an object is created in a process space, say Process A, and it is passed to another application in another process space, Process B, the Server in Process A will execute as a local Server relative to the application in Process B regardless of the fact that the Server in Process A is a DLL-based Server. This issue is very critical since more times than not in-process Servers are used to improve performance of the application using them.

Local Execution Local execution is when an Automation Server is executing in a process space other than the process space of the controller application. As was stated earlier, a DLL-based Server may execute locally to its controller, depending on how it was created versus which application is using it. The main issue with local Servers is performance since all of the method calls have to cross process boundaries. This condition requires additional code overhead to move data back and forth between the Server and its Controller.

Remote Execution Remote Execution is when the Server is executing on a machine other than the application that is controlling it. As with local Servers, performance is an issue with this type of execution.

ActiveX Controls

An ActiveX Control, for all intents and purposes, is still the same OCX or OLE Control that you have come to know and love over the past several years. In fact, the only change with the coming of ActiveX is a decrease in the requirements to qualify as a control.

To qualify as an ActiveX Control, a component must be a COM Object, implement an IUnknown interface, and support registration and unregistration through the exported functions, DLLRegisterServer and DLLUnregisterServer. That's it! Pretty easy, right? Well, not really.

Even though your component qualifies as an ActiveX control, if all it supports is the preceding features, it will not do much more than take up space on your hard disk. If it needs UI, persistence, events, or any other feature common to controls, the control must implement other categories of interfaces. The exact requirements are in the OLE Control and Control Container Guidelines, Version 1.1 published by Microsoft. All of the guidelines for ActiveX development are available on the Internet at the Microsoft Web site or on the ActiveX SDK CD.

You have three tools at your disposal for creating ActiveX controls: MFC, ATL, and BaseCtl. As we pointed out earlier in the section "ActiveX Automation Servers," each tool has its strengths and weaknesses. With ActiveX Controls you only have one option when creating and executing the control: as a DLL and in-process to the Control's container application. Even though the extension of the Control is .ocx, it is still in fact just a .dll.

Support Tools Needed for Building ActiveX Components

When creating your ActiveX Components, a few tools are essential to successful ActiveX development. Most of these tools are automatically installed as part of the Visual C++ development environment. As ActiveX development matures, more and more tools will become available. Using as many tools as you can will greatly improve your understanding of how your components work, as well as improve their overall implementation.

MIDL Compiler

The Microsoft MIDL compiler is now a standard component of the Microsoft Visual C++ environment. The MIDL compiler compiles COM interface definitions (IDL) files into C code, which is then compiled into the project by the Visual C++ compiler.

The MIDL compiler also provides support for marshaling interfaces across process boundaries. Starting with Visual C++ 4.0, the MIDL compiler started shipping as a standard component of Visual C++. The MIDL compiler is also available via the Win32 SDK from Microsoft.


Mktyplib is a type library compiler for compiling ODL files. Mktyplib is the predecessor to MIDL and produces the same type of output as MIDL. The use of Mktyplib will decrease as Microsoft moves from ODL to IDL files.


GUIDGEN is a tool that is used to generate Global Unique Identifiers (GUID), which can be used for Interface IDs, Class IDs or any other 128-bit UUID, such as an RPC interface. GUIDGEN is installed only when the OLE development option is selected during the Visual C++ installation. When GUIDGEN is run, a GUID is created and placed in the Windows clipboard. After running the GUIDGEN application, the resultant GUID is pasted from the clipboard into the code that needs a GUID.


RegEdit, or the registration editor, is a standard component of both the Windows 95 and Windows NT operating systems. The registration editor is used for browsing and altering operating system and application settings. The registration editor can also be used for installing and registering your COM objects.

is a very powerful tool and must be used with extreme caution by experienced users. If used improperly, systems can be damaged, resulting in a loss of data or a malfunctioning computer.

In Windows 95, this program is called regedit.exe. In Windows NT, this program is called regedt32.exe.

Registration Server

The Registration Server is an application that can be used to register the settings of a COM object in the Windows registry without the need to create a separate registration file. The application is called regsvr32.exe and is automatically installed if the OLE development option is selected during Visual C++ installation or if the ActiveX SDK is installed.


Ole2View is a program that lists ActiveX components relative to their function and category. Ole2View is extremely helpful in the test and use of your components. Ole2View can be used to determine whether your component is registered properly and the type and number of interfaces your component defines. It even allows you to instantiate and access various components and interfaces within your application, and now it has expanded support for configuring components to use DCOM. When attempting to determine what is the cause of problems with a troublesome component, Ole2View is invaluable.

Adding the Tools to the Visual C++ Development Environment

In order to maximize development productivity, the tools needed for COM programming should be integrated into the Visual C++ environment. Each of the tools needed can be added to the IDE's (Integrated Development Environment) Tools menu. The following section illustrates how to incorporate the tools into the IDE.

Adding GUIDGEN to the Visual C++ environment enables the generation of a UUID from a single menu command. The generated UUID is placed in the Windows clipboard and must be pasted into the project code.

To add GUIDGEN to the Visual C++ environment:

  1. Select the Customize command from the Tools menu. Select the Tools tab from the Customize dialog.

  2. In the Menu Contents list box, scroll to the bottom of the list, and select a blank line, and type &Generate New UUID.

  3. In the Command edit box, type GUIDGEN.EXE.

  4. Clear all text from the Arguments edit box.

  5. Clear all text from the Initial Directory edit box.

  6. Click the Close button to add the entry to the Tools menu.

In addition to the GUIDGEN program, you may want to consider adding the registry editor, the ability to unregister a component, and any other tool that you find useful for your development.

From Here...

All of the frameworks described in this chapter have their strengths and weaknesses. MFC allows for rapid component creation and implementation and the level of support built into the VC++ IDE for MFC is beyond comparison with ATL or BaseCtl. MFC offers a very large and robust class library for solving most, if not all, of your development problems. MFC does, however, suffer from the problem that it is everything to everyone, which results in a slower application or one that cannot deviate from the "norm" fairly easily.

ATL provides a small and deliberate framework for creating ActiveX components. ATL, however, falls very short in the area of common class and utility support, which is the thing that is MFC's strength. In addition, ATL's integration with the VC++ IDE also leaves room for improvement.

BaseCtl is similar to ATL in that it is focused specifically on small, fast component development. Like ATL, it lacks the same common class and utility support that makes MFC so attractive. BaseCtl has an added negative of being considered only as a sample and not as a supported product by Microsoft.

The level of experience of the development team and the intended life cycle of the code and applications will also affect the decision of which tool to choose to create ActiveX components. Take the time to investigate all of the options available to you before deciding on a platform and a direction.

The following chapters examine in detail how to implement some of the components described so far using MFC, ATL, and BaseCtl. Even with all of the information presented so far, the only true way to know that you have made the right choice is to do it yourself.