Documente Academic
Documente Profesional
Documente Cultură
Contents
Sources of information Samples and components to download How do I ... ? Common problems
Sources of information
Web sites Delphi sites Allan Harkness's Word automation page Borland's papers Chapter 17 of C. Calvert's D4 Unleashed Graham Marshall's Delphi 3 and Word Joel Milne's Word FAQ For catching Word events, or general COM concepts, see also Binh Ly's tutorials Non-Delphi sites Cindy Meister's Word FAQ Jonathan West's WordFAQ MS Visual Basic Programmer's Guide Microsoft Developer's Network Books Charlie Calvert's Delphi 4 Unleashed
Back to top
How do I ... ?
Start Word Close Word Create a new document Open an existing document Close a document Insert text Format text Create and access tables Get document properties Get the cursor position
Code examples are given for Word 97+ first, but Word Basic examples are given in boxes for those using earlier versions of Word.
Back to top
Using the D5 components Using the type library (early binding) Without using the type library (late binding)
Using the D5 components to start Word Starting up Word with the new D5 components is a piece of cake. Here's an example:
WordApplication1.Connect; WordApplication1.Visible := True;
After you've connected, you can use the ConnectTo method of the other Word components to associate them with other Word elements, such as a Word document:
WordDocument1.ConnectTo(WordApplication1.ActiveDocument);
Using the type library (early binding) Before you can use this method, you must have imported the type library (MSWord8.olb for Word 97). One way of starting Word is to try the GetActiveObject call, to get a running instance of Word, but put a call to CoApplication.Create in an except clause. But except clauses are slow, and can cause problems within the IDE for people who like Break On Exceptions set to True. The following code removes the need for a try...except clause, by avoiding using OleCheck on GetActiveObject in the case when Word is not running.
uses ComObj, ActiveX, Word_TLB; // or Word97; or Word2000; for D5 users var Word: _Application; AppWasRunning: boolean; // tells you if you can close Word when you've finished Unknown: IUnknown; Result: HResult; begin AppWasRunning := False; {$IFDEF VER120} // Delphi 4 Result := GetActiveObject(CLASS_Application_, nil, Unknown); if (Result = MK_E_UNAVAILABLE) then Word := CoApplication_.Create {$ELSE} // Delphi 5
Result := GetActiveObject(CLASS_WordApplication, nil, Unknown); if (Result = MK_E_UNAVAILABLE) then Word := CoWordApplication.Create {$ENDIF} else begin { make sure no other error occurred during GetActiveObject } OleCheck(Result); OleCheck(Unknown.QueryInterface(_Application, Word)); AppWasRunning := True; end; Word.Visible := True; ...
Thanks to Allan Harkness for this tip. He has written a comprehensive TWordApp class that uses this method, which you can download from his site.
Without using the type library Automation is so much easier and faster using type libraries (early binding) that you should avoid managing without if at all possible. But if you really can't, here's how to get started:
var Word: Variant; begin try Word := GetActiveOleObject('Word.Application'); except Word := CreateOleObject('Word.Application'); end; Word.Visible := True;
By using GetActiveOleObject, you use an instance of Word that's already running, if there is one.
Back to 'HowDoI..?'
Other possible values for the SaveChanges parameter are wdSaveChanges and wdPromptToSaveChanges - pretty self-explanatory, but if you're using late binding you'll need to define them in your own code like this:
const wdDoNotSaveChanges = $00000000; wdSaveChanges = $FFFFFFFF; wdPromptToSaveChanges = $FFFFFFFE;
The second parameter is used for documents not in Word format. The possible values are wdOriginalDocumentFormat, or wdPromptUser, or wdWordDocument. Again, in late binding, you can declare these yourself:
const wdWordDocument = $00000000; wdOriginalDocumentFormat = $00000001; wdPromptUser = $00000002;
The last parameter should be set to True if you want the document to be routed to the next recipient in line. Word Basic command
Word.FileExit(1);
quits Word, saving any modified file. If you pass 2 as the parameter, files are not saved; if the parameter is 0 or omitted, the user is prompted.
Back to 'HowDoI..?'
If you want the new document to be based on a template other than the Normal template, pass the name (and path) of the template as the first parameter. If you want to open the new document as a template, pass True for the second parameter. In Word 2000, the Documents.Add method has two extra parameters, for the document type, and for specifying whether the document should be visible on screen. But using this method on a Word 97 machine will cause an exception, so if you need your code to
be compatible with Word 97, use Word 2000's Documents.AddOld method. This takes the same parameters as the Word 97 Add method.
If you want the new document to be based on a template other than the Normal template, pass the name (and path) of the template as the first parameter. If you want to open the new document as a template, pass 1 for the second parameter.
Back to 'HowDoI..?'
The optional parameters here include ReadOnly (the third parameter, default False), PasswordDocument (the fifth parameter, pass the password string for the document), and Format (the last parameter, lets you specify the file converter to be used). In Word 2000, the Documents.Open method has two extra parameters, for encoding, and for specifying whether the document should be visible on screen. But using this method on a Word 97 machine will cause an exception, so if you need your code to be compatible with Word 97, use Word 2000's Documents.OpenOld method. This takes the same parameters as the Word 97 Open method.
Look in the WordBasic help file for information on the (numerous) optional parameters for FileOpen.
Back to 'HowDoI..?'
The parameters are the same as for the Word.Close method, but with one unfortunate exception: there's a bug which stops the wdPromptToSaveChanges value working in this method. So if you want the user to be asked whether to save changes, use the ActiveWindow.Close method instead (see Example). Word Basic command
Word.FileClose(1);
To save the file, pass 1 as the first parameter; to close without saving, pass 2; to ask the user whether to save it first you can pass 0, or omit the parameter altogether.
Back to 'HowDoI..?'
If the Application.Options.ReplaceSelection property is True, any text selected will be overwritten by the new text. Word Basic command
Word.Insert('Here is some text');
Back to 'HowDoI..?'
S.TypeParagraph; {Write the next sentence in italic type} S.Font.Italic := integer(True); S.TypeText('Be daring!'); S.Font.Italic := integer(False);
When using the selection object, remember that if the Application.Options.ReplaceSelection property is True, any text selected will be overwritten by the new text.
Back to 'HowDoI..?'
>>>>>How to create and access tables<<<<< You can create tables like this (tested on Word97):
var Doc: _Document; T: Table; begin Doc := Word.ActiveDocument; T := Doc.Tables.Add(Word.Selection.Range, 5, 3); T.Cell(1, 1).Range.Text := 'January'; T.Cell(1, 2).Range.Text := 'February'; T.Cell(1, 3).Range.Text := 'March'; T.Columns.Width := 72; // in points
But using tables is very slow in Word (even worse with Word 2000). If you can you should put the text in first and then convert it to a table at the last possible moment, like this for example:
const Line1 = 'January,February,March'; Line2 = '31,28,31'; Line3 = '31,59,90'; var R: Range; Direction, Separator, Format: OleVariant; begin Doc := Word.ActiveDocument; R := Word.Selection.Range; Direction := wdCollapseEnd; R.Collapse(Direction); R.InsertAfter(Line1); R.InsertParagraphAfter; R.InsertAfter(Line2); R.InsertParagraphAfter; R.InsertAfter(Line3); R.InsertParagraphAfter; Separator := ','; Format := wdTableFormatGrid1;
R.ConvertToTable(Separator, EmptyParam, EmptyParam, EmptyParam, Format, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam); Back to 'HowDoI..?'
>>>>>How to get document properties<<<<< Use variants for this. (So many of the DocumentProperties calls return IDispatch interfaces, rather than the specific interface you want, that trying to do this through early binding is far too complicated to be worth the effort.)
uses Word97; // or Word2000, or Word_TLB for Delphi 4 var Doc: OleVariant; ... Doc := Word.ActiveDocument; Caption := Doc.BuiltInDocumentProperties['Template'].Value; ShowMessage(Doc.BuiltInDocumentProperties[wdPropertyTitle].Value); ShowMessage(Doc.BuiltInDocumentProperties[wdPropertyAuthor].Value);
As you see, properties can be accessed either by name or by one of the wdProperty constants. You can also loop through the BuiltInDocumentProperties collection - but don't do this unless you really have to, because it will cause the word/page count of the document to be recalculated. If the document is of any length, users won't thank you for this. :)
Back to 'HowDoI..?'
>>>>>How to get the current position of the cursor<<<<< The Selection object's information property can tell you this. Here's an example:
VertPos := Word.Selection.Information[wdVerticalPositionRelativeToPage]; HorizPos := Word.Selection.Information[wdHorizontalPositionRelativeToPage];
This code will tell you the vertical position of the selection - which is the cursor (insertion point) if no text is selected. The answer will be in twips, or 1/1440ths of an inch. To get the line number the cursor is on, you'd use
Word.Selection.Information[wdFirstCharacterLineNumber];
instead. Look up the Information property in the VBAWrd help to see all the other things you can find out this way - it's quite a long list.
Back to 'HowDoI..?'