Delphi and Word Part II

By: Charles Calvert

Abstract: This article shows how to use interfaces to access Word, Excell and the Internet Explorer from Delphi

Introducing Interfaces

(d)Manipulating HTML in a Browser at Runtime

In IE4 and later, you have virtually complete programmatic control over what is going on inside the TWebBrowser control. In particular, you can access each of the HTML elements in your browser and treat each of them as objects. For instance, you can get hold of a tag, such as a Paragraph tag, and call the methods of the object that represents that tag to change its properties dynamically.

In the ChangeHTML program, you can see how this process works. The program, shown in Figure 17.4 and Listing 17.5, loads a simple HTML file into the TWebBrowser control. It then iterates through all the elements in the HTML file and shows them to you in a combo box. Menu items let you choose options to dynamically change the text in the HTML file or the background color of the file.

***Insert Figure 17.4 19FIG04 PC

Figure 17.4

The ChangeHTML program lets you dynamically change the text and background color in an HTML file.

Listing 17.5[em]The Main Form of the ChangeHTML Program

unit Main;

 

interface

 

uses

Windows, Messages, SysUtils,

Classes, Graphics, Controls,

Forms, Dialogs, StdCtrls,

ExtCtrls, OleCtrls, SHDocVw_TLB,

MSHTML_TLB, Menus;

 

type

TForm1 = class(TForm)

WebBrowser1: TWebBrowser;

Panel1: TPanel;

ViewAllBtn: TButton;

ComboBox1: TComboBox;

Edit1: TEdit;

RefreshBtn: TButton;

OpenDialog1: TOpenDialog;

MainMenu1: TMainMenu;

File1: TMenuItem;

N1: TMenuItem;

Exit1: TMenuItem;

Options1: TMenuItem;

ChangeColor: TMenuItem;

Open1: TMenuItem;

ChangeText1: TMenuItem;

ShowObject1: TMenuItem;

procedure OpenFileBtnClick(Sender: TObject);

procedure ViewAllBtnClick(Sender: TObject);

procedure Edit1KeyDown(Sender: TObject; var Key: Word;

Shift: TShiftState);

procedure ComboBox1Click(Sender: TObject);

procedure RefreshBtnClick(Sender: TObject);

procedure Exit1Click(Sender: TObject);

procedure ChangeColorClick(Sender: TObject);

procedure FormShow(Sender: TObject);

procedure ChangeText1Click(Sender: TObject);

procedure ShowObject1Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

A, B, C, D: OleVariant;

FAll: IHTMLElementCollection;

public

procedure HandleObject(Item: OleVariant);

end;

 

var

Form1: TForm1;

 

implementation

 

uses

DirectAnimation_TLB, CodeBox;

 

{$R *.DFM}

 

procedure TForm1.ChangeColorClick(Sender: TObject);

var

Doc: OleVariant;

begin

Doc := WebBrowser1.Document;

Doc.BGColor := $0000FF;

end;

 

procedure TForm1.ChangeText1Click(Sender: TObject);

var

Item: OleVariant;

Len, i: Integer;

begin

if FAll = nil then

ViewAllBtnClick(nil);

Len := FAll.Length;

for i := 0 to Len - 1 do begin

Item := FAll.Item(i, varEmpty);

if Item.TagName = 'H1' then

Item.InnerText := 'New Text is Shown Here';

end;

end;

 

procedure TForm1.ComboBox1Click(Sender: TObject);

var

Item: OleVariant;

begin

Item := FAll.Item(ComboBox1.ItemIndex, varEmpty);

if Item.TagName = 'H1' then begin

ShowMessage(Item.InnerText);

Item.InnerText := 'New Text is Shown Here';

end else if Item.TagName = 'OBJECT' then

HandleObject(Item)

else

ShowMessage(Item.ClassName + ' Tag: ' + Item.TagName);

end;

 

procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;

Shift: TShiftState);

begin

if Key = 13 then

WebBrowser1.Navigate(Edit1.Text, A, B, C, D);

end;

 

procedure TForm1.Exit1Click(Sender: TObject);

begin

Close;

end;

 

procedure TForm1.FormCreate(Sender: TObject);

const

MCW_EM = DWord($133f);

begin

Set8087CW(MCW_EM);

end;

 

procedure TForm1.FormShow(Sender: TObject);

begin

WebBrowser1.Navigate(GetStartDir + Edit1.Text, A, B, C, D);

