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 For a Microsoft Visual C++ 6.0 version of this
article, see
193039. IN THIS TASKNote 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:
- 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.
- In the MFC Application Wizard, follow these steps:
- Click Application Type and then select Single Document.
- Click Compound Document Support and then select Container.
- Check Active document container.
- Click Finish to accept the remaining default settings.
- Add interfaces from the Excel object library. To do this,
follow these steps:
- On the Project menu, click Add Class.
- From the list of templates, select MFC Class
From TypeLib and click Open. The Add Class From Typelib Wizard appears.
- 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.
- Add the following interfaces:
_Application
_Workbook
_Worksheet
Range
Worksheets
Click Finish.
- 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. - Double-click Menu to see two menus: IDR_CNTR_INPLACE and IDR_MAINFRAME.
- 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.
- 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". - Click Add and Edit to insert the skeleton handler into the code for the
CAutomateEmbedView.cpp file.
- In Solution Explorer, double-click the AutomateEmbedView.cpp to open the file in the code editor window.
- 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
- Add a new public member function to CAutomateEmbedView in
the AutomateEmbedView.h file:
HRESULT GetDocIDispatch( LPDISPATCH* ppDisp );
- 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;
}
- Compile and run the application.
- On the Container form click Edit and then click Insert Object.
- 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.
- From the container's File menu, click AutomateExcel. The string "Hello World" appears in cell A1.
- On the File menu, click New to clear the worksheet. Do not save the worksheet.
- In the new document, insert an existing Excel Workbook (Create from File).
- On the File menu, click AutomateExcel. "Hello World" appears in cell A1 of the
worksheet.
back to the top
Troubleshooting
back to the top
REFERENCESFor
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: | Major | Last Reviewed: | 12/12/2003 |
---|
Keywords: | kbActiveDocs kbHOWTOmaster KB316587 kbAudDeveloper |
---|
|