Click here to Skip to main content
15,886,258 members
Articles / Programming Languages / C++
Article

Adding Spell Check and Synonym Info to a Text Editor, using Word Automation

Rate me:
Please Sign up or sign in to vote.
4.72/5 (11 votes)
3 Feb 20053 min read 80.8K   2.8K   43   6
An article on adding spell check and synonym info functionality to an editor using MS Word Automation.

Sample Image - AutoSpellCheck.gif

Introduction

This article explains how the automation objects exposed by the Word Application can be customized to achieve similar functionality. The Spell check, Spelling suggestions and Synonyms suggestions are implemented in this project. The word suggestions are shown in a popup menu when a word is selected and right-clicked. For a correct word, it gives the synonyms list, and for a wrong word, it will give the spelling suggestions. There is a lot of scope for improvement in this application including better handling of errors and additional functionalities, but this is just to illustrate the power and extensibility of automation.

Background

This article is an extension to the article "Creating a Dictionary using Word Automation and Text-to-Speech Control". But the code is made a lot more simpler.

Using the code

The article referred above gives an explanation on how to start with Word Automation. The steps involved in creating an application like this would be:

  1. Create an SDI application using MFC.
  2. Specify CRichEditView as the base class for your CView class.

    Image 2

  3. Import the Word type library.

    Open Class Wizard. In the 'Automation' tab, click 'Add Class' --> 'From Type-Library'. The open file dialog asks for '*.olb' files. You can find it in the 'Microsoft Office directory'. For Office 2000, it is msword9.olb. For older versions, it is msword8.olb. Once you find the Type-Lib, open it and a new window pops up. It asks us to choose the automation objects that Word exposes. If you are not sure which classes to choose then select all and press OK. You can see that two files are being added to the project - Msword9.cpp and Msword9.h (in older versions, it is Msword8.cpp and Msword8.h). Don't forget to include Msword9.h in the file where you declare the Word objects (as in CSpellerView.h for this project).

  4. Add code to invoke methods exposed by the Automation objects. Relevant sections of code are given below.
  5. In order to have a pop-up menu that lists the synonyms/spelling suggestions, we need to handle the Right-Click event in the Rich Edit control.

Creating an Instance of Word Application

if (!oWordApp.CreateDispatch("Word.Application"))
{
    AfxMessageBox("CreateDispatch failed.", MB_OK | MB_SETFOREGROUND);
    exit(1);  
    return;
}

Check for Spelling:

First, we need to separate each word and then invoke the Spell checker.

void CSpellerView::OnSpellCheck()
{
    CString str,strRes;
    ClearUnderline(); //Clear all underlines as this is a fresh search
    m_rich.GetWindowText(str); //Get all the text from the editor
    int iStart = 0,iEnd =0, iLen=0, iStrLen;

    iEnd = str.Find(' ',0);        //find the first space
    iStrLen = str.GetLength();            
    if(iEnd <0 && iStrLen >0)
    {
        iEnd = iStrLen;
        //This is for the special case when there is only one word
    }
    for(int ndx = 0; iEnd > 0 && ndx < iStrLen; ndx ++ )
    {
        strRes = str.Mid(iStart,iEnd-iStart); //Extract the word
        if(!CheckWord(strRes))
        //check for spelling
        {
            Underline(iStart,iEnd); //Underline on error
        }
        iStart = iEnd+1;
        iEnd = str.Find(' ',iStart); //get the next word separated by ' '
    }
    strRes = str.Mid(iStart,iStrLen-1); // get the last word
    if(!CheckWord(strRes))
    //Check word checks the spelling for each word that is separated.
    {
        Underline(iStart,iStrLen);                
    }
}

Now check if the spelling is correct for each word.

Invoke the CheckSpelling() method of the Word Application object passing the string containing the word. All other parameters can be kept optional.