end;

 

procedure TForm1.HandleObject(Item: OleVariant);

var

Disp: IDispatch;

I: IDAViewerControl;

M: IDAStatics;

LS: IDALineStyle;

fs: OleVariant;

arc1: IDAImage;

Arc2, Arc3, Arc4, Arc5, Arc6: IDAImage;

MyArray: OleVariant;

begin

if VarType(Item) = varDispatch then

Disp := IDispatch(TVarData(Item).vDispatch)

else

Exit;

 

I := Disp as IDAViewerControl;

m := I.MeterLibrary;

 

// Use the default line style, set to red, to draw the arcs

ls := m.DefaultLineStyle.Color(m.Red);

 

// Use the default font style for the labels

fs := m.DefaultFont;

 

// Example 1

// This is an 180 degree arc that starts at 0 and goes to 180 degrees

arc1 := m.ArcDegrees(0, 180,

0.04, 0.04).Draw(ls).Transform(m.Translate2(-0.06, 0.07));

arc2 := m.ArcDegrees(90, 270,

0.04, 0.04).Draw(ls).Transform(m.Translate2(0.01, 0.07));

arc3 := m.ArcDegrees(90, -90,

0.04, 0.04).Draw(ls).Transform(m.Translate2(0.05, 0.07));

arc4 := m.ArcDegrees(0, -180,

0.04, 0.04).Draw(ls).Transform(m.Translate2(-0.06, 0.01));

arc5 := m.ArcDegrees(0, 360,

0.04, 0.04).Draw(ls).Transform(m.Translate2(0.01, 0.01));

arc6 := m.ArcRadiansAnim(m.DANumber(0), m.DANumber(PI * 2),

m.Mul(m.DANumber(0.04), m.Sin(m.LocalTime)), m.Mul(m.DANumber(0.04),

m.Sin(m.LocalTime))).Draw(ls).Transform(m.Translate2(0.01, -0.04));

 

 

MyArray := VarArrayCreate([0, 5], VarVariant);

MyArray[0] := Arc1;

MyArray[1] := Arc2;

MyArray[2] := Arc3;

MyArray[3] := Arc4;

MyArray[4] := Arc5;

MyArray[5] := Arc6;

 

I.Image := M.OverLayArray(MyArray);

I.UpdateInterval := 0.50;

I.Start;

 

end;

 

procedure TForm1.OpenFileBtnClick(Sender: TObject);

begin

if OpenDialog1.Execute then

WebBrowser1.Navigate(OpenDialog1.FileName, A, B, C, D);

end;

 

procedure TForm1.RefreshBtnClick(Sender: TObject);

begin

WebBrowser1.Refresh;

end;

 

procedure TForm1.ViewAllBtnClick(Sender: TObject);

var

Doc: IHTMLDocument2;

i, Len: Integer;

begin

Doc := WebBrowser1.Document as IHTMLDocument2;

FAll := Doc.All;

Len := FAll.Length;

for i := 0 to Len - 1 do begin

A := FAll.Item(i, varEmpty);

ComboBox1.Items.Add(A.Tagname);

end;

ComboBox1.ItemIndex := 0;

end;

 

procedure TForm1.ShowObject1Click(Sender: TObject);

var

Item: OleVariant;

Len, i: Integer;

begin

if FAll = nil then

ViewAllBtnClick(nil);

Len := FAll.Length;

for i := 0 to Len - 1 do begin

Item := FAll.Item(i, varEmpty);

if Item.TagName = 'OBJECT' then

HandleObject(Item)

end;

end;

 

end.

Consider the following bit of HTML:

<P>A superior vessel takes a long time to complete.</P>

If this code fragment were in an HTML file you were viewing inside your Delphi program, then you could iterate through all the tags in the browser until you found this one tag. Then the TWebBrowser control would allow you to access an object that encapsulates this tag and change the text associated with it:

MyParagraph.InnerHtml := 'Look within, thou art the Buddha.'

After executing this line of Pascal, the text displayed in the HTML file would change accordingly. This does not mean that the actual HTML file will change, but rather the dynamic image of that file as shown in the browser will change so that the string about the Buddha would be shown to the user.

Here is another way to think about what all this information means. You have no doubt heard about dynamic HTML and the promise that it brings to the Web. Well, the technology I'm describing here allows you to do everything that Dynamic HTML can do, except that instead of relying on VBScript or JavaScript, you can harness your own version of IE to use Object Pascal as the engine behind your Web pages.

