Spell Checking from Delphi code using MS Word - Office Automation in Delphi

of 07

What is (OLE) Automation? What is Automation Server? What is Automation Client?

Suppose you are developing an HTML editor like HTML Kit. As like any other textual editor your application should contain some kind of spell checking system. Why buy spell checking components or write them from scratch when you can easily use MS Word?

OLE Automation

Automation is a convention by which one application can control another. The controlling application is referred to as the automation client, and the one being controlled is referred to as the automation server. The client manipulates the server application’s components by accessing those components properties and methods.

Automation (also known as OLE Automation) is a feature that programs use to expose their objects to development tools, macro languages, and other programs that support Automation. For example, Microsoft Outlook may expose objects for sending and receiving e-mail, for scheduling, and for contact and task management.

By using Word Automation (server), we can use Delphi (client) to dynamically create a new document, add some text we want to spell check, and then have Word check the spelling. If we keep Microsoft Word minimized, our users might never know! Thanks to Microsoft Word's OLE interface, we can take a side trip from Delphi and look at ways to cheat when developing our version of Notepad editor :)

There's only one glitch ;) Users of the application need to have Word installed. But don't let this stop you.

Of course, to fully master the use of Automation in your applications, you must have detailed working knowledge of the applications you are integrating - in this case the MS Word.

In order for your "Office" programs to work, the user must own the application that acts like Automation server. In our case MS Word must be installed on the user's machine.

of 07

Connecting to Word: "Hello Word" Early Binding vs. Late Binding

There are several main steps and three main ways to automate Word from Delphi.

Delphi >= 5 - Office XX Server Components

If you are the owner of Delphi version 5 and up, you can use the components located on the Servers tab of the component palette to connect and control the Word. Components like TWordApplication and TWordDocument wrap the interface of Word exposed objects.

Delphi 3,4 - Early Binding

Speaking in terms of Automation, in order for Delphi to access methods and properties exposed by MS Word the Word type library must be installed. Type libraries provide the definitions for all methods and properties that are exposed by an Automation Server.

To use Word's type library in Delphi (version 3 or 4) select the Project | Import Type Library… menu and choose the file msword8.olb located in Microsoft Office's "Office" directory. This will create the file "Word_TLB.pas" which is the object pascal translation of the type library. Include Word_TLB in the uses list of any unit that will be accessing Word properties or methods. Referencing Word methods using the type library is called early binding.

Delphi 2 - Late Binding

To access Word objects without the use of type libraries (Delphi 2) an application can use, so called, late binding. Late binding should be avoided, if possible, since it's much easier and faster to use type libraries - the compiler helps by catching errors in the source. When using late binding Word is declared to be a variable of Variant type. This in particular means than to call methods and access properties you must know what they are.
of 07

Launching (Automating) Word Silently

"Server" Components in Delphi
"Server" Components in Delphi.

The example in this article will use "server" components provided with Delphi. If you have some earlier version of Delphi I suggest you should use early binding with Word type library.

 uses Word_TLB;
WordApp : _Application;
WordDoc : _Document;
VarFalse : OleVariant;
WordApp := CoApplication.Create;
WordDoc := WordApp.Documents.Add(EmptyParam, EmptyParam) ;
spell check code as described
later in this article
WordApp.Quit(VarFalse, EmptyParam, EmptyParam) ;
Many parameters passed to Word methods are defined as optional parameters. When using interfaces (typep libraries), Delphi does not allow you to left out any optional arguments. Delphi provides a variable which can be used for optional parameters that are not being used called EmptyParam.

To automate Word with a Variant variable (late binding) use this code:

 uses ComObj;
WordApp, WordDoc: Variant;
WordApp := CreateOleObject('Word.Application') ;
WordDoc := WordApp.Documents.Add;
spell check code as described
later in this article
When using late binding, Delphi allows you to left out any optional arguments when calling methods (like Quit). You call methods and properties, as long as you know what they are.

The "Easy" Way

As mentioned, newer Delphi version simplify the use of MS Word as an Automation server by wrapping methods and properties into components. Since many parameters passed to Word methods are defined as optional, Delphi overloads these methods and defines several versions with varying numbers of parameters.
of 07

The Spell Check Project - TWordApplication, TWordDocument

The Spell Project at Design-Time
The Spell Project at Design-Time.
To build a spell checking project we'll need two forms: one used to edit the text and the other to see the spelling suggestions... but, let's go from the beginning.

