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 automate embedded Office ActiveX documents with C++


View products that this article applies to.

Summary

Use this step-by-step guide to automate an embedded Microsoft Office documents. The procedure that follows uses a VC++ 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.

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.

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 then 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 that contains the legend Type Here. Type AutomateExcel as the caption.
  7. Right-click the newly-captioned CommandBarButton, and then click Add Event Handler 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 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(TEXT("A1")), COleVariant(TEXT("A1")));
    
          // Fill the range with the string "Hello World".
          oRange.put_Value(COleVariant((long)DISP_E_PARAMNOTFOUND, VT_ERROR), COleVariant(TEXT("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. If you receive compiler errors, see the "Troubleshooting" section.
  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.

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 instead of browsing for the file. For more information, click the following article number 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 more information about this error message, click the following article number 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
  • If you receive a compiler error in Excel.tlh when you build by using Visual C++ 2005 or Visual C++ 2008:
    1. Run "Find in Files" (CTRL+SHIFT+F), and then search for "#import." Comment or delete every line where the EXCEL.EXE import library has been imported by using #import. There will be approximately five instances in the five header files that are created by the type library import wizard.
    2. Open the Stdafx.h source file. After each #include statement, add the following import statements together with the auto_rename modifier to import the Excel type library and its dependencies.
      // mso.dll
      #import "libid:{2df8d04c-5bfa-101b-bde5-00aa0044de52}"  auto_rename
      // vb6ext.olb
      #import "libid:{0002e157-0000-0000-c000-000000000046}"  auto_rename
      // excel.exe
      #import "libid:{00020813-0000-0000-c000-000000000046}"  auto_rename
      

↑ Back to the top


References

For more information, click the following article number to view the article in the Microsoft Knowledge Base:
311546 How to embed and automate an Excel worksheet by using MFC and Visual C++ 2005 or Visual C++ .NET
316207 How to embed and automate a Word document by using C++ .NET and MFC

↑ Back to the top


Keywords: KB316587, kbhowtomaster, kbactivedocs

↑ Back to the top

Article Info
Article ID : 316587
Revision : 6
Created on : 12/12/2003
Published on : 12/12/2003
Exists online : False
Views : 431