The following code shows how to access the HTML elements in a Web page:

procedure TForm1.Button2Click(Sender: TObject);

var

Doc: IHTMLDocument2;

i, Len: Integer;

begin

Doc := WebBrowser1.Document as IHTMLDocument2;

FElements := Doc.All;

Len := FElements.Length;

for i := 0 to Len - 1 do begin

A := FElements.Item(i, varEmpty);

ComboBox1.Items.Add(A.tagname);

end;

ComboBox1.ItemIndex := 0;

end;

The first line of code retrieves the Document object from the browser. The Document object is a COM interface belonging to the internal structure of IE4. In other words, using COM, you are able to start calling the methods of objects internal to IE4. This capability does more than just give you dynamic control over the menu in a program. You are actually calling the methods of an object internal to the structure of the program itself!

The next line of code retrieves the All object, which contains all the elements of an HTML file. For instance, the first three elements in the All object represent an HTML tag, a TITLE tag, and a BODY tag. Each of these elements will be wrapped inside a COM object and made available to you to manipulate as you please.

The individual methods of the COM objects for each tag are set forth in the Internet Client SDK, mentioned earlier in this chapter. In this case, the only important element is the tagname property.

A slightly different thing happens in the following method, which singles out one element in the file and changes the text associated with it:

procedure TForm1.ChangeText1Click(Sender: TObject);

var

Item: OleVariant;

Len, i: Integer;

begin

if FAll = nil then

ViewAllBtnClick(nil);

Len := FAll.Length;

for i := 0 to Len - 1 do begin

Item := FAll.Item(i, varEmpty);

if Item.TagName = 'H1' then

Item.InnerText := 'Look within, thou art the Buddha.';

end;

end;

The H1 tag has a property called InnerText that defines the text displayed in the tag. For instance, the following line has the line "That thou mayest seek to know everything" as its InnerText:

<H1>That thou mayest seek to know everything</H1>

This tag has the words "Seek to know nothing" as its InnerText:

<H1>Seek to know nothing.</H1>

The point of this example is that you can use interfaces to find the individual elements of an HTML file and change them to suit you own needs. You will find that this technology is not shallow and that you have the power to make an HTML document do just about anything of which it is capable. In short, it lets you enrich the limited powers of an HTML document with the virtually unlimited resources found in the VCL and the Object Pascal language.

(c)Placing an ActiveX Control Within a Browser

This next program is a bit like one of those Russian dolls that you open to find yet another doll. Inside that doll is yet another and so on. In particular, this example shows how to create a Delphi application that hosts a Web browser that hosts an ActiveX control. The ActiveX control is driven by the Object Pascal code that you write in your program.

The Delphi application in this example is called AnimationViewer; it is shown in Figure 17.5. AnimationViewer contains a TWebBrowser component. Inside the Web browser, you can load an HTML file that hosts a DirectAnimation ActiveX control. AnimationViewer then proceeds to use Delphi interfaces to drive the animation control. The end result is a relatively simple Delphi application that rotates a three-dimensional cube or other shape in a window. When you run this program, you may have to scroll the window to see the cube.

***Insert Figure 17.5 19FIG05 PC

Figure 17.5

The AnimationViewer program features a rotating 3D cube or any other shape you wish to import from an X file.

The AnimationViewer program can draw a series of curves using the Microsoft DirectAnimation control, or it can use the control to load an X file and rotate the object in your browser. X files have an .x extension and contain geometry defining a shape such as a cube, a sailboat, or anything else you can draw with a program such as 3D studio. The DirectX SDK includes programs to import .3DS files to .x files, as discussed in Chapter 28, "More DirectX Technologies."

Listing 17.6[em]The Main Form from the AnimationViewer1 Program

unit Main;

 

{------------------------------------------------------------------------------

When you run this program, you may have to scroll the window

in order to see the cube or certain other shapes that you might

load that are not particularly large.

------------------------------------------------------------------------------}

 

interface

 

uses

Windows, Messages, SysUtils,

Classes, Graphics, Controls,

Forms, Dialogs, Menus,

ExtCtrls, OleCtrls, SHDocVw_TLB,

DirectAnimation_TLB, MSHTML_TLB;

 

type

TForm1 = class(TForm)

