Hi all, I need to open a word document and perform various operations on it using Word automation with COM objects in C++. I have succeeded in doing everything I set out to do, except for one problem. When I open the document through my code, the MS Word GUI opens as well. This is achieved my calling the "Open" method with only one parameter - the filename.
I know where the problem is. According to the VBA documentation in MS Word 2007, the Open method in the Documents object takes several other optional parameters. The 13th parameter is a "Visible" flag. The problem is that I can't seem to pass anything more than a single parameter to the Open method without getting a "Type mismatch" error. My code is below:
[code]
HRESULT gveMSWordController::Open(const gveString& in_szFilename)
{
VARIANT result;
HRESULT hr;
VARIANT fname;
VariantInit(&fname);
fname.vt = VT_BSTR; // set variant type to BSTR string
fname.bstrVal = SysAllocString(in_szFilename);
// GetDocuments
hr=OLEMethod(DISPATCH_PROPERTYGET, &result, m_pWApp, L"Documents", 0);
m_pDocumentsCollection = result.pdispVal;
// Open Document
VARIANT l_varOptional;
VariantInit(&l_varOptional);
l_varOptional.vt = VT_ERROR;
l_varOptional.lVal = (long)DISP_E_PARAMNOTFOUND;
VARIANT l_varFalse;
VariantInit(&l_varFalse);
l_varFalse.vt = VT_BOOL;
l_varFalse.boolVal = FALSE;
//hr=OLEMethod(DISPATCH_METHOD, &result, m_pDocumentsCollection, L"Open", 1, fname);
hr=OLEMethod(DISPATCH_METHOD, &result, m_pDocumentsCollection, L"Open", 2, fname, l_varFalse/*, l_varFalse, l_varFalse, l_varOptional, l_varOptional, l_varFalse, l_varOptional, l_varOptional, l_varOptional, l_varOptional, l_varOptional, l_varFalse*/);
m_pDocument = result.pdispVal;
return hr;
}
HRESULT gveMSWordController::OLEMethod(int nType, VARIANT *pvResult, IDispatch *pDisp,LPOLESTR ptName, int cArgs...)
{
if(!pDisp) return E_FAIL;
VariantInit(pvResult);
va_list marker;
va_start(marker, cArgs);
DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
char szName[200];
// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);
// Get DISPID for name passed...
HRESULT hr= pDisp->GetIDsOfNames(IID_NULL, &ptName, 1,
LOCALE_USER_DEFAULT, &dispID);
if(FAILED(hr)) {
return hr;
}
// Allocate memory for arguments...
VARIANT *pArgs = new VARIANT[cArgs+1];
// Extract arguments...
for(int i=0; iInvoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
nType, &dp, pvResult, NULL, NULL);
if(FAILED(hr)) {
return hr;
}
// End variable-argument section...
va_end(marker);
delete [] pArgs;
return hr;
}
[/code]
any help would be greatly appreciated. Thanks!
Comments
[link=http://support.microsoft.com/kb/196776]http://support.microsoft.com/kb/196776[/link]
omitted optional arguments should be initialized as:
[code]VARIANT varOpt;
varOpt.vt = VT_ERROR;
varOpt.scode = DISP_E_PARAMNOTFOUND;[/code]
that is, DISP_E_PARAMNOTFOUND should be assigned to varOpt.scode
One other thing. Your OLEMethod seems to be based on AutoWrap in
[link=http://support.microsoft.com/kb/238393]http://support.microsoft.com/kb/238393[/link]
and there's a note there which I too missed at first:
[color=Blue]Therefore, when you use call AutoWrap to invoke a function with more than one argument, you must pass them in reverse order as illustrated in the sample with the call to invoke DocumentProperties::Add:
[/color]
Oh and the Visible argument seems to be the 12th, not 13th.
hope that solves your problem
I can successfully open a word document when passing 1 argument
But I cannot open a word document using the full 17 argument call
I reversed the order (see below code)
I believe that the MS documentation is wrong.
Maybe what they say is optional is actually required
What are legal values for some of these parameters
Say for the Format parameter
The MS Documentation says:
Can be one of the following WdOpenFormat constants
Where can I get a C header file listing these constants?
Is there a way to get the actual error message?
IE: you have the wrong number of parameters
The format of parameter X is wrong
Calling the system routine GetLastError() doesn't help
I have MS-Word 2002
Please help
Email me for the full sample program
Thanks
VARIANT result;
VariantInit(&result);
VARIANT fname;
VariantInit(&fname);
fname.vt = VT_BSTR;
fname.bstrVal = ::SysAllocString("c:\temp\test.doc");
VARIANT vTrue;
vTrue.vt = VT_BOOL;
vTrue.boolVal = TRUE;
VARIANT vFalse;
vFalse.vt = VT_BOOL;
vFalse.boolVal = FALSE;
VARIANT vOptional;
VariantInit(&vOptional);
vOptional.vt = VT_ERROR;
vOptional.scode = DISP_E_PARAMNOTFOUND;
//hr = AutoWrap1(DISPATCH_METHOD, &result, pDocs, L"Open", fname);
hr = AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 17,
vTrue, // 16 optional NoEncodingDialog
vOptional, // 15 ??? DocumentDirection
vFalse, // 14 optional OpenAndRepair
vOptional, // 13 optional OpenConflictDocument
vFalse, // 12 optional Visible
vOptional, // 11 optional Encoding
vOptional, // 10 Required??? Format
vOptional, // 9 optional Format
vOptional, // 8 optional WritePasswordTemplate
vOptional, // 7 optional WritePasswordDocument
vFalse, // 6 optional Revert
vOptional, // 5 optional PasswordTemplate
vOptional, // 4 optional PasswordDocument
vFalse, // 3 optional AddToRecentFiles
vTrue, // 2 optional ReadOnly
vFalse, // 1 optional ConfirmConversions
fname);
[code]
hr = AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 14,
vFalse, // 14 optional OpenAndRepair
vOptional, // 13 optional OpenConflictDocument
vFalse, // 12 optional Visible
vOptional, // 11 optional Encoding
vOptional, // 10 optional Format
vOptional, // 9 optional WritePasswordTemplate
vOptional, // 8 optional WritePasswordDocument
vFalse, // 7 optional Revert
vOptional, // 6 optional PasswordTemplate
vOptional, // 5 optional PasswordDocument
vFalse, // 4 optional AddToRecentFiles
vTrue, // 3 optional ReadOnly
vFalse, // 2 optional ConfirmConversions
fname);
[/code]
You can find the WdOpenFormat constants in the Word VBA help file, or you can google WdOpenFormat.
Also to get the actual error message you can google the error code. e.g. 0x80010105
But the primary reason why I called the full version of "Open" is so that the file is not added to the Recently Used list. The "Open" routine is correctly NOT adding the file to the MRU.
I noticed that when calling Document->Close that the file is added to the recently used list. The document shows 3 parameters, but no matter what I try, I cannot stop the close routine from adding the file to the MRU list.
What are the proper variants to call "Close" so that the file is not added to the MRU
Thank you
This worked for me:
[code]
AutoWrap(DISPATCH_METHOD, NULL, pDoc, L"SaveAs", 5, vFalse, vOptional, vOptional, vOptional, fname);
VARIANT x;
x.vt = VT_I4;
x.lVal = 0; //wdDoNotSaveChanges
AutoWrap(DISPATCH_METHOD, NULL, pDoc, L"Close", 1, x);
[/code]
Call SaveAs with vFalse for AddToRecentFiles
then close without saving changes.
I am opening pDoc->BuiltinDocumentProperties
and getting each property
I set a breakpoint at the pDoc->Close
The test.doc file is NOT in the MRU
I then step past the pDoc->Close
The test.doc file is then added to the MRU
I actually wrote code to delete the MRU shortcut from the "recent" folder - doesn't work?
Please help
Here's my code. I renamed the document to test4.doc because test.doc was already in the MRU:
[code]
int main(int argc, char* argv[])
{
// 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 fname;
VariantInit(&fname);
fname.vt = VT_BSTR;
fname.bstrVal = ::SysAllocString(L"c:\temp\test4.doc");
VARIANT vTrue;
vTrue.vt = VT_BOOL;
vTrue.boolVal = TRUE;
VARIANT vFalse;
vFalse.vt = VT_BOOL;
vFalse.boolVal = FALSE;
VARIANT vOptional;
vOptional.vt = VT_ERROR;
vOptional.scode = DISP_E_PARAMNOTFOUND;
hr = AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 14,
vFalse, // 14 optional OpenAndRepair
vOptional, // 13 optional OpenConflictDocument
vFalse, // 12 optional Visible
vOptional, // 11 optional Encoding
vOptional, // 10 optional Format
vOptional, // 9 optional WritePasswordTemplate
vOptional, // 8 optional WritePasswordDocument
vOptional, // 7 optional Revert
vOptional, // 6 optional PasswordTemplate
vOptional, // 5 optional PasswordDocument
vFalse, // 4 optional AddToRecentFiles
vOptional, // 3 optional ReadOnly
vOptional, // 2 optional ConfirmConversions
fname);
pDoc = result.pdispVal;
SysFreeString(fname.bstrVal);
}
// Get BuiltinDocumentProperties collection
IDispatch *pProps;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc,
L"BuiltinDocumentProperties", 0);
pProps = result.pdispVal;
}
// Get "Subject" from BuiltInDocumentProperties.Item("Subject")
IDispatch *pPropSubject;
{
VARIANT result;
VariantInit(&result);
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"Subject");
AutoWrap(DISPATCH_PROPERTYGET, &result, pProps, L"Item", 1, x);
pPropSubject = result.pdispVal;
SysFreeString(x.bstrVal);
}
// Get the Value of the Subject property and display it
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pPropSubject, L"Value",
0);
char buf[512];
wcstombs(buf, result.bstrVal, 512);
::MessageBox(NULL, buf, "Subject", 0x10000);
}
// Set the Value of the Subject DocumentProperty
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"This is my subject");
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pPropSubject, L"Value", 1,
x);
::MessageBox(NULL,
"Subject property changed, examine document.",
"Subject", 0x10000);
SysFreeString(x.bstrVal);
}
// Get CustomDocumentProperties collection
IDispatch *pCustomProps;
{
VARIANT result;
VariantInit(&result);
AutoWrap(DISPATCH_PROPERTYGET, &result, pDoc,
L"CustomDocumentProperties", 0);
pCustomProps = result.pdispVal;
}
// Add a new property named "CurrentYear"
{
VARIANT parm1, parm2, parm3, parm4;
parm1.vt = VT_BSTR;
parm1.bstrVal = SysAllocString(L"CurrentYear");
parm2.vt = VT_BOOL;
parm2.boolVal = false;
parm3.vt = VT_I4;
parm3.lVal = 1; //msoPropertyTypeNumber = 1
parm4.vt = VT_I4;
parm4.lVal = 1999;
AutoWrap(DISPATCH_METHOD, NULL, pCustomProps, L"Add", 4, parm4,
parm3, parm2, parm1);
::MessageBox(NULL, "Custom property added, examine document.",
"Custom Property", 0x10000);
SysFreeString(parm1.bstrVal);
}
// Get the custom property "CurrentYear" and delete it
IDispatch *pCustomProp;
{
VARIANT result;
VariantInit(&result);
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = ::SysAllocString(L"CurrentYear");
AutoWrap(DISPATCH_PROPERTYGET, &result, pCustomProps, L"Item",
1, x);
pCustomProp = result.pdispVal;
SysFreeString(x.bstrVal);
AutoWrap(DISPATCH_METHOD, NULL, pCustomProp, L"Delete", 0);
::MessageBox(NULL,
"Custom property removed, examine document.",
"Custom Property", 0x10000);
}
// Close the document without saving changes and quit Word
{
VARIANT fname;
VariantInit(&fname);
fname.vt = VT_BSTR;
fname.bstrVal = ::SysAllocString(L"c:\temp\test4.doc");
VARIANT vTrue;
vTrue.vt = VT_BOOL;
vTrue.boolVal = TRUE;
VARIANT vFalse;
vFalse.vt = VT_BOOL;
vFalse.boolVal = FALSE;
VARIANT vOptional;
vOptional.vt = VT_ERROR;
vOptional.scode = DISP_E_PARAMNOTFOUND;
AutoWrap(DISPATCH_METHOD, NULL, pDoc, L"SaveAs", 5, vFalse, vOptional, vOptional, vOptional, fname);
VARIANT x;
x.vt = VT_I4;
x.lVal = 0; //wdDoNotSaveChanges
AutoWrap(DISPATCH_METHOD, NULL, pDoc, L"Close", 1, x);
AutoWrap(DISPATCH_METHOD, NULL, pWordApp, L"Quit", 0);
}
// Cleanup
pCustomProp->Release();
pCustomProps->Release();
pPropSubject->Release();
pProps->Release();
pDoc->Release();
pDocs->Release();
pWordApp->Release();
// Uninitialize COM for this thread...
CoUninitialize();
return 0;
}
[/code]
Hope that helps.
I am using Word 2003
I still have the problem of having the file added to the Most Recently Used list after to call to pDoc->Close
I will have to call DeleteFile to delete the shortcut