Notice: This website is an unofficial Microsoft Knowledge Base (hereinafter KB) archive and is intended to provide a reliable access to deleted content from Microsoft KB. All KB articles are owned by Microsoft Corporation. Read full disclaimer for more details.

How To Use Visual C++ to Access DocumentProperties with Automation


View products that this article applies to.

Summary

This article illustrates how to automate Microsoft Word with Microsoft Visual C++ to retrieve and to manipulate document properties. Although the sample in this article is specifically written to automate Word, the same concepts can be applied to Microsoft Excel and Microsoft PowerPoint as well.

↑ Back to the top


More information

To build a simple Visual C++ 6.0 console application that automates Microsoft Word by using Visual C++, follow these steps:
  1. Start Visual C++ 6.0, and create a new Win32 Console Application that is named AutoWord. Choose a "Hello, World!" application base, and then click Finish.
  2. Open the generated AutoWord.cpp file, and then replace its contents with the following code:
    #include "stdafx.h"
    #include <ole2.h>
    
    // 
    // AutoWrap() - Automation helper function...
    // 
    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[i] = 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(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 x;
                x.vt = VT_BSTR;
                x.bstrVal = ::SysAllocString(L"C:\\Doc1.doc");
    
                AutoWrap(DISPATCH_METHOD, &result, pDocs, L"Open", 1, x);
                pDoc = result.pdispVal;
                SysFreeString(x.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 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
          pCustomProp->Release();
          pCustomProps->Release();
          pPropSubject->Release();
          pProps->Release();
          pDoc->Release();
          pDocs->Release();
          pWordApp->Release();
    
    
          // Uninitialize COM for this thread...
          CoUninitialize();
    
          return 0;
    }
    					
  3. Use Microsoft Word to create a new document, and save the new document as C:\Doc1.doc. Otherwise, the error 0x800A1436 (-2146823114) appears. This error indicates that "the file does not exist" when the code tries to open it.
  4. Compile and run.
The code demonstrates reading and writing both the built-in document properties and the custom document properties. When run, the code displays the value of the built-in Subject property, changes its value to This is my subject, and creates a new custom document property that is named CurrentYear. When you are prompted to Examine Document by the code, switch to Microsoft Word, and then click Properties on the File menu. When done, read through the comments in the code to learn how it works.

Note The purpose of the AutoWrap function in this sample is to wrap the calls for GetIDsOfNames and Invoke to facilitate automation with straight C++. When you create a DISPPARAMS structure for a call to Invoke, the elements are actually passed in reverse order from what the invoked function expects. 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:
      AutoWrap(DISPATCH_METHOD, NULL, pCustomProps, L"Add", 4, parm4,
               parm3, parm2, parm1);
				

↑ Back to the top


References

For additional information about automating Office by using Visual C++, see the following articles in the Microsoft Knowledge Base:
196776 FAQ: Office Automation Using Visual C+
179494 How To Use Automation to Retrieve Built-in Document Properties

↑ Back to the top


Keywords: kbfaq, kbautomation, kbhowto, KB238393

↑ Back to the top

Article Info
Article ID : 238393
Revision : 7
Created on : 7/13/2004
Published on : 7/13/2004
Exists online : False
Views : 750