WebBrowser1: TWebBrowser;

Panel1: TPanel;

MainMenu1: TMainMenu;

File1: TMenuItem;

N1: TMenuItem;

Exit1: TMenuItem;

Options1: TMenuItem;

Cube1: TMenuItem;

Arcs1: TMenuItem;

OpenDialog1: TOpenDialog;

procedure Cube1Click(Sender: TObject);

procedure FormShow(Sender: TObject);

procedure Arcs1Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

A, B, C, D: OleVariant;

FViewer: IDAViewerControl;

function GetViewer: IDaViewerControl;

public

{ Public declarations }

end;

 

var

Form1: TForm1;

 

implementation

 

uses

CodeBox;

 

{$R *.DFM}

 

procedure TForm1.FormShow(Sender: TObject);

var

FileName: string;

begin

FileName := GetStartDir + 'TestPage.htm'; // In CodeBox

WebBrowser1.Navigate(FileName, A, B, C, D);

end;

 

function TForm1.GetViewer: IDaViewerControl;

var

Doc: IHTMLDocument2;

FElements: IHTMLElementCollection;

HTMLElement: OleVariant;

Len,i: Integer;

begin

Doc := WebBrowser1.Document as IHTMLDocument2; // CoHTMLDocument.Create;

FElements := Doc.All;

 

Len := FElements.Length;

for i := 0 to Len - 1 do begin

HTMLElement := FElements.Item(i, varEmpty);

if HTMLElement.tagName = 'OBJECT' then Break;

end;

 

if VarType(HTMLElement) = varDispatch then

FViewer := IDispatch(HTMLElement) as IDAViewerControl

else

FViewer := nil;

 

Result := FViewer

end;

 

procedure TForm1.Cube1Click(Sender: TObject);

const

RotateSpeed = 50;

var

Camera: IDACamera;

DLight, PLight: IDAGeometry;

FinalGeo, Geo, ACube: IDAGeometry;

RenderedGeo: IDAImage;

MeterLib: IDAStatics;

SRate: IDANumber;

begin

if not OpenDialog1.Execute then Exit;

 

GetViewer;

MeterLib := FViewer.MeterLibrary;

 

// Import the X File. This is a 1-meter cube

geo := MeterLib.ImportGeometry(OpenDialog1.FileName);

 

// Add light.

pLight := MeterLib.PointLight.Transform(MeterLib.Translate3(0,0,4));

dLight := MeterLib.DirectionalLight.Transform(

MeterLib.Rotate3(MeterLib.YVector3, 0.5));

 

// Scale

geo := geo.Transform(MeterLib.Scale3Uniform(0.5));

 

// Create and rotate cube

srate := MeterLib.Mul(MeterLib.LocalTime, MeterLib.DANumber(RotateSpeed));

ACube := geo.Transform(MeterLib.Compose3(MeterLib.Translate3(0, 0, 2),

MeterLib.Rotate3Anim(MeterLib.Vector3(1, 0, 0),

MeterLib.Mul(MeterLib.DANumber(0.04), srate))));

 

finalgeo := ACube;

 

// Add a camera

Camera := MeterLib.PerspectiveCamera(5.5,

4.5).Transform(MeterLib.Scale3(30, 30, 1));

 

// Combine the lights and Cube

Finalgeo := MeterLib.UnionGeometry(Finalgeo, pLight);

 

// Render and display the image.

RenderedGeo := Finalgeo.Render(Camera);

FViewer.Image := MeterLib.Overlay(RenderedGeo,

MeterLib.SolidColorImage(MeterLib.White));

 

// Set the background in case of a non-windowless browser (like IE3).

FViewer.BackgroundImage := MeterLib.SolidColorImage(MeterLib.Blue);

 

// View your work

FViewer.Start;

end;

 

 

procedure TForm1.Arcs1Click(Sender: TObject);

var

I :IDAViewerControl;

M: IDAStatics;

LS: IDALineStyle;

fs: OleVariant;

arc1: IDAImage;

Arc2, Arc3, Arc4, Arc5, Arc6: IDAImage;

MyArray: OleVariant;

 

begin

I := GetViewer;

m := I.MeterLibrary;

 

// Use the default line style, set to red, to draw the arcs

ls := m.DefaultLineStyle.Color(m.Red);

 

// Use the default font style for the labels

fs := m.DefaultFont;

 

// Draw some arcs

