Mega Code Archive

 
Categories / Delphi / Ide Indy
 

Delphi and Automation with Word

Title: Delphi and Automation with Word Question: Automation allows one application to control another application. The application being controlled is called an automation server (in our case Word). The application controlling the server is called an automation controller. Answer: Delphi 3 and Automation with Word. Automation allows one application to control another application. The application being controlled is called an automation server (in our case Word). The application controlling the server is called an automation controller. There are two ways that automation servers can be accessed: Late Binding (IDispatch interface) When using this method, function names and parameter datatypes are resolved at runtime, all parameters are passed as variants. As no errors in function names or parameter types are reported at compile time, this method is error prone. As function names and parameter types need to be looked up at runtime, performance is slow. The only advantage of this method for Delphi programming is that it is the only way optional parameters can be omitted from function calls. Early Binding (Using type libraries/interfaces) When using this method, function names and parameter datatypes are all resolved at compile time. A type library needs to be imported into Delphi. A type library is a language neutral description of all the objects and functions exposed by a server. (This is similar in nature to a C header file). All parameters need to be supplied, even when calling functions where the documentation states that some are optional. This enables many errors to be detected and corrected before ever running a program. Performance is better than for late binding. Due to the advantages of the second approach, the rest of the document demonstrates the basics of creating an application with early binding. All applications that use Word Automation should use this technique unless there is a strong justification for not doing so. Preparing the Type Library. A pascal unit needs to be created from the type library file. Select Project|Import Type Library menu item. Press the add button and select the following file c:\program files\microsoft office\office\msword8.olb Select OK. Unfortunately, this leaves the project in a state that will not compile, this is because the word_tlb unit redefines the word application to mean something else. The easiest way to resolve this is to remove the word_tlb unit from the project and only add it to the uses clause of the units that perform automation. Documentation The help file c:\program files\microsoft office\office\vbawrd8.hlp contains information about the objects available for Word. The macro recorder allows VBA code to be created. This can then be taken and translated into Delphi quite easily. Automation Example The following example uses a Delphi wrapper class to hide direct calls to the Word objects. This makes word easier to use in the following ways: Provides paramaters hiding. Allows default parameters to be set for many methods. Many of the word methods also take var paramaters. This means that constants cannot be used, parameter hiding overcomes this. Provides type checking. Many of the methods are defined as taking OLEVariant parameters where integer or other types are appropriate. The following wrapper class demonstrates the key techniques in automating word. The full class in listed in Appendix 1. unit doc; interface uses windows, sysutils, Word_TLB; type TWinWord = class Private App : _Application; public constructor Create; destructor Destroy; override; procedure NewDoc(Template : String); procedure GotoBookmark(Bookmark : String); procedure InsertText(Text : String); procedure SaveAs(Filename : string); end; //------------------------------------------------------------------ implementation //------------------------------------------------------------------ constructor TWinWord.Create; begin App := CoApplication.Create; end; //------------------------------------------------------------------ destructor TWinWord.Destroy; var SaveChanges : OLEVariant; OriginalFormat : OLEVariant; RouteDocument : OLEVariant; begin SaveChanges := wdDoNotSaveChanges; OriginalFormat := unAssigned; RouteDocument := unAssigned; app.Quit(SaveChanges, OriginalFormat, RouteDocument); inherited destroy; end; //------------------------------------------------------------------ procedure TWinWord.GotoBookmark(Bookmark : string); var What : OLEVariant; Which : OLEVariant; Count : OLEVariant; Name : OLEVariant; begin What := wdGoToBookmark; Which := unAssigned; Count := unAssigned; Name := Bookmark; App.Selection.GoTo_(What, Which, Count, Name); end; //------------------------------------------------------------------ procedure TWinWord.InsertText(Text : String); begin App.Selection.TypeText(Text); end; //------------------------------------------------------------------ procedure TWinWord.NewDoc(Template : String); var DocTemplate : OleVariant; NewTemplate : OleVariant; begin DocTemplate := Template; NewTemplate := False; App.Documents.Add(DocTemplate, NewTemplate); end; //------------------------------------------------------------------ procedure TWinWord.SaveAs(Filename : string); begin OLEVariant(App).ActiveDocument.SaveAs(FileName); end; //------------------------------------------------------------------ end. To create the class: Add the type library unit to the uses clause. uses windows, sysutils, Word_TLB; Create the class definition: TWinWord = class Private App : _Application; public procedure NewDoc(Template : String); procedure GotoBookmark(Bookmark : String); procedure InsertText(Text : String); procedure SaveAs(Filename : string); constructor Create; destructor Destroy; override; end; The App variable is a Word application variable reference. This allows the word methods to be called using early binding. The public procedures are the procedures which can be called by the user of the class. Implement the constructor. constructor TWinWord.Create; begin App := CoApplication.Create; end; This is called when an instance of the class TWinWord is created. CoApplication.create creates a new instance of word and returns a reference to the Application interface. This will allow us to call methods of the app object. Implement a destructor destructor TWinWord.Destroy; var SaveChanges : OLEVariant; OriginalFormat : OLEVariant; RouteDocument : OLEVariant; begin SaveChanges := wdDoNotSaveChanges; OriginalFormat := unAssigned; RouteDocument := unAssigned; app.Quit(SaveChanges, OriginalFormat, RouteDocument); inherited destroy; end; The destructor MUST be called. The quit method of the Word application object closes word and frees all the memory allocated by it. As the parameters of the of the quit method are defined as var parameters of type OLEVaraint, storage must be allocated for them. Implement the NewDoc method. This method creates a new word document based on the specified template. procedure TWinWord.NewDoc(Template : String); var DocTemplate : OleVariant; NewTemplate : OleVariant; begin DocTemplate := Template; NewTemplate := False; App.Documents.Add(DocTemplate, NewTemplate); end; This method forces stricter type checking within the Delphi class than is performed within the Word application. The parameter Template must be a valid word template in a string. Words Add method will take any datatype as the parameter. A better example of this can be seen in the MoveRight method in Appendix 1. Impelement the SaveAs method. This method saves the current document as an operating system file. procedure TWinWord.SaveAs(Filename : string); begin OLEVariant(App).ActiveDocument.SaveAs(FileName); end; This example uses late binding. This results in the method name and datatypes not being checked until runtime. Placing any method name will not cause a compile time error. This is useful in this case as the SaveAs method takes 12 parameters and only the filename is mandatory. As this call will be made infrequently, any performance loss from using late binding will be unnoticed. The following code sample demonstrates using this class to create, edit, print and save a document based on a template: Word := TWinWord.create; try Word.visible := true; Word.NewDoc('c:\delphi\word\sample\Demo'); Word.GotoBookmark('From'); Word.InsertText('Graham Marshall'); Word.GotoBookmark('Dept'); Word.InsertText('Development'); Word.GotoBookmark('Phone'); Word.InsertText('111111'); Word.GotoBookmark('Now'); Word.InsertText(FormatDateTime('d-mmm-yyyy', now)); //SF Items Word.GotoBookmark('Items'); Word.InsertText('112021'); Word.MoveRight(1); Word.InsertText('PVCS'); Word.MoveRight(1); Word.InsertText('1'); Word.MoveRight(1); Word.InsertText(' 305.99'); Word.MoveRight(1); Word.InsertText(' 305.99'); Word.MoveRight(1); Word.UpdateFields; Word.RunMacro('Demo'); Word.Print; Word.SaveAs(filename); finally Word.Free; end; Summary Where possible use early binding. Typecast the application object to an OLEVariant to force late binding. Do not include the type library unit within your project. Only add it to the uses clause of units which call automation objects. Keep automation code within a separate unit. Encapsulate calls within a wrapper class. Use the Word macro recorder to prototype your automation code. Use the vbawrd8.hlp file for programming information. Use the unit Word_tlb.pas to check for Delphi parameter types and numbers. If code completion does not seem to be working, press to recompile your code. Base documents on word templates that contain predefined text and formatting and then super-impose data. This is faster and reduces the programming required to create formatted documents. Templates MUST be stored with the application in the application directory. This will remove the possibility of name clashes. Use Bookmarks to define areas for text insertion by the Delphi Application. Ensure that the quit method is called for the Word application object (app.quit). Not calling app.quit will quickly exhaust all windows resources as multiple instances of Word will be created. These may not be visible. It is easy to check for multiple instances of Word by using the NT Task Manager Processes Page (press CTL+ALT+Del to access this.) Appendix A Full TWinWord class source The full source for the tWinWord class is show below. This include the implementation of all methods: unit doc; interface uses Word_TLB, windows, sysutils; Type TWinWord = class Private App : _Application; function fGetVisible : boolean; procedure fSetVisible(visible : boolean); public procedure NewDoc(Template : String); procedure GotoBookmark(Bookmark : String); procedure InsertText(Text : String); procedure MoveRight(Count : integer); procedure Print; procedure UpdateFields; procedure SaveAs(Filename : string); Procedure RunMacro(MacroName : string); constructor Create; destructor Destroy; override; property visible : boolean read fGetVisible write fSetVisible; end; implementation //------------------------------------------------------------------ constructor TWinWord.Create; begin App := CoApplication.Create; end; //------------------------------------------------------------------ destructor TWinWord.Destroy; var SaveChanges : OLEVariant; OriginalFormat : OLEVariant; RouteDocument : OLEVariant; begin SaveChanges := wdDoNotSaveChanges; OriginalFormat := unAssigned; RouteDocument := unAssigned; app.Quit(SaveChanges, OriginalFormat, RouteDocument); inherited destroy; end; //------------------------------------------------------------------ function TWinWord.fGetVisible : boolean; begin result := App.Visible; end; //------------------------------------------------------------------ procedure TWinWord.fSetVisible(Visible : boolean); begin App.visible := Visible; end; //------------------------------------------------------------------ procedure TWinWord.GotoBookmark(Bookmark : string); var What : OLEVariant; Which : OLEVariant; Count : OLEVariant; Name : OLEVariant; begin What := wdGoToBookmark; Which := unAssigned; Count := unAssigned; Name := Bookmark; App.Selection.GoTo_(What, Which, Count, Name); end; //------------------------------------------------------------------ procedure TWinWord.InsertText(Text : String); begin App.Selection.TypeText(Text); end; //------------------------------------------------------------------ procedure TWinWord.NewDoc(Template : String); var DocTemplate : OleVariant; NewTemplate : OleVariant; begin DocTemplate := Template; NewTemplate := False; App.Documents.Add(DocTemplate, NewTemplate); end; //------------------------------------------------------------------ procedure TWinWord.MoveRight(Count : integer); var MoveUnit : OleVariant; vCount : OleVariant; Extended : OleVariant; begin MoveUnit := wdCell; vCount := Count; Extended := unassigned; app.selection.MoveRight(MoveUnit, vCount, Extended); end; //------------------------------------------------------------------ procedure TWinWord.Print; begin OLEVariant(app).Printout; end; //------------------------------------------------------------------ procedure TWinWord.UpdateFields; begin App.ActiveDocument.Fields.Update; end; //------------------------------------------------------------------ procedure TWinWord.SaveAs(Filename : string); begin OLEVariant(App).ActiveDocument.SaveAs(FileName); end; //------------------------------------------------------------------ procedure TWinWord.RunMacro(MacroName : string); begin App.Run(MacroName); end; //------------------------------------------------------------------ end.