COleVariant vOpt((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
// parameters for OLE calls...

if(oWordApp!=NULL)
{
    if(oWordApp.CheckSpelling(LPCTSTR(str),vOpt,vOpt,vOpt, 
                vOpt,vOpt,vOpt,vOpt,vOpt,vOpt,vOpt,vOpt,vOpt))
        return TRUE;
}

To get the Synonym list:

Invoke the GetSynonymInfo() method. Each word in the list is added to the popup menu also.

BOOL CSpellerView::GetSuggestions(CString argstr)
{

    try{
        
        COleVariant vOpt((long)DISP_E_PARAMNOTFOUND, 
                 VT_ERROR);// parameters for OLE calls...
        
        SynonymInfo* oSin = new SynonymInfo();
        
        oSin->AttachDispatch(oWordApp.GetSynonymInfo(LPCTSTR(argstr),vOpt));
        
        // let's find the synonym list for the first meaning only
        COleVariant vMeaning(short(1));
        COleVariant vSyno=oSin->GetSynonymList(vMeaning);
        //get it...
        
        long index=0;
        if(vSyno.vt==(VT_ARRAY|VT_BSTR))    
        {
            
            CString str;
            int syncount;

            // get the size of the list
            syncount=(vSyno.parray)->cbElements;
            long* ptr;
            HRESULT lResult;    
            if(syncount==0)
            // no synonyms... but the server is the first
            // to know this!!! and it will react
            {
                if(oSin != NULL)
                {
                    delete oSin; //Delete the synonym object
                    oSin = NULL;    
                }
                return FALSE;    
            }            
            // lock the safe-array before extraction
            lResult=SafeArrayLock(vSyno.parray);
            if(lResult)
            {
                if(oSin != NULL)
                {
                    delete oSin; //Delete the synonym object
                    oSin = NULL;    
                }
                return FALSE;
                
            }
            for(int i=0;i<syncount;i++)
            {
                
                ptr=(long*) (vSyno.parray)->pvData;
                // get the pointer to the array data
                
                str=(BSTR)ptr[i];
                // get each meaning
                
                menu.AppendMenu(MF_ENABLED|MF_STRING, 
                    i, LPCTSTR(str)); //Add each to the pop-up menu

            }
            lResult=SafeArrayUnlock(vSyno.parray);
            // done... so unlock
        }

        if(oSin != NULL)
        {
            delete oSin;
            //Delete the synonym object

            oSin = NULL;    
        }
        return TRUE;
    }
    catch(...)
    {
        return FALSE;
    }
}

Similarly, to get the spelling suggestions:

Invoke the GetSpellingSuggestions() method. Please note that it is required to open a document to check for spelling suggestions. Again, each word in the list is added to the popup menu for spelling suggestions.

BOOL CSpellerView::GetCorrections(CString argstr)
{
    
    try{
        // Parameters
        COleVariant vTrue((short)TRUE), vFalse((short)FALSE), 
                  vOpt((long)DISP_E_PARAMNOTFOUND, VT_ERROR);

        SpellingSuggestions oSpells;// The spelling suggestions collection
        SpellingSuggestion oSpell;  // For each spelling 
        
        // A document has to be opened 
        Documents oDocs(oWordApp.GetDocuments());
        _Document oDoc;  // To check the Spelling suggestions

        oDoc = oDocs.Add(vOpt, vOpt, vOpt, vTrue);
        // Add the doc to the documents collection
        
        oSpells = oWordApp.GetSpellingSuggestions((LPCTSTR)argstr, 
                  vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, vOpt, 
                  vOpt, vOpt, vOpt, vOpt, vOpt, vOpt);
                  // Get the Spelling Suggestions

        long lCount = oSpells.GetCount();
        // Get the number of suggestions
        if (lCount > 0)
            {
                for(long i=1; i<= lCount; i++)
                {
                    oSpell = oSpells.Item(i); // Get each suggestion
                    menu.AppendMenu(MF_ENABLED|MF_STRING,i-1, 
                     LPCTSTR(oSpell.GetName()));
                     //Add each word to the pop-up menu
                }
            }
        oDoc.SetSaved(TRUE);     // Set the doc to be saved
        oDoc.Close(vFalse, vOpt, vOpt); // Close the document
        return TRUE;
    }
    catch(...)
    {
        return FALSE;
    }

To handle the pop-up menu:

We need to handle the PreTranslateMessage as it is difficult to track the WM_RBUTTONDOWN method for the Rich Edit control otherwise. Here's the code:

BOOL CSpellerView::PreTranslateMessage(MSG* pMsg) 
{
    if (pMsg->message == WM_RBUTTONDOWN)
    //Handle the right click event in rich edit
        OnSynonym();
        //Get the synonyms for the selected word
    return CView::PreTranslateMessage(pMsg);
}

Also, it is required to process the OnCommand() handler to know when the user selects an item in the popup menu.

BOOL CSpellerView::OnCommand(WPARAM wParam, LPARAM lParam) 
{
    // TODO: Add your specialized code here and/or call the base class
    
        UINT iID = wParam;
        CString str;    
        menu.GetMenuString(iID,str,MF_BYCOMMAND);//Find selection by ID
        if(str.GetLength() > 0)
        {
            CString sel;
            sel = m_rich.GetSelText();  //Get the selected word from the pop-up
            if(sel.Find(' ') >0 )
                str+=" ";
            m_rich.ReplaceSel(LPCTSTR(str),TRUE);
            //Replace the selection in editor

            OnSpellCheck();  //Re-Check the Spelling.
        }

    return CView::OnCommand(wParam, lParam);
}

Rest of the code deals mostly with the pop-up menu, underlining the miss-spelt word, copying the clipboard data etc. I hope this can be easily understood from the code. By the way, I haven't spell checked this article with this application. ;-) !!!

Poetry courtesy: Rabindranath Tagore.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
India India
I'm a nature lover

Comments and Discussions

 
QuestionCan I use this to my c# application? Pin
Billy Pabiona8-Nov-13 18:12
Billy Pabiona8-Nov-13 18:12 
GeneralStrange behavior with MS Word Pin
levaey4-Apr-06 12:33
levaey4-Apr-06 12:33 
GeneralGood Work, However some memory leaks Pin
hw770418-Jul-05 16:51
hw770418-Jul-05 16:51 
QuestionSupport for other languages? Pin
mms_q26-Apr-05 7:47
mms_q26-Apr-05 7:47 
GeneralGREAT JOB Pin
Hillmann-20235-Feb-05 9:32
Hillmann-20235-Feb-05 9:32 
GeneralRe: GREAT JOB Pin
Anonymous7-Feb-05 7:01
Anonymous7-Feb-05 7:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.