arc1 := m.ArcDegrees(0, 180, 0.04,

0.04).Draw(ls).Transform(m.Translate2(-0.06, 0.07));

arc2 := m.ArcDegrees(90, 270,

0.04, 0.04).Draw(ls).Transform(m.Translate2(0.01, 0.07));

arc3 := m.ArcDegrees(90, -90,

0.04, 0.04).Draw(ls).Transform(m.Translate2(0.05, 0.07));

arc4 := m.ArcDegrees(0, -180,

0.04, 0.04).Draw(ls).Transform(m.Translate2(-0.06, 0.01));

arc5 := m.ArcDegrees(0, 360,

0.04, 0.04).Draw(ls).Transform(m.Translate2(0.01, 0.01));

arc6 := m.ArcRadiansAnim(m.DANumber(0),

m.DANumber(PI * 2), m.Mul(m.DANumber(0.04),

m.Sin(m.LocalTime)), m.Mul(m.DANumber(0.04),

m.Sin(m.LocalTime))).Draw(ls).Transform(m.Translate2(0.01, -0.04));

 

MyArray := VarArrayCreate([0, 5], VarVariant);

MyArray[0] := Arc1;

MyArray[1] := Arc2;

MyArray[2] := Arc3;

MyArray[3] := Arc4;

MyArray[4] := Arc5;

MyArray[5] := Arc6;

 

I.Image := M.OverLayArray(MyArray);

// DAViewer.UpdateInterval := 0.50;

I.Start;

end;

 

{------------------------------------------------------------------------------

This method sets up Delphi's floating point exception handling to

conform with Microsoft standards.

------------------------------------------------------------------------------}

 

procedure TForm1.FormCreate(Sender: TObject);

const

MCW_EM = DWord($133f);

begin

Set8087CW(MCW_EM);

end;

 

end.

This program has two menu items: one lets you draw curves on an HTML form; the other lets you rotate a three-dimensional object in the form.

The following is the HTML file displayed in the TWebBrowser object:

<HTML>

<HEAD>

<TITLE>DirectAnimation SDK, JScript sample</TITLE>

</HEAD>

 

<BODY BGCOLOR= WHITE TOPMARGIN=15 LEFTMARGIN=10>

<FONT FACE = "Verdana, Arial, Helvetica" SIZE=2>

 

<DIV ID=controlDiv>

<OBJECT ID="DAViewer" class="classviewer"

STYLE="position:absolute; left:10; top:50;width:800;height:900;z-index: -1"

CLASSID="CLSID:B6FFC24C-7E13-11D0-9B47-00C04FC2F51D">

</OBJECT>

</DIV>

 

</BODY>

</HTML>

The important part of this file is the code called ControlDiv in the DIV section; it loads the DirectAnimationViewer control into the browser. The line that does the real work is the code containing the GUID for the object:

CLASSID="CLSID:B6FFC24C-7E13-11D0-9B47-00C04FC2F51D">

This line allows the TWebBrowser control to retrieve the control and load it into memory. After reading the previous chapters on COM, you should clearly understand why this information is enough to allow the TWebBrowser to accomplish its task.

The key method in the program is the one that iterates through the elements in the HTML file, finds the DirectAnimation viewer, and makes it available to other portions of the DirectAnimation program:

function TForm1.GetViewer: IDaViewerControl;

var

Doc: IHTMLDocument2;

FElements: IHTMLElementCollection;

HTMLElement: OleVariant;

Len,i: Integer;

begin

Doc := WebBrowser1.Document as IHTMLDocument2; // CoHTMLDocument.Create;

FElements := Doc.All;

 

Len := FElements.Length;

for i := 0 to Len - 1 do begin

HTMLElement := FElements.Item(i, varEmpty);

if HTMLElement.tagName = 'OBJECT' then Break;

end;

 

if VarType(HTMLElement) = varDispatch then

FViewer := IDispatch(HTMLElement) as IDAViewerControl

else

FViewer := nil;

 

Result := FViewer

end;

The first lines of this method should be familiar to you from the preceding section of this chapter. The most interesting code, however, is the line that retrieves the DirectAnimation control from the appropriate element of the All collection:

FViewer := IDispatch(HTMLElement) as IDAViewerControl

