HOW TO: Automate Embedded Office ActiveX Documents with C++ .NET (316587)



The information in this article applies to:

  • Microsoft Excel 2000
  • Microsoft Visual C++ .NET (2002)
  • Microsoft Word 2000
  • Microsoft Excel 2002
  • Microsoft Word 2002
  • Microsoft Visual C++ .NET (2003)

This article was previously published under Q316587
Note Microsoft Visual C++ .NET (2002) supports both the managed code model that is provided by the Microsoft .NET Framework and the unmanaged native Microsoft Windows code model. The information in this article applies only to unmanaged Visual C++ code.

SUMMARY

Use this step-by-step guide to automate an embedded Microsoft Office documents. The procedure that follows uses a VC++ .NET MFC container as an ActiveX Document Container for an activated Excel worksheet. The code includes sample methods for acquiring an IDispatch interface pointer to the document's server and demonstrates how to automate an embedded Excel worksheet.

back to the top

Use Office Applications as ActiveX Document Servers

An ActiveX document container can contain a number of Office documents, but only one document can be displayed at a time. When the Office document is activated, the menu for the Office document's server application merges into the container's menu. An ActiveX document container automatically activates the document being displayed. This varies from conventional OLE document embedding. Conventional embedding or linking requires the end-user to activate the document before the menus are merged.

When a "new" worksheet is embedded in the container it is treated as an ActiveX object. No end-user action is required to merge the Excel menu with the container's menu.

back to the top

Create an MFC Container Application that Automates an ActiveX Document