Start Delphi. Create a new project with one blank form (form1, by default). This will be the main form in the spell checking with MS Word project. Add one TMemo (Standard tab) and two TButtons to the form. Add some text to the Memo filling the Lines property. Of course, with some typo errors. Select the Servers tab and add TWordApplication and TWordDocument to the form. Change the name of TWordApplication component from WordApplication1 to WordApp, WordDocument1 to WordDoc.

TWordApplication, TWordDocument

When automating Word, we use properties and methods of the Application object to control or return application­ wide attributes, to control the appearance of the application window, and to get to the rest of the Word object model.

The published property ConnectKind is used to control whether we connect to a newly launched Word instance or to an existing instance that is already running. Set ConnectKind to ckRunningInstance.

When we open or create a file in Word, we create a Document object. A common task when using automating Word is to specify an area in a document and then do something with it, such as insert text and spell check it. An object that represents a contiguous area in a document is called Range.

of 07

The Spell Check Project - Spell Check / Replace

GetSpellingSuggestions at Design-Time
GetSpellingSuggestions at Design-Time.
The idea is to loop through the text in the Memo and parses it into space delimited words. For each word, we call MS Word to spell check it. Word's Automation model contains the SpellingErrors method that lets you check the spelling of text contained in some Range.

Range is defined to contain only the word just parsed out. The SpellingErrors method returns a collection of misspelled words. If this collection contains more that zero words we move on. A call to the GetSpellingSuggestions method, passing in the incorrectly spelled word, fills a SpellingSuggestions collection of suggested replacement words.

We pass this collection to the SpellCheck form. That is the second form in our project.

To add a new form to a project use File|New Form. Let it have the 'frSpellCheck' name. Add three TBitBtn components on this form. Two EditBox-es and one ListBox. Note the three more Labels. The "Not in dictionary" label is "connected" with the edNID edit box. The edNID simply display the misspelled word. The lbSuggestions list box will list the items in SpellingSuggestions collection. The selected spelling suggestion is placed in the edReplaceWith edit box.

The three BitButtons are used to Cancel the spell checking, Ignore the current word and to Change the misspelled word with the one in the edReplaceWith edit box. The BitBtn components ModalResult property is used when referring to what the user has clicked. The "Ignore" button has its ModalResult property set to mrIgnore, "Change" to mrOk and "Cancel" to mrAbort.

The frSpellCheck has one Public string variable called sReplacedWord. This variable returns the text in the edReplaceWith when the user presses the "Change" button.

of 07

Finally: Delphi Source Code

Here goes the parse-and-spell-check procedure:

 procedure TForm1.btnSpellCheckClick (Sender: TObject) ;
var colSpellErrors : ProofreadingErrors;
colSuggestions : SpellingSuggestions;
j : Integer;
StopLoop : Boolean;
itxtLen, itxtStart : Integer;
varFalse : OleVariant;
WordDoc.ConnectTo(WordApp.Documents.Add(EmptyParam, EmptyParam)) ;
//main loop
while not StopLoop do begin
{parse the memo text into words.}
itxtStart := itxtLen + itxtStart;
itxtLen := Pos(' ', Copy(Memo.Text,1+itxtStart, MaxInt)) ;
if itxtLen = 0 then StopLoop := True;
Memo.SelStart := itxtStart;
Memo.SelLength := -1 + itxtLen;
if Memo.SelText = '' then Continue;
WordDoc.Range.Delete(EmptyParam,EmptyParam) ;
WordDoc.Range.Set_Text(Memo.SelText) ;
{call spell check}
colSpellErrors := WordDoc.SpellingErrors;
if colSpellErrors.Count <> 0 then begin
colSuggestions := WordApp.GetSpellingSuggestions (colSpellErrors.Item(1).Get_Text) ;
with frSpellCheck do begin
edNID.text := colSpellErrors.Item(1).Get_Text;
{fill in the list box with suggestions}
for j:= 1 to colSuggestions.Count do
lbSuggestions.Items.Add(VarToStr(colSuggestions.Item(j))) ;
lbSuggestions.ItemIndex := 0;
lbSuggestionsClick(Sender) ;
case frSpellCheck.ModalResult of
mrAbort: Break;
mrIgnore: Continue;
if sReplacedWord <> '' then begin
Memo.SelText := sReplacedWord;
itxtLen := Length(sReplacedWord) ;
WordApp.Quit(varFalse) ;
Memo.SelStart := 0;
Memo.SelLength := 0;
of 07

Thesaurus? Thesaurus!

As a bonus the project has the code to use Word's Thesaurus. Using the thesaurus is quite easier. We don't parse the text, for the selected word the CheckSynonyms method is called. This method displays its own selection dialog. Once a new word is selected, the Word Documents Range contents is used to replace the original word.