The elements retrieved from the All collection are just Variants containing a standard IDispatch interface. You can therefore query the interface asking whether it supports the IDAViewControl interface, which is part of the DirectAnimation control. In this case, your call should succeed, and you will get an instance of the interface back. (Recall that the Object Pascal as operator ends up calling QueryInterface and, in this case, passes in the GUID for the IDAViewControl interface as its first parameter. The second parameter is an out parameter containing your FViewer control variable.)

After you have tucked the IDAViewControl interface away in the FViewer variable, you can then safely execute the Cube1Click method:

procedure TForm1.Cube1Click(Sender: TObject);

const

RotateSpeed = 50;

var

Camera: IDACamera;

DLight, PLight: IDAGeometry;

FinalGeo, Geo, ACube: IDAGeometry;

RenderedGeo: IDAImage;

MeterLib: IDAStatics;

SRate: IDANumber;

begin

if not OpenDialog1.Execute then Exit;

 

GetViewer;

MeterLib := FViewer.MeterLibrary;

 

// Import the X File. This is a 1-meter cube

geo := MeterLib.ImportGeometry(OpenDialog1.FileName);

 

// Add light.

pLight := MeterLib.PointLight.Transform(MeterLib.Translate3(0,0,4));

dLight := MeterLib.DirectionalLight.Transform(

MeterLib.Rotate3(MeterLib.YVector3, 0.5));

 

// Scale

geo := geo.Transform(MeterLib.Scale3Uniform(0.5));

 

// Create and rotate cube

srate := MeterLib.Mul(MeterLib.LocalTime, MeterLib.DANumber(RotateSpeed));

ACube := geo.Transform(MeterLib.Compose3(MeterLib.Translate3(0, 0, 2),

MeterLib.Rotate3Anim(MeterLib.Vector3(1, 0, 0),

MeterLib.Mul(MeterLib.DANumber(0.04), srate))));

 

finalgeo := ACube;

 

// Add a camera

Camera := MeterLib.PerspectiveCamera(5.5,

4.5).Transform(MeterLib.Scale3(30, 30, 1));

 

// Combine the lights and Cube

Finalgeo := MeterLib.UnionGeometry(Finalgeo, pLight);

 

// Render and display the image.

RenderedGeo := Finalgeo.Render(Camera);

FViewer.Image := MeterLib.Overlay(RenderedGeo,

MeterLib.SolidColorImage(MeterLib.White));

 

// Set the background in case of a non-windowless browser (like IE3).

FViewer.BackgroundImage := MeterLib.SolidColorImage(MeterLib.Blue);

 

// View your work

FViewer.Start;

end;

This code works with the FViewer object to display a three-dimensional scene to the user. Note that all the code shown here works primarily with real interfaces, though I sometimes call an instance of IDispatch off one of these interfaces:

pLight := MeterLib.PointLight.Transform(MeterLib.Translate3(0,0,4));

Though I would enjoy talking about this code at some length, it is really not germane to the topic at hand. Instead, you need only note that the code you see here is based on one of the standard Microsoft VBScript examples found in the Internet Client SDK. Porting the code over to Delphi was not hard work, and it would have been considerably simpler had I stuck with Variants rather than digging up all the interfaces you see here.

This example illustrates several key points:

[lb] You can use ActiveX controls such as TWebBrowser in your application.

[lb] You can use HTML, DHTML, and related technology in your Delphi applications.

[lb] You can use large chunks of Visual Basic Automation code in your applications with only minor changes to the original source.

[lb] You can tap into the resources of the operating system, such as its Browser and Automation controls and harness them inside a Delphi program.

(c)Summary

We are entering a time when the rich resources of a computer system are all becoming relatively easily accessible in the form of interfaces and ActiveX controls. In this chapter, you learned how to tap into those resources.

I've shown you many different examples of the types of things you can do with automation objects, ActiveX controls, and interfaces. However, even this plethora of examples might not quite give you a sense of the scope of available objects on a typical Windows 98 or NT system.

If you spend some time seeking them out and browsing the Web in search of additional resources, you will find that your entire computer is fast becoming a cornucopia of useful routines and utilities available through automation, interfaces, and ActiveX controls. I've seen people use some pretty crazy excuses as to why they don't want to tap into this vast wealth of good code. As a rule, I think most of those excuses are just nonsense, and there is no reason why COM objects cannot help us all write richer, more interesting programs. Certainly, I find them useful and expect that they shall play an ever increasing role in Windows programming over the next few years.


Server Response from: ETNASC03