Word Automation using C++ without MFC--SpellingErrors

A

Acheron010

I have a similar question to Andreas from 7/13/2009 who wanted to "copy all
possible misspelled words (wavy red underline) to the end of the document."
Greg Maxey provided a macro that accomplishes this task. The problem is it is
too slow. It takes a couple minutes for a 25 page document with 155 spelling
errors.
Although it does seem faster than my VB an C# versions.

I want to use C++ to speed things up. I can't use MFC because i have the
Express Edition. I am using the framework provided by
http://support.microsoft.com/kb/238393. So what I have is this:

#include <ole2.h>
#include <stdio.h>

HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
LPOLESTR ptName, int cArgs...)
{
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);

if(!pDisp) {
MessageBox(NULL, "NULL IDispatch passed to AutoWrap()",
"Error", 0x10010);
_exit(0);
}

// Variables used...
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;
char buf[200];
char szName[200];

// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);

// Get DISPID for name passed...
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT,
&dispID);
if(FAILED(hr)) {
sprintf(buf,
"IDispatch::GetIDsOfNames(\"%s\") failed w/err0x%08lx",
szName, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}

// Allocate memory for arguments...
VARIANT *pArgs = new VARIANT[cArgs+1];

// Extract arguments...
for(int i=0; i<cArgs; i++) {
pArgs = va_arg(marker, VARIANT);
}

// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;

// Handle special-case for property-puts!
if(autoType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}

// Make the call!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType,
&dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
sprintf(buf,
"IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx",
szName, dispID, hr);
MessageBox(NULL, buf, "AutoWrap()", 0x10010);
_exit(0);
return hr;
}
// End variable-argument section...
va_end(marker);

delete [] pArgs;

return hr;

}

int main(void)
{

// Initialize COM for this thread...
CoInitialize(NULL);

// Get CLSID for Word.Application...
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"Word.Application", &clsid);
if(FAILED(hr)) {
::MessageBox(NULL, "CLSIDFromProgID() failed", "Error",
0x10010);
return -1;
}

// Start Word and get IDispatch...
IDispatch *pWordApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER,
IID_IDispatch, (void **)&pWordApp);
if(FAILED(hr)) {
::MessageBox(NULL, "Word not registered properly",
"Error", 0x10010);
return -2;
}

// Make Word visible
{
VARIANT x;
x.vt = VT_I4;
x.lVal = 1;
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pWordApp, L"Visible", 1,
x);
}

// Get Documents collection
IDispatch *pDocs;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pWordApp, L"Documents",
0);

pDocs = result.pdispVal;
}

// Call Documents.Open() to open C:\Doc1.doc
IDispatch *pDoc;
{
VARIANT result;
VariantInit(&result);
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"c:\\doc.doc");

AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 1, x);
pDoc = result.pdispVal;
SysFreeString(x.bstrVal);
}

// Get ProofreadingErrors collection
IDispatch *pSE;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc, L"SpellingErrors",
0);
pSE = result.pdispVal;
}


// Wait for user...
::MessageBox(NULL, "All done.", "Notice", 0x10000);

// Close the document without saving changes and quit Word
{
VARIANT x;
x.vt = VT_BOOL;
x.boolVal = false;
AutoWrap(DISPATCH_METHOD, NULL, pDoc, L"Close", 1, x);
AutoWrap(DISPATCH_METHOD, NULL, pWordApp, L"Quit", 0);
}

// Cleanup

pDoc->Release();
pDocs->Release();
pWordApp->Release();


// Uninitialize COM for this thread...
CoUninitialize();


return 0;
}

To get the ProofreadingErrors collection which contains the Spelling Errors
Range objects in VB I can use:

Dim pe As Word.ProofreadingErrors
pe = doc.SpellingErrors

Why won't the following work in C++?

IDispatch *pSE;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc, L"SpellingErrors",
0);
pSE = result.pdispVal;
}

Even if I could get the ProofreadingErrors collection how would I access the
individual SpellingErrors objects?

In VB I can use:
Dim spellError as Word.Range
Dim t as String

For each ser in spellError
t = ser.Text
Next

I think I need to use a SAFEARRAY but I am lost as how to use it. I have
studied all the code Microsoft offers on using C++ but for the life of me I
can not figure out how to work with the ProofreadingErrors collection. This
has become one of those problems I am obsessed with figuring out so your help
would be greatly appreciated. Thanks for your time.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top