To generate the sample application that automates an embedded Excel worksheet, follow these steps:
  1. Start Microsoft Visual Studio .NET. On the File menu, point to New, and then click Project. Under Project types, click Visual C++ Projects, and select the MFC Application template. Name the project AutomateEmbed. Save the project to your C:\...root folder.
  2. In the MFC Application Wizard, follow these steps:
    1. Click Application Type and then select Single Document.
    2. Click Compound Document Support and then select Container.
    3. Check Active document container.
    4. Click Finish to accept the remaining default settings.
  3. Add interfaces from the Excel object library. To do this, follow these steps:
    1. On the Project menu, click Add Class.
    2. From the list of templates, select MFC Class From TypeLib and click Open. The Add Class From Typelib Wizard appears.
    3. In the list of available type libraries, locate Microsoft Excel version Object Library, where version is 9.0 for Excel 2000 or 10.0 for Excel 2002.
    4. Add the following interfaces:

      _Application
      _Workbook
      _Worksheet
      Range
      Worksheets

      Click Finish.
  4. In the Solution AutomateEmbed area in Solution Explorer, you will see a tree view that includes the following:

    Source Files
    Header Files
    Resource Files

    Expand the Resource Files node, and double-click AutomateEmbed.RC to open it.
  5. Double-click Menu to see two menus: IDR_CNTR_INPLACE and IDR_MAINFRAME.
  6. Double-click IDR_CNTR_INPLACE. A graphic Menu Designer window opens, showing the File menu. Near the bottom of the File menu is a blank CommandBarButton containing the legend Type Here. Type AutomateExcel as the caption.
  7. Right-click the newly-captioned CommandBarButton and then click Add Event Handler on the shortcut menu to run the Event Handler Wizard. In the wizard, set the following values:
       Set this:                  To this:
       ---------------------------------------------------------
    
       Command Name               ID_FILE_AUTOMATEEXCEL
       Message Type               Command
       Function Handler Name      OnFileAutomateExcel
       Class List                 CAutomateEmbedView
    
    						
    The Handler description will say "Called after menu item or command button has been chosen".
  8. Click Add and Edit to insert the skeleton handler into the code for the CAutomateEmbedView.cpp file.
  9. In Solution Explorer, double-click the AutomateEmbedView.cpp to open the file in the code editor window.
  10. Type or paste the following code at the top of the file:
    // AutomateEmbedView.cpp : implementation of the CAutomateEmbedView class
    // 
    
    #include "stdafx.h"
    #include "AutomateEmbed.h"
    
    #include "AutomateEmbedDoc.h"
    #include "CntrItem.h"
    #include "AutomateEmbedView.h"
    
    #include "CWorkbook.h"
    #include "CWorksheet.h"
    #include "CWorksheets.h"
    #include "CRange.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    // CAutomateEmbedView
    					
  11. Add a new public member function to CAutomateEmbedView in the AutomateEmbedView.h file:
    HRESULT GetDocIDispatch( LPDISPATCH* ppDisp );
    					
  12. At the bottom of the AutomateEmbedView.cpp file, replace the skeleton message handler for CAutomateEmbedView::OnFileAutomateExcel with the following code:
    // CAutomateEmbedView message handlers
    
    void CAutomateEmbedView::OnFileAutomateExcel()
    {
       // Query for the IDispatch pointer for the embedded object.
       // In this case it is Excel worksheet.
       LPDISPATCH lpDisp;
       HRESULT hr = GetDocIDispatch(&lpDisp); // Your own new function.
    
       // If you got an IDispatch, then use it to Automate Excel
       if(SUCCEEDED(hr))
       {
          CWorkbook oBook;
          CWorksheets oSheets;
          CWorksheet oSheet;
          CRange oRange;
    
          // Set_Workbook oBook to use lpDisp, the IDispatch* of the
          // embedded/Embedded workbook.
          oBook.AttachDispatch(lpDisp);
    
          // Then, get the first worksheet in the workbook.
          oSheets = oBook.get_Worksheets();
          oSheet = oSheets.get_Item(COleVariant((long)1));
    
          // Get the Range object corresponding to Cell A1.
          oRange = oSheet.get_Range(COleVariant("A1"), COleVariant("A1"));
    
          // Fill the range with the string "Hello World".
          oRange.put_Value(COleVariant((long)DISP_E_PARAMNOTFOUND, VT_ERROR), COleVariant("Hello World"));
    
          //NOTE: If you are automating Excel 2000 the Range.SetValue requires only one
          // argument. The first parameter in the Excel 2002 syntax in the line above is for the data type, 
          // and is optional. It is not permitted by Excel 2000 or earlier versions of Excel.
    
       } // End if
    } // End of method
    
    /*****************************************************************************
    *                                                                            *
    * GetDocIDispatch - This method determines if the document is embedded       *
    *  or linked, and acquires an IDispatch pointer to the embedded/linked       *
    *  document's server application for use in Automation.                       *
    *  The document must be activated for this method to succeed.                *
    *                                                                            *
    * Parameters: ppDisp = The address of an LPDISPATCH to be filled with        *
    *  the IDispatch pointer of the embedded/linked document's server.           *
    *                                                                            *
    * Returns: S_OK if successful, otherwise an HRESULT reporting the error.     *
    *                                                                            *
    *****************************************************************************/ 
    HRESULT CAutomateEmbedView::GetDocIDispatch(LPDISPATCH* ppDisp)
    {
       //HRESULT hr = S_OK;
       HRESULT hr = E_UNEXPECTED; // If no document then return no ppDisp.
       IOleLink* lpLink = NULL;
       IMoniker* lpMoniker = NULL;
       IRunningObjectTable* lpROT = NULL;
       IUnknown* lpUnk = NULL;
    
       if(!m_pSelection)
       {
          return hr;
       }
    
       // First, try to get an IOleLink interface from the document.
       // If successful, this indicates that the document is linked as
       // opposed to embedded.
       hr = m_pSelection->m_lpObject->QueryInterface(IID_IOleLink, (void**)&lpLink);
    
       if(SUCCEEDED(hr))
       {
          // Get the moniker of the source document for this link.
          // You need this to find the ActiveX Document Server.
          hr = lpLink->GetSourceMoniker(&lpMoniker);
    
          if(SUCCEEDED(hr))
          {
             // For linked documents, search the Running Object Table
             // for the relevant server. Do this through the 
             // IRunningObjectTable interfce, which you can get through
             // an API call.
             hr = GetRunningObjectTable(0,&lpROT);
    
             if(SUCCEEDED(hr))
             {
                // Search the Running Object Table for the ActiveX
                // Document Server of this document. You'll get back an
                // IUnknown pointer to the server.
                hr = lpROT->GetObject( lpMoniker, &lpUnk );
    
                if(SUCCEEDED(hr))
                {
                   // Finally, get the IDispatch pointer from the
                   // IUnknown pointer.
                   hr = lpUnk->QueryInterface(IID_IDispatch, (void**)ppDisp);
                }
             }
          }
       }
       else
       {
          // If that fails, try for a direct IDispatch pointer. This
          // indicates that the document is embedded, not linked.
          hr = m_pSelection->m_lpObject->QueryInterface(IID_IDispatch, (void**)ppDisp);
       }
       // Clean up interface pointers you may have acquired along the way.
       if(lpLink)
          lpLink->Release();
       if(lpMoniker)
          lpMoniker->Release();
       if(lpROT)
          lpROT->Release();
       if(lpUnk)
          lpUnk->Release();
       return hr;
    }
    					
  13. Compile and run the application.
  14. On the Container form click Edit and then click Insert Object.
  15. In the Insert New Object list box, select a new Excel Worksheet. The empty Excel Worksheet appears in the container and the Excel menu merges with the menu of the container.
  16. From the container's File menu, click AutomateExcel. The string "Hello World" appears in cell A1.
  17. On the File menu, click New to clear the worksheet. Do not save the worksheet.
  18. In the new document, insert an existing Excel Workbook (Create from File).
  19. On the File menu, click AutomateExcel. "Hello World" appears in cell A1 of the worksheet.
back to the top

Troubleshooting

  • If you add class wrappers for the Excel object library by using the File option in the Add Class From TypeLib Wizard, and browse the object library, you may receive an error message. To avoid this problem, type the full path and file name for the object library rather than browse for the file.For additional information, click the article number below to view the article in the Microsoft Knowledge Base:

    311408 BUG: 'Read-Only' Warning When Adding an MFC Class From a Type Library

  • If you receive the following error message when you build your sample application, change "Variant DialogBox" in CRange.h to "Variant _DialogBox":
    Warning C4003: Not enough actual parameters for macro 'DialogBoxA'.
    For additional information about this error message, click the article number below to view the article in the Microsoft Knowledge Base:

    311407 BUG: MFC Class Wizard Does Not Resolve Naming Conflicts Between Windows APIs and COM Interface Methods

back to the top

REFERENCES

For additional information, click the article number below to view the article in the Microsoft Knowledge Base:

311546 HOW TO: Embed and Automate an Excel Worksheet by Using C++ .NET and MFC

316207 HOW TO: Embed and Automate a Word Document by Using C++ .NET and MFC

back to the top

Modification Type:MajorLast Reviewed:12/12/2003
Keywords:kbActiveDocs kbHOWTOmaster KB316587 kbAudDeveloper