OLE, ActiveX, COM, DCOM, VCL, CORBA, MTS. . . the industry certainly doesn't lack for acronyms when it comes to the subject of component architecture! In this chapter, I explain some of these acronyms and at least mention the others in passing. I'll explain what these terms mean and try to shed some light on the often-confusing world of COM and ActiveX. Specifically, I cover
I'd be lying if I said that COM, ActiveX, and OLE are easy to understand. They are not. It can be very confusing at first. I can't do justice to this subject in one chapter. My goal for this chapter is to give you enough of a background on these architectures so that you can better understand the acronyms you see bandied about these days. You will also get some good hands-on training in creating COM and ActiveX controls. Fortunately, Delphi takes a great deal of the pain out of dealing with these APIs.
You can't talk about OLE and ActiveX without talking about COM, which stands for Component Object Model.
New Term: COM (Component Object Model) is a Microsoft specification for creating and implementing reusable components.
"Components? I thought Delphi used VCL components." Certainly VCL components are the most effective use of components in Delphi. They aren't the only possibility, though. As you work through this hour, you will get a clearer picture of how COM and ActiveX work with Delphi.
COM is the basis for both OLE and ActiveX. An analogy might be the TObject class in VCL. All classes in VCL are ultimately inherited from TObject. Derived classes automatically get the properties and methods of TObject. They then add their own properties and methods to provide additional functionality. Similarly, OLE and ActiveX are built on top of COM. COM is the foundation for all OLE and ActiveX objects.
As a component architecture, COM has two primary benefits:
NOTE: One major drawback to COM is that it is heavily tied to the WinTel (Windows/Intel) platform. So although you can use a COM object in many different Windows programming environments, you can't necessarily use that COM object in a UNIX programming environment. Recently Microsoft has tried to move COM to non-Windows platforms, but it remains to be seen whether this attempt will ultimately succeed. This chapter deals only with COM and ActiveX as they exist in the Win32 programming environment.
You can use a number of different languages and environments to write COM objects. You can create COM objects with Delphi, C++Builder, Visual C++, Visual Basic, and probably a few other development environments. When created, a COM object can be used in an even wider variety of development environments. A COM object created in Delphi can be used by a VB programmer, a C++Builder programmer, or even a Visual dBASE or PowerBuilder programmer.
A COM object is typically contained in a DLL. The DLL might have an extension of .DLL or it might have an extension of .OCX. A single library file (DLL or OCX) can contain an individual COM object or can contain several COM objects.
COM is full of confusing terminology. The following sections explain some of the terms used in COM and how the many pieces of COM fit together. All of these pieces are interrelated, so you'll have to read the entire section to get the big picture.
New Term: A COM object is a piece of binary code that performs a particular function.
A COM object exposes certain methods to enable applications to access the functionality of the COM object. These methods are made available via COM interfaces. A COM object might contain just one interface, or it might contain several interfaces. To a programmer, COM objects work a lot like Object Pascal classes.
Access to a COM object is through the COM object's interface.
New Term: A COM interface is the means by which the user of a COM object accesses that object's functionality.
A COM interface is used to access a COM object; to use the object, if you will. The interface in effect advertises what the COM object has to offer. A COM object might have just one interface, or it might have several. In addition, one COM interface might implement one or more additional COM interfaces.
COM interfaces typically start with the letter I. The Windows shell, for example, implements interfaces called IShellLink, IShellFolder, and IShellExtInit. Although you can use any naming convention you like, the leading I universally and immediately identifies the class as a COM interface to other programmers.
COM interfaces are managed internally by Windows according to their interface identifiers (IIDs). An IID is a numerical value contained in a data structure (a record). The IID uniquely identifies an interface.
New Term: A COM class (also known as a coclass) is a class that contains one or more COM interfaces.
You can't use a COM interface directly. Instead, you access the interface through a coclass. A coclass includes a class factory that creates the requested interface and returns a pointer to the interface. COM classes are identified by class identifiers (CLSIDs). A CLSID, like an IID, is a numerical value that uniquely identifies a COM class.
COM objects must be registered with Windows. This is where CLSIDs and IIDs come into play. CLSIDs and IIDs are really just different names for the same base data structure: the Globally Unique Identifier (GUID).
New Term: A GUID is a unique 128-bit (16-byte) value.
GUIDs are created by a special COM library function called CoCreateGUID. This function generates a GUID that is virtually guaranteed to be unique. CoCreateGUID uses a combination of your machine information, random number generation, and a time stamp to create GUIDs. Although it is possible that CoCreateGUID might generate two GUIDs that are identical, it is highly unlikely (more like a statistical impossibility).
Thankfully, Delphi programmers don't have to worry about creating GUIDs. Delphi automatically generates a GUID when you create a new automation object, COM object, ActiveX control, or ActiveForm control. GUIDs in Delphi are defined by the TGUID record. TGUID is declared in System.pas as follows:
TGUID = record D1: Integer; D2: Word; D3: Word; D4: array[0..7] of Byte; end;
When you create a new COM object, Delphi automatically creates the GUID for you. For example, here's the GUID for a test COM object I created:
Class_Test: TGUID = `{F34107A1-ECCF-11D1-B47A-0040052A81F8}';
Because GUIDs are handled for you by Delphi, you won't typically have to worry very much about GUIDs. You will, however, see GUIDs a lot as you create and use COM objects (including ActiveX controls).
TIP: If you need to generate a GUID manually, you can type Ctrl+Shift+G in the Code Editor. Delphi will generate a GUID for you and insert it at the cursor point.
COM objects often use a type library.
New Term: A type library is a special file that contains information about a COM object. This information includes a list of the properties, methods, interfaces, structures, and other elements that the control contains. The type library also provides information about the data types of each property and the return type and parameters of the object's methods.
This information includes the data types in the object, the methods and properties of the object, the version information, interfaces in the object, and so on. Type libraries can be contained in the COM object as resources or as a standalone file. Type library files have a .TLB extension. A type library is necessary if other developers are going to use your COM objects as development tools. A COM object's type library contains more information about the object than is available by simply querying the object for its interfaces. The Delphi IDE, for example, uses the information found in type libraries to display an ActiveX control on the Component palette. Users of a COM object can examine the type library to see exactly what methods and interfaces the object contains.
Distributed COM (DCOM) is a subset of COM that provides the capability to use COM objects across networks or across the Internet. DCOM extends COM to provide the mechanism for using COM in a networking environment. A detailed discussion of DCOM is beyond the scope of this book, but note that DCOM is definitely prevalent in certain types of network programming.
NOTE: CORBA (Common Object Request Broker Architecture) is a competing technology to DCOM. CORBA is platform-independent, which makes it more desirable than DCOM in many ways. In addition, CORBA is an open architecture supported by a consortium of software companies (unlike DCOM, which is a Microsoft-specific solution). Fortunately, Delphi gives you the option of creating both DCOM and CORBA objects.
Every COM object has a reference count. The reference count, naturally, contains the number of processes that are currently using the COM object. A process is any application or DLL that uses a COM object. Because a COM object can be used by any number of processes at one time, reference counting is used to determine when the COM object is no longer needed in memory.
When a COM object is created, its reference count is initialized to 1. The reference count is incremented by one each time a process attaches to the COM object. When a process detaches from the COM object, the reference count is decremented by one. When the reference count gets to 0, the COM object is freed from memory.
All COM interfaces descend from a base interface called IUnknown. Table 15.1 lists the methods of IUnknown.
Method | Description |
QueryInterface | Queries the interface to obtain a list of supported interfaces. |
AddRef | Increments the interface's reference count. |
Release | Decrements the interface's reference count. When the reference count reaches 0, the object is freed from memory. |
I mention IUnknown primarily for historical reasons. Delphi programmers don't really have to worry much about IUnknown as other programmers do. Delphi takes care of handling reference counting and freeing the memory for the COM object. Delphi also elevates dealing with COM objects to a level that makes an intimate knowledge of IUnknown all but obsolete.
To help bring this into perspective, let's create a COM object. This COM object will be ridiculously simple but should illustrate how to use and build COM objects in Delphi. The COM object you create will have these characteristics:
Type | Name | Description |
property | X | The first number to multiply. |
property | Y | The second number to multiply. |
method | DoIt | A method that multiplies the two numbers and returns the result. |
The following sections explain the process of creating the COM object.
The first step in creating a COM object is to create a DLL project that will contain the COM object's code. Delphi uses the term "ActiveX Library" to refer to all COM library projects. This description isn't entirely accurate, but it's close enough. Perform these steps to create an ActiveX Library:
Figure 15.1. The Object Repository's ActiveX page.
That's all there is to this particular step. Delphi creates a DLL project for you and is waiting for your next move.
The next step is to create the COM object itself. This step is also relatively simple. Perform these steps to do so:
FIGURE 15.2. The COM Object Wizard.
THE COM OBJECT WIZARD
Let me take a moment to talk about the COM Object Wizard. The Class Name field is used to specify the class name for your COM object. Type the class name here, but don't prepend the class name with either a T as you would for a Delphi class, nor an I as is customary for interfaces. Delphi will take care of creating the class and interface names automatically.
The Instancing field is used to control how multiple instances of the COM object are handled. Choices include Internal, Single Instance, or Multiple Instance. See the "COM object wizard" topic in the Delphi help for descriptions of these instancing options (you can click the Help button on the COM Object Wizard to display the correct help topic).
The Threading Model field is used to specify how client applications can call your COM object. Choices include Single, Apartment, Free, or Both. Again, see the Delphi help for descriptions of the threading models.
The Implemented Interfaces field is where you add the names of any interfaces that your COM object will implement. If you have an interface called IMyFileIO and you want to use that interface in your new COM object, you would type IMyFileIO in this field.
The Description field is used to supply a description for the COM object. The description is optional, but it's a good idea to provide one.
When the Include Type Library check box is checked, Delphi will create a type library for the COM object. Creating a type library enables your COM object to be used by client applications.
Okay, let's get back to work:
When you click the OK button, Delphi creates a unit for the COM object's class and displays the Type Library Editor, as shown in Figure 15.3. Before continuing, I need to take a moment to talk about the Type Library Editor.
The Type Library Editor is used to manipulate a type library. The Type Library Editor enables you to add or remove interfaces, add properties and methods to interfaces, remove elements from interfaces, and create host of other COM elements such as enumerations, records, or coclasses. The Type Library Editor makes it easy to add elements to a type library. You'll learn about adding elements in the next section when you add properties and a method to the COM object.
On the left side of the Type Library Editor is the Object pane. The Object pane contains a tree view control. At the top of the tree view hierarchy is the type library itself. Below the type library are elements contained in the type library. In Figure 15.3, you see two elements: the IMultiply interface and the Multiply coclass.
On the right side of the Type Library Editor is the Information pane. This pane provides information about the object currently selected in the Object pane. The information presented in the information pane varies with the type of object selected. The Attributes page shows the type library name, its GUID, version, help string, help file, and so on.
NOTE: Remember earlier when I said that Delphi programmers don't need to worry much about GUIDs? The COM object you just created already has a GUID, as does the type library itself. Delphi creates these GUIDs for you automatically. As I said before, you'll see GUIDs a lot as you work with COM objects, but you don't have to worry about creating them.
When the type library node is selected, the Information pane shows a tab labeled Uses. When you click on this tab you will see a list of type libraries that this type library relies on. In almost all cases, this list will include the OLE Automation library, but it can contain others as well. The exact libraries a particular type library relies on depends on the type and complexity of the COM object the type library describes.
The Text page shows the type library definitions in IDL syntax. IDL is sort of a scripting language used to create binary type library files. You shouldn't change any of the text on this tab unless you know exactly what you are doing. You might, however, refer to the Text page for reference. This is probably of more value to experienced programmers than to beginners.
Other pages might be displayed in the Information pane depending on the type of object selected. For complete details, be sure to read the "Type Library Editor" help topic in the Delphi help.
You will learn more about the Type Library Editor as you work through the rest of the chapter. Now let's get back to creating the COM object.
Before going further, you should save the project again. You didn't realize it, but Delphi created a new unit when you created the COM object in the previous step. Choose File|Save All from the main menu and save the unit as MultiplyU.
Now you are ready to make the COM object do something. Remember, this COM object is incredibly simplistic, so it won't do much, but it will at least do something.
ADDING PROPERTIES
First you will add properties to the COM object. Here are the steps:
Adding a Method
Next, you add a method. Perform these steps:
function DoIt : Integer;
Now that you have added the two properties and methods, it's time to see what Delphi has been doing behind the scenes. Listing 15.1 shows the class's unit as it appears after performing the steps up to this point. (Don't worry if your unit doesn't look exactly like Listing 15.1. My version of Delphi might have added code in a slightly different order than yours.)
unit MultiplyU; interface uses Windows, ActiveX, ComObj, ComTest_TLB; type TMultiply = class(TTypedComObject, IMultiply) protected function DoIt: Integer; stdcall; function Get_X: Integer; stdcall; function Get_Y: Integer; stdcall; procedure Set_X(Value: Integer); stdcall; procedure Set_Y(Value: Integer); stdcall; {Declare IMultiply methods here} end; implementation uses ComServ; function TMultiply.DoIt: Integer; begin end; function TMultiply.Get_X: Integer; begin end; function TMultiply.Get_Y: Integer; begin end; procedure TMultiply.Set_X(Value: Integer); begin end; procedure TMultiply.Set_Y(Value: Integer); begin end; initialization TTypedComObjectFactory.Create(ComServer, TMultiply, Class_Multiply, ciMultiInstance, tmSingle); end.
This is the shell of the COM object. Notice that the TMultiply class is derived from both TTypedComObject and IMultiply. (To C++ programmers, this might look like multiple inheritance. It's not exactly multiple inheritance, but it is similar in some ways.) You haven't seen the IMultiply class yet, but you will a bit later. You must fill out this shell in order to make the COM object do something. You will do that next.
You will now add code to the TMultiply class to make the COM object functional. Perform these steps (refer to Listing 15.2 if necessary):
private FX : Integer; FY : Integer;
Result := FX;
Result := FY;
Result := FX * FY;
FX := Value;
FY := Value;
That's all you need to do. Your code should now look like Listing 15.2.
unit MultiplyU; interface uses Windows, ActiveX, ComObj, ComTest_TLB; type TMultiply = class(TTypedComObject, IMultiply) private FX : Integer; FY : Integer; protected function DoIt: Integer; stdcall; function Get_X: Integer; stdcall; function Get_Y: Integer; stdcall; procedure Set_X(Value: Integer); stdcall; procedure Set_Y(Value: Integer); stdcall; {Declare IMultiply methods here} end; implementation uses ComServ; function TMultiply.DoIt: Integer; begin Result := FX * FY; end; function TMultiply.Get_X: Integer; begin Result := FX; end; function TMultiply.Get_Y: Integer; begin Result := FY; end; procedure TMultiply.Set_X(Value: Integer); begin FX := Value; end;
procedure TMultiply.Set_Y(Value: Integer);
begin FY := Value; end; initialization TTypedComObjectFactory.Create(ComServer, TMultiply, Class_Multiply, ciMultiInstance, tmSingle); end.
Although you were working on the MulitplyU unit, Delphi was busy building the type library and a unit to contain the type library code. The unit has the same name as the project with a trailing _TLB. This project is named ComTest. The full unit name for the type library unit, then, is ComTest_TLB.pas. Listing 15.3 shows this unit as it exists at this point. Remember, your unit might not look exactly like Listing 15.3.
unit ComTest_TLB; // ******************************************************************** // // WARNING // // ------- // // The types declared in this file were generated from data read from a // // Type Library. If this type library is explicitly or indirectly (via // // another type library referring to this type library) reimported, or // // the `Refresh' command of the Type Library Editor activated while // // editing the Type Library, the contents of this file will be // // regenerated and all manual modifications will be lost. // // ******************************************************************** // // PASTLWTR : $Revision: 1.11.1.55 $ // File generated on 6/8/98 7:16:51 PM from Type Library described below. // ******************************************************************** // // Type Lib: D:\Borland\D4\Bin\ComTest.tlb // IID\LCID: {7CDAFB76-FF36-11D1-81F1-0040052A83C4}\0 // Helpfile: // HelpString: ComTest Library // Version: 1.0 // ******************************************************************** // interface uses Windows, ActiveX, Classes, Graphics, OleCtrls, StdVCL; // *********************************************************************// // GUIDS declared in the TypeLibrary. Following prefixes are used: // // Type Libraries : LIBID_xxxx // // CoClasses : CLASS_xxxx // // DISPInterfaces : DIID_xxxx // // Non-DISP interfaces: IID_xxxx // // *********************************************************************// const LIBID_ComTest: TGUID = `{7CDAFB76-FF36-11D1-81F1-0040052A83C4}'; IID_IMultiply: TGUID = `{7CDAFB77-FF36-11D1-81F1-0040052A83C4}'; CLASS_Multiply: TGUID = `{7CDAFB79-FF36-11D1-81F1-0040052A83C4}'; type // *********************************************************************// // Forward declaration of interfaces defined in Type Library // // *********************************************************************// IMultiply = interface; // *********************************************************************// // Declaration of CoClasses defined in Type Library // // (NOTE: Here we map each CoClass to its Default Interface) // // *********************************************************************// Multiply = IMultiply; // *********************************************************************// // Interface: IMultiply // Flags: (0) // GUID: {7CDAFB77-FF36-11D1-81F1-0040052A83C4} // *********************************************************************// IMultiply = interface(IUnknown) [`{7CDAFB77-FF36-11D1-81F1-0040052A83C4}'] function Get_X: Integer; stdcall; procedure Set_X(Value: Integer); stdcall; function Get_Y: Integer; stdcall; procedure Set_Y(Value: Integer); stdcall; function DoIt: Integer; stdcall; end; CoMultiply = class class function Create: IMultiply; class function CreateRemote(const MachineName: string): IMultiply; end; implementation uses ComObj;
class function CoMultiply.Create: IMultiply;
begin Result := CreateComObject(CLASS_Multiply) as IMultiply; end; class function CoMultiply.CreateRemote(const MachineName: string): ÂIMultiply; begin Result := CreateRemoteComObject(MachineName, CLASS_Multiply) as ÂIMultiply; end; end.
Notice that this unit contains the declaration for the IMultiply interface. As you can see, IMultiply is derived from IUnknown. Notice also that this unit contains the coclass Multiply.
It is important to understand that this unit is regenerated each time you compile an ActiveX library project. It is generated from the type library file. Note the warning at the top of the unit. The comments are telling you that any changes you make to this file will be lost the next time the COM object is rebuilt. It really doesn't do any good to modify the type library source file, because it will be regenerated automatically.
Now you are ready to compile the ActiveX library project. This step compiles the COM object and builds the DLL in which the COM object resides. After building the COM object, you can register it. Here are the steps:
Delphi registers the COM object DLL with Windows. After the DLL has been registered, Delphi displays a message box, as shown in Figure 15.4.
FIGURE 15.4. Delphi reporting the COM object successfully registered.
When Windows registers the COM object, it adds information about the object to the Registry. Figure 15.5 shows the Registry entry created when the COM object was registered with Windows.
FIGURE 15.5. The Registry key created when the COM object was registered.
NOTE: Delphi ships with a utility called TREGSVR.EXE that can be used to register an ActiveX control from the command line. To register a control called MYSTUFF.OCX, you would run TREGSVR from a command prompt like this:tregsvr mystuff.ocx
To unregister an ActiveX, use the -u switch as follows:
tregsvr -u mystuff.ocx
Sometimes this is more convenient than loading an ActiveX project in Delphi and registering or unregistering the control from the IDE.
NOTE: In this exercise I had you create a COM object. You could also have used an automation object. An automation object derives from IDispatch rather than IUnknown. IDispatch provides the additional functionality required for a COM object to act as an automation server (an object that can control one application from another).
Your COM object is now ready to use.
A COM object doesn't do you much good if you can't use it. In this step, you create an application that uses the COM object you just created. Follow these steps:
ComObj ComTest_TLB
procedure TForm1.Button1Click(Sender: TObject); var Mult : IMultiply; Res : Integer; begin Mult := CreateComObject(CLASS_Multiply) as IMultiply; if Assigned(Mult) then begin Mult.Set_X (20); Mult.Set_Y (60); Res := Mult.DoIt; Label1.Caption := IntToStr(Res); end; end;
This code first declares a pointer to the IMultiply interface called Mult and an Integer variable to hold the result. Next, the CreateComObject function is called with a parameter of CLASS_Multiply. CLASS_Multiply is a constant that contains the GUID for the COM object class (refer to Listing 15.3).
The return value from CreateComObject is assigned to the Mult pointer. Notice that I use the as operator to cast the return value to an IMultiply pointer. CreateComObject returns an IUnknown pointer, so the as operator is used to cast the IUnknown pointer to an IMultiply pointer.
After the COM object is created, I assign values to the X and Y properties. After that I call the DoIt method of the COM object and display the result in the Label component.
NOTE: In the real world, I would have written the preceding procedure differently. For example:procedure TForm1.Button1Click(Sender: TObject); begin with CreateComObject(CLASS_Multiply) as IMultiply do begin Set_X(20); Set_Y(60); Label1.Caption := IntToStr(DoIt); end; end;
I wrote the procedure the way I did to illustrate each step.
Run the program. When you click the form's button, the label should change to say "1200" (the product of 20 * 60). That's it! Your COM object works. This COM object can be used from Visual Basic, Visual C++, C++Builder, or any other development environment that supports COM.
ActiveX is a relatively new term for a technology that has been around for awhile. Originally ActiveX controls were called OCX controls. The term OCX is still widely used in some circles. An ActiveX control typically has a filename extension of either DLL or OCX.
An ActiveX control is essentially a COM object in disguise. The primary difference between an ActiveX control and a COM object is that an ActiveX control has a design-time interface. An ActiveX control also has code that enables it to be deployed on a Web page or over a network. ActiveX is a subset of COM, so everything you learned about COM objects in the first part of the chapter applies to ActiveX controls as well.
There isn't a lot to know about installing and using third-party ActiveX controls. All you have to do is import the ActiveX into the IDE and begin using the control. To see how this works, let's do a quick exercise. This exercise requires that you have Microsoft Internet Explorer installed on your system. If you don't have Internet Explorer installed, skip this exercise. (You won't be missing anything because I show you how to install an ActiveX control you have built in the section "Build, Register, and Install the Control.") Perform these steps:
FIGURE 15.6. The Import ActiveX dialog box.
Experiment with the new controls and see how they work. You probably won't get very far without documentation, but at least you get a sense for how installing an ActiveX works. (For a more complete explanation of using Internet Explorer as an ActiveX, see the section, "Using Internet Explorer as an ActiveX Control" on the Bonus Day, "Building Internet Applications.")
NOTE: You must have a design-time license in order to use an installed ActiveX control. A design-time license is a file with an .LIC extension. In some cases you can import ActiveX controls to the Delphi Component palette without a design-time license, but you will get an error message when you attempt to place the control on your form.
To remove the Internet Explorer controls from the Component palette, choose Component|Install Packages from the main menu. Locate the Internet Explorer Package in the Design packages list box and click the Remove button. The ActiveXTest tab is removed from the Component palette.
NOTE: Deploying an application that uses ActiveX controls requires special attention. Deploying applications using ActiveX controls is covered in detail on the Bonus Day in the section "Deploying Internet Applications."
There are two ways to create an ActiveX control in Delphi:
In this section, you create an ActiveX control using both of these methods.
Creating an ActiveX control from an existing VCL component is downright simple. After you create a component, you can turn it into an ActiveX control in no time at all. I haven't talked about creating components yet, so I don't want to go into a lot of detail on creating components now (covered on Day 20, "Creating Components"). What you will do, then, is create an ActiveX control from one of the VCL components provided by Borland.
Generate the ActiveX Project with the ActiveX Control Wizard
The first step is to generate the ActiveX project. As always, Delphi does most of the work for you. All you have to do is supply a few fields in the ActiveX Control Wizard. Here are the steps:
FIGURE 15.7. The ActiveX Control Wizard.
Delphi will create a project file (ButtonXControl1.bpr) and three units for the project. The first unit is the TButtonX class unit (ButtonXImp1.pas). The second unit is the type library file for the control, named ButtonXControl1_TLB.pas. This file contains the information Delphi needs to create the type library for the control. The third file, About1.pas, is the unit for the About box. If you want to customize the About box, you can do that at this time. The About box is just another Delphi form, so feel free to customize it in any way you like.
NOTE: Version info is required in order for your ActiveX controls to work in Visual Basic.
Build, Register, and Install the Control
Because you aren't making any modifications to the control itself, you can jump right to building the control and registering it. This is the same process you went through when you registered the COM object you created earlier. An extra step is required when implementing ActiveX controls, though, because ActiveX controls have a design-time interface. Try this:
Test the ActiveX Control
Now you can test your new ActiveX control. First, create a new project.
NOTE: When you create a new project, Delphi will prompt you to save the package file (DCLUSR40.DPK) and the ActiveX control project. Whether you save these files is up to you. My intention was to have you create a quick ActiveX. There's really no need to save the files. If, however, you think you might want to save the files to examine them later, save the files.
Now follow these steps:
MessageDlg(`Hey, it works!', mtInformation, [mbOK], 0);
NOTE: The idea behind one-step ActiveX is to take a working VCL component and create an ActiveX control from that component. Most of the time, you won't have to modify the ActiveX code in any way. However, you certainly can modify the ActiveX code after it has been generated by Delphi if you so desire. Be aware, though, that if you regenerate the ActiveX code from your original VCL component, all changes made to the ActiveX source will be lost.
NOTE: You can create ActiveX controls only from windowed VCL controls (controls derived from TWinControl or one of its descendents). The list of VCL controls from which you can build an ActiveX control contains all installed components that specifically meet this criteria.
Unregister the ActiveX Control
After experimenting with your new ActiveX control, you should unregister it so that it doesn't occupy space in the Registry. To unregister the ActiveX control, do this:
Alternatively, you can load the ActiveX project (if you previously saved it) and choose Run|Unregister ActiveX Server from the main menu.
NOTE: If all else fails, you can always locate the ActiveX control in the Registry and delete the key for that control. Use the Registry Editor's find function to find the key (search for the control's name or its GUID). Naturally, you want to be careful when editing the Registry manually.
Creating an ActiveForm is almost as easy as creating an ActiveX from an existing VCL component. Naturally, you can create a complex ActiveX containing many components on a single form. Contrary to what its name implies, however, an ActiveForm can be used to create a simple ActiveX control from scratch (a colored button, for example). In other words, ActiveForms are not only for creating fancy forms with dozens of gadgets. They are for creating single-use ActiveX controls as well.
In this section, you will create an ActiveForm. The ActiveForm will have two edit controls, a label and a button. The button will take the contents of the two edit controls, multiply them together, and display the result in the label. Yes, I know it doesn't require a lot of imagination to stick with the "multiply two numbers" idea, but my goal is to show you how to create an ActiveForm with the minimum amount of code. Keeping the code to a minimum allows you to focus on the ActiveForm creation process without getting bogged down in code.
Create the ActiveForm
Creating an ActiveForm is so easy it's amazing. Follow these steps:
Delphi creates the required units and displays a form.
Create the Form
An ActiveForm form is just a regular form at this stage. You can add controls to the form, add code, and respond to events just like you do for a form that belongs to an application. The one difference is that the title bar on an ActiveForm does not appear on the control itself. It's just there at design time.
In this step, you will add components and code to make the ActiveForm functional. As you work through the steps, it might help to refer to Figure 15.8 later in the chapter, which shows the completed form. I'm going to give you the primary components in the following steps and let you finish the rest on your own. Perform these steps:
procedure TMyFormX.GoButtonClick(Sender: TObject); begin try ResultLbl.Caption := IntToStr( StrToInt(Num1Edit.Text) * StrToInt(Num2Edit.Text)); except on EConvertError do MessageDlg(`Oops! You entered an invalid value.', mtError, [mbOK], 0); end; end;
FIGURE 15.8. The finished ActiveForm.
Build, Register, and Import the ActiveForm
Now you can build, register, and import the ActiveForm. When built, the ActiveForm is like any other ActiveX control. Because you've done this several times now, I'm not going to go over every step. Follow these steps:
The ActiveForm is now installed as an ActiveX control.
Try the ActiveForm
Now it's time to take the ActiveForm for a test drive. This will be fairly simple:
That's all there is to it. With Delphi, great-looking ActiveX controls are a breeze to create! There simply is no better development environment for creating ActiveX controls than Delphi, bar none.
Ultimately you will want to change the bitmap of the ActiveX from the default that Delphi provides to one of your own design. Changing the bitmap requires following these steps:
TActiveFormFactory.Create( ComServer, TActiveFormControl, TMyFormX, Class_MyFormX, 1, { Change this number. } `', OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL, tmApartment);
Alternatively, you can modify the ActiveForm project's .RES file and change the bitmap named 1 to look like you want.
One of the great features of ActiveForms is that you can use them on a Web page. In order to use an ActiveForm on a Web page, you must use the Web Deploy option. Using Web Deploy requires a Web server, so I can't effectively walk you through the Web deployment process. I can, however, give you a little insight into the process. When you choose Web Deploy, Delphi performs two tasks:
The locations of these files is determined by the Web deployment options. Let's look at that next.
Before you can use Web Deploy, you must set the Web deployment options. To set the Web deployment options, choose Project|Web Deployment Options from the main menu. The Web Deployment Options dialog box is displayed, as shown in Figure 15.9.
Figure 15.9. The Web Deployment Options dialog box.
At the bottom of the Web Deployment Options dialog box is a check box labeled Default. Check this box if you want the settings you have specified to be the new defaults for future projects. Most of the time, you will deploy to the same Web site, so you will probably want to set the defaults after you have everything set up just the way you want it.
The Directories and URLs section is where you specify the target location for your ActiveX. The Target dir field is used to specify the directory where Delphi will copy the ActiveX after it is built. This field must be a directory--it cannot be an URL location.
If you are like me, you might not have direct access to the directory where your Web site is located. (TurboPower's Webster Royland keeps access pretty tightly controlled.) If that is the case, you will have to specify a local directory in this field and then later use your Web publishing software to publish the files to your Web site.
The Target URL field is used to specify the page where the ActiveX will reside on the server. This page is used by Delphi when it creates the HTML page that shows the control. For example, the HTML file that Delphi created for me is shown in Listing 15.4. (I had to break a couple of lines because they were too long for the page.)
<HTML> <H1> Delphi 4 ActiveX Test Page </H1><p> You should see your Delphi 4 forms or controls embedded in the form below. <HR><center><P> <OBJECT classid="clsid:52FB5B97-EDA3-11D1-B47B-0040052A81F8" codebase="http://www.home.turbopower.com/~kentr/test/MyFormProj.cab #version=1,0,0,0" width=275 height=175 align=center hspace=0 vspace=0 > </OBJECT> </HTML>
Notice the URL in the codebase statement. This is the path I typed in the Target URL field of the Web Deployment Options dialog box. By the way, you can copy the entire OBJECT tag from the Delphi-generated HTML code directly to your Web page's HTML source when you get ready to officially deploy your ActiveX code.
NOTE: The name of the HTML file created by Delphi is the project name with an extension of .htm.
The HTML dir field of the Web Deployment Options dialog box is used to specify the location where Delphi will place the HTML code it generates (refer to Listing 15.4). As with the Target dir field, if you don't have direct access to your Web site's directories, you will have to specify a local directory and then publish the HTML file to your Web site.
This section is where you specify the global Web deployment options. The Use CAB file compression field determines whether the ActiveX is compressed. Compressing the ActiveX reduces the size of your ActiveX, making downloading the control that much faster. I used CAB compression on the ActiveForm created earlier in the project and the ActiveX size went from 312KB in OCX form to 204KB in CAB form. Windows takes care of automatically decompressing and registering the ActiveX, so there's no real disadvantage to using CAB compression.
The Include file version number indicates whether Delphi should include the version number in the codebase statement (again refer to Listing 15.4). The version tag is optional, so you don't specifically need it. Note, however, that some browsers won't load the ActiveX if the version tag is present (Netscape Navigator with an ActiveX plug-in, for example).
The Auto increment release number will automatically increment the version number in the ActiveX's version info each time the ActiveX is deployed.
The Code sign project option plays an important role in ActiveX deployment. When this option is on, Delphi will code sign the ActiveX. Code signing is the process of attaching a binary signature to a file. This binary signature identifies the company that created the ActiveX, among other information.
Code signing is important because Internet Explorer expects ActiveX controls to be code signed. If Internet Explorer's security level is set to High or Medium, any ActiveX controls that are not code signed will not load. Simply put, if you are going to deploy your ActiveX controls so that the public can use them, they must be code signed.
The Code Signing page of the Web Deployment Options dialog box contains the information needed to code sign the ActiveX. Delphi does not provide the credentials file or the private key needed to code sign files. To obtain a credentials file and private key, you will need to contact Microsoft. For more information, search the Microsoft Web site for the terms "Digital Signing" and "Certificate Authority."
The Deploy required packages and Deploy additional files options are used if you have built your ActiveX with runtime packages or if there are additional files that must ship with your control. If you choose either of these options, you must specify the packages or additional files on the Packages and Additional Files pages of the Web Deployment Options dialog box.
NOTE: When in doubt, click the Help button in the Web Deployment Options dialog box. Delphi help explains each of the pages of this dialog box.
After you set the deployment options, you are ready to deploy your ActiveX. To deploy the ActiveX, simply load the ActiveX project and choose Project|Web Deploy from the Delphi main menu. Delphi will build the ActiveX and deploy it based on the settings in the Web Deployment Options dialog box. If you elected to use CAB compression, Delphi will compress the ActiveX into a CAB file as well. Remember, if you don't have direct access to your Web site's directories, you will have to publish the ActiveX and HTML file to your Web site before you can test the ActiveX.
The act of deploying the ActiveX is trivial--setting the Web deployment options is the hard part. Figure 15.10 shows the example ActiveForm created earlier running on a Web page.
FIGURE 15.10. The test ActiveForm running on a Web page.
NOTE: ActiveX controls have virtually no security restrictions. Be careful when downloading ActiveX controls from unknown (or unverified) sources. When downloaded, an ActiveX control has access to your entire system. Be equally careful when writing your own ActiveX controls. Make absolutely sure that your ActiveX control won't do anything to negatively impact other users' machines.
I won't lie to you--there's a lot more to COM and ActiveX than what is presented here. For example, I didn't talk about OLE. OLE, like ActiveX, is a subset of COM. OLE adds a layer to COM to enable applications to link and embed OLE automation servers in a container application. Still, you learned a great deal about COM and ActiveX today. Most importantly, you found out how to create COM objects, ActiveX controls, and ActiveForms. You also learned a bit about Web Deploy and how to use it to deploy your ActiveX controls on a Web page.
The Workshop contains quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you have learned. You can find the answers to the quiz questions in Appendix A, "Answers to the Quiz Questions."
© Copyright, Macmillan Computer Publishing. All rights reserved.