Access MS-Word spell-checker from (unmanaged) C++

2019-06-05 06:22发布

I found a few articles on interacting with the MS-Word spell-checker from C# and managed C++ using .NET. (For anyone interested: this and this)

But I couldn't find anything comparable on doing it through standard unmanaged C++ in an MFC app, using COM. I assume the .NET examples are actually using COM meaning it's possible, but is it likely to be horrific and ugly to do?

1条回答
神经病院院长
2楼-- · 2019-06-05 07:11

I did this. It is not that complicated. I packed the whole thing in a DLL and made the suggestions-dialog myself.

Basically it is just opening word and asking it to do a spell-check for a specific single word. If the check failed, you ask for suggestions.

There are only two points where you can fail: 1. a document must be open, that means you always have to create one in code 2. for different word versions, the dictionary sometimes is stored as unicode and sometimes (older versions) not. Of course this is only important if you want to store new words to the user dictionary.

Ok, here's the code:

header file:

#pragma once
#include "msword.h"

class CWord  
{
public:
    CWord();
    bool Initialize();
    void Terminate();
    bool CheckSpelling(CString text);
    bool CorrectSpelling(CString text,CString& corrected,CWnd* pParent,CPoint point);

private:
    _Application m_word;
    COleVariant m_vTrue;
    COleVariant m_vFalse;
    COleVariant m_vOpt;
    bool m_bInit;

    void AddToDictionary(CString text);
    CString OleErrorMsg(COleDispatchException* e);
};

And the implementation (only the most important parts, sorry for the german comments -> I will translate them if needed)

CWord::CWord()
{
    // häufig gebrauchte Variants
    m_vFalse = COleVariant((short) FALSE);
    m_vTrue  = COleVariant((short) TRUE);
    m_vOpt   = COleVariant((long)DISP_E_PARAMNOTFOUND,VT_ERROR);

    m_bInit = false;
}

// sinnvolle Fehlermeldung erstellen
CString CWord::OleErrorMsg(COleDispatchException* e)
{
    CString msg;

    if(!e->m_strSource.IsEmpty())
        msg = e->m_strSource + " - ";

    if(!e->m_strDescription.IsEmpty())
        msg += e->m_strDescription;
    else
        msg += "Unbekannter Fehler.";

    return msg;
}

// Word starten
bool CWord::Initialize()
{
    try
    {
        if(!m_bInit)
        {
            m_word.CreateDispatch("Word.Application");
            m_bInit = true;
        }
    }
    catch(COleDispatchException* e) 
    {   
        AfxMessageBox(OleErrorMsg(e),MB_ICONEXCLAMATION);
        e->Delete();
        return false;
    }

    return true;
}

// Aufräumen
void CWord::Terminate()
{   
    try
    {   
        if(m_word != NULL)  
        {
            m_word.Quit(m_vFalse,m_vOpt,m_vOpt);
            m_word.DetachDispatch();
            m_word = NULL;
            m_bInit = false;
        }
    }
    catch(COleDispatchException* e)
    {
        AfxMessageBox(OleErrorMsg(e),MB_ICONEXCLAMATION);
        e->Delete();
    }
}

// ein Wort auf Rechtschreibung überprüfen
bool CWord::CheckSpelling(CString text)
{
    try
    {
        if(m_word == NULL)
        {
            AfxMessageBox("Word nicht initialisiert!",MB_ICONINFORMATION);
            return false;
        }

        int res = m_word.CheckSpelling((LPCTSTR) text,m_vOpt,m_vFalse,m_vOpt,
            m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt);
        return res != 0;
    }
    catch(COleDispatchException* e)
    {
        AfxMessageBox(OleErrorMsg(e),MB_ICONEXCLAMATION);
        e->Delete();
    }

    return false;
}

// Dialog mit Möglichkeiten anzeigen und Auswahl zurückgeben
bool CWord::CorrectSpelling(CString text,CString& corrected,CWnd* pParent,CPoint /*point*/)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    ASSERT(pParent != NULL);
    bool ret = false;

    CVorschlagDlg dlg(pParent);
    dlg.m_strWort = text;

    try
    {
        // ein Dokument muss geöffnet sein, sonst verweigert GetSpellingSuggestions!
        Documents docs = m_word.GetDocuments();
        _Document doc = docs.Add(m_vOpt,m_vOpt,m_vOpt,m_vTrue);

        // jetzt die Vorschläge holen
        SpellingSuggestions spells = m_word.GetSpellingSuggestions((LPCTSTR) text,m_vOpt,m_vOpt,m_vOpt,
            m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt,m_vOpt);

        // in die Stringlist des Dialogs einfüllen
        for(int i = 1;i <= spells.GetCount();i++)
        {
            SpellingSuggestion ss = spells.Item(i);
            dlg.m_slVorschlaege.AddTail((LPCTSTR) ss.GetName());
        }

        // das Dokument wieder schliessen
        doc.SetSaved(TRUE);
        doc.Close(m_vFalse,m_vOpt,m_vOpt);
    }
    catch(COleDispatchException* e)
    {
        AfxMessageBox(OleErrorMsg(e),MB_ICONEXCLAMATION);
        e->Delete();
        return false;
    } 

    // Dialog öffnen und Ergebnis auswerten
    // ACHTUNG: im Switch fällt das Ergebnis durch bis zu 3 Cases durch!
    switch(dlg.DoModal())
    {
    case IDOK:
        // noch zum Word-Wörterbuch hinzufügen
        AddToDictionary(dlg.m_strWort);

    case IDYES:
    case IDIGNORE:
        corrected = dlg.m_strWort;
        ret = true;
        break;

    default: 
        break;
    } // switch

    return ret;
} 

void CWord::AddToDictionary(CString text)
{
    CString strFilename;
    CStdioFile datei;

    try
    {
        // den Dateinamen herausfinden
        Dictionaries dics = m_word.GetCustomDictionaries();
        Dictionary dic = dics.GetActiveCustomDictionary();
        strFilename = dic.GetPath() + "\\" + dic.GetName();
    }
    catch(COleDispatchException* e)
    {
        AfxMessageBox(OleErrorMsg(e),MB_ICONEXCLAMATION);
        e->Delete();
        return;
    }

    try
    {
        if(!datei.Open(strFilename, CFile::modeReadWrite))
        {
            AfxMessageBox("Fehler beim Öffnen des Wörterbuches!",MB_ICONEXCLAMATION);
            return;
        }

        // herausfinden ob Datei UNICODE - kodiert - für Office 2007
        bool bUnicode = false;
        unsigned char cBOM[2];
        const unsigned char UNICODE_BOM[2] = {unsigned char(0xFF),unsigned char(0xFE)};

        if(datei.GetLength() > 2)
        {
            datei.Read((LPVOID) cBOM,2);
            bUnicode = cBOM[0] == UNICODE_BOM[0] &&  cBOM[1] == UNICODE_BOM[1];
        }

        datei.SeekToEnd();

        if(bUnicode)
        {
            USES_CONVERSION;
            LPCWSTR lpsz = T2W(text);
            datei.Write(lpsz,wcslen(lpsz) * sizeof(WCHAR)); 
        }
        else
        {
            datei.WriteString(text + "\n");
        }

        datei.Close();


        // jetzt noch das CRLF im Unicode nachschreiben
        if(bUnicode)
        {
            using namespace std;
            char crlf[4] = {13,0,10,0};
            //ofstream of(strFilename,ios_base::binary | ios_base::_Openmode::app);
            ofstream of(strFilename,ios_base::binary | ios_base::app);
            of.write(crlf,4);
            of.close();         
        }
    }
    catch(CException* e)
    {
        e->ReportError();
        e->Delete();
    }
}
查看更多
登录 后发表回答