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.
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 impossible to port to a 32-bit environment.
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.
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.
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.
A WIN32 Process is an running instance of an application loaded into memory.
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 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.3. A property sheet with the General stock properties.
Figure 5.4. A property sheet with the Font stock properties.
Figure 5.5. A property sheet with the Color stock properties.
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 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 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.
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.
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.
1: // Function to get a pointer to a control's IUnknown and use 2: // QueryInterface to see if it supports the interface. 3: int MyClass::DoControlWork() 4: { 5: LPUNKNOWN lpUnknown; 6: LPPPROVIDECLASSINFO lpClassInfo; 7: 8: lpUnknown = GetControlUnknown(); 9: 10: if(lpUnknown == NULL) 11: { 12: // return my error code to let me know IUnknown was NULL 13: return ERROR_CODE_IUNKNOWN_NULL; 14: } 15: else 16: { 17: if(SUCCEEDED(lpUnknown->QueryInterface(IID_IProvideClassInfo, 18: (void**) &lpClassInfo))) 19: { 20: // QueryInterface Returned a Succeeded so this 21: // Interface is Supported 22: // { 23: // Perform some function with lpClassInfo such 24: // as getting the class info and examining the class attributes 25: // { 26: 27: lpClassInfo->Release(); 28: } 29: else 30: { 31: // Control Does Not Support Interface 32: return ERROR_INTERFACE_NOT_SUPPORTED; 33: } 34: } 35: return SUCCESSFUL; 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.
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).
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.
Listing 5.2. Using the DllRegisterServer to support self-registration of the control.
1: ///////////////////////////////////////////////////////////////////////// 2: // DllRegisterServer - Adds entries to the system registry 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)) 12: return ResultFromScode(SELFREG_E_CLASS); 13: 14: return NOERROR; 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.
Listing 5.3. Using DllUnregisterServer to support self-unregistration of a control.
1: ///////////////////////////////////////////////////////////////////////// 2: // DllUnregisterServer - Removes entries from the system registry 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)) 12: return ResultFromScode(SELFREG_E_CLASS); 13: 14: return NOERROR; 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 - 3: // Adds or removes system registry entries for CMyCtrl 4: BOOL CMyCtrl::CMyCtrlFactory::UpdateRegistry(BOOL bRegister) 5: { 6: if (bRegister) 7: return AfxOleRegisterControlClass( 8: AfxGetInstanceHandle(), 9: m_clsid, // Records the Object's CLSID 10: m_lpszProgID, // Records a Unique Program ID for MyControl 11: IDS_MYCONTROL, // Records a Human Readable Name of MyControl 12: IDB_MYCONTROL, // Recordes the Bitmap to Represent MyControl 13: TRUE, // Records that MyControl can be insertable 14: // in a Container's Insert Object Dialog 15: dwMyControlOleMisc, // Records the Status bits of MyControl 16: tlid, // Records the Unique ID of the MyControls 17: // Control Class 18: wVerMajor, // Records the Major Version of MyControl 19: wVerMinor); // Recorde the Minor Version of MyControl 20: else 21: return AfxOleUnregisterClass(m_clsid, m_lpszProgID); 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."
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.
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:
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.
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 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 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.
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. This signature block is embedded into the executable program.
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.
At present Netscape does not currently support code signing.
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.
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.
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: { 3: return COleControl::GetControlFlags() | noFlickerActivate; 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.
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.
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() { return COleControl::GetControlFlags() | windowlessActivate; }
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() { return COleControl::GetControlFlags() & ~clipPaintDC; }
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() { return COleControl::GetControlFlages() | pointerInactivate; }
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).
Figure 5.12. Some of the numerous OLE/ActiveX Controls available.
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:
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.
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.
The Chart ActiveX Control allows you to display a variety of charts such as a bar chart, pie charts, and graphs.
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.
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.
The Marquee ActiveX Control allows you to have scrolling, bouncing, or sliding text and URLs within a window, much like the old cinema marquees.
The Menu ActiveX Control allows you to embed menu button or pull-down menu functionality in your Web page.
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.
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.
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.
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.
The Timer ActiveX Control fires an event periodically at a set time interval. The timer control is invisible at runtime.
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.
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.
Listing 5.6. The HTML syntax for the <OBJECT> tag.
1: <OBJECT 2: ALIGN= alignment type 3: BORDER= number 4: CLASSID= universal resource locator 5: CODEBASE= universal resource locator 6: CODETYPE= codetype 7: DATA= universal resource locator 8: DECLARE 9: HEIGHT= number 10: HSPACE= value 11: NAME= universal resource locator 12: SHAPES 13: STANDBY= message 14: TYPE= type 15: USEMAP= universal resource locator 16: VSPACE= number 17: WIDTH= number 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.
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> 3: <TITLE>AN EMMBEDDED ActiveX Control</TITLE> 4: </HEAD> 5: <BODY> 6: 7: <p align=center><font size=6><em><strong><u>An EMMBEDDED ActiveX Control </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" 15: <PARAM NAME="_ExtentX" VALUE="9155"> 16: <PARAM NAME="_ExtentY" VALUE="8573"> 17: <PARAM NAME="MovieWindowSize" VALUE="2"> 18: <PARAM NAME="MovieWindowWidth" VALUE="342"> 19: <PARAM NAME="MovieWindowHeight" VALUE="243"> 20: <PARAM NAME="FileName" VALUE="E:\vinman\duds.avi"> 21: <PARAM NAME="Auto Start" VALUE="TRUE"> 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.
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.
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 features of Internet Explorer 3.0.
The most current ActiveX SDK is available from Microsoft, at no charge, at the following Internet URL: http://www.microsoft.com/intdev/sdk/.
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.
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.
Figure 5.16. The Microsoft Developers Network Library CD.
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.
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.
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.)
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.
Figure 5.17. The Class Hierarchy for COleControl.
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.
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.
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.
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.
Figure 5.18. The New dialog box in Visual C++.
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:
Figure 5.20. Step 1 of the OLE ControlWizard in Visual C++.
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).
Figure 5.21. Step 2 of the OLE ControlWizard in Visual C++.
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.
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:
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.
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.
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.
Figure 5.25. Step 1 of the ATL COM AppWizard in Visual C++.
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.
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.
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.
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.
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.