HOWTO: Catch Microsoft Word97 Application Events Using VC++
ID: Q183599
|
The information in this article applies to:
-
Microsoft Visual C++, 32-bit Editions, versions 5.0, 6.0
-
Microsoft Word 97 for Windows
SUMMARY
This article demonstrates how to catch Microsoft Word 97 application events
using Microsoft Visual C++. However, the concepts and code in this
article are not specific to Microsoft Word; they are applicable to the
entire suite of Microsoft Office applications, as well as any other
applications that expose events.
MORE INFORMATION
The following steps illustrate how to create an MFC application that
catches the Microsoft Word 97 Application events Startup(),
DocumentChange(), and Quit():
- Create a new dialog box-based application using the MFC AppWizard. Name
your project WordEvents, and accept the default settings.
- Add two buttons to your dialog box and name the buttons "Start and
Setup" and "Quit and Clean Up," respectively.
- Add the following code to a handler for the "Start and Setup" button:
// Check to see if you've already started the server.
if(m_app.m_lpDispatch != NULL) {
AfxMessageBox("Server already started.");
return;
}
char buf[256]; // General purpose buffer.
// Start Automation server.
COleException e;
if(!m_app.CreateDispatch("Word.Application.8", &e)) {
sprintf(buf, "Error on CreateDispatch(): %ld (%08lx)",
e.m_sc, e.m_sc);
AfxMessageBox(buf, MB_SETFOREGROUND);
return;
}
// Make server visible through automation.
// I.e.: Application.Visible = TRUE
DISPID dispID;
unsigned short *ucPtr;
BYTE *parmStr;
ucPtr = L"visible";
m_app.m_lpDispatch->GetIDsOfNames(
IID_NULL, &ucPtr, 1, LOCALE_USER_DEFAULT, &dispID
);
parmStr = (BYTE *)( VTS_VARIANT );
m_app.InvokeHelper(
dispID, DISPATCH_METHOD | DISPATCH_PROPERTYPUT, VT_EMPTY,
NULL, parmStr, &COleVariant((short)TRUE)
);
// Declare the events you want to catch.
// {000209F7-0000-0000-C000-000000000046}
static const GUID IID_IWord8AppEvents =
{0x000209f7,0x000,0x0000,{0xc0,0x00,0x0,0x00,0x00,0x00,0x00,0x46 } };
// Steps for setting up events.
// 1. Get server's IConnectionPointContainer interface.
// 2. Call IConnectionPointContainerFindConnectionPoint()
// to find the event you want to catch.
// 3. Call IConnectionPoint::Advise() with the IUnknown
// interface of your implementation of the events.
HRESULT hr;
// Get server's IConnectionPointContainer interface.
IConnectionPointContainer *pConnPtContainer;
hr = m_app.m_lpDispatch->QueryInterface(
IID_IConnectionPointContainer,
(void **)&pConnPtContainer
);
ASSERT(!FAILED(hr));
// Find connection point for events you're interested in.
hr = pConnPtContainer->FindConnectionPoint(
IID_IWord8AppEvents,
&m_pConnectionPoint
);
ASSERT(!FAILED(hr));
// Get the IUnknown interface of your event implementation.
LPUNKNOWN pUnk = m_myEventSink.GetInterface(&IID_IUnknown);
ASSERT(pUnk);
// Setup advisory connection!
hr = m_pConnectionPoint->Advise(pUnk, &m_adviseCookie);
ASSERT(!FAILED(hr));
// Release IConnectionPointContainer interface.
pConnPtContainer->Release();
- Add the following code to a handler for the "Quit and Clean Up" button:
// Check if you've started the server.
if(m_app.m_lpDispatch == NULL) {
AfxMessageBox("You haven't started the server yet.");
return;
}
m_pConnectionPoint->Unadvise(m_adviseCookie);
// Tell server to quit.
// Application.Quit()
DISPID dispID; // Temporary DISPID
unsigned short *ucPtr; // Temporary name holder
ucPtr = L"quit";
m_app.m_lpDispatch->GetIDsOfNames(
IID_NULL, &ucPtr, 1, LOCALE_USER_DEFAULT, &dispID
);
m_app.InvokeHelper(dispID, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
// Release application object.
m_app.ReleaseDispatch();
- Start the MFC ClassWizard (CTRL+W), and add a new class derived from
CCmdTarget and with automation support (check the "Automation" option).
Name this class MyEventSink; it will be our implementation of Microsoft
Word's Application events.
- In the MFC ClassWizard, click the Automation tab and add these three
methods in order:
void Startup()
void Quit()
void DocumentChange()
- In MyEventSink.cpp, implement these new methods to display message boxes
when they are called to let you know when they are triggered:
void MyEventSink::Startup()
{
AfxMessageBox("MyEventSink::Startup() called.");
}
void MyEventSink::Quit()
{
AfxMessageBox("MyEventSink::Quit() called.");
}
void MyEventSink::DocumentChange()
{
AfxMessageBox("MyEventSink::DocumentChange() called.");
}
- Open your MyEventSink.cpp file and find the declaration for
IID_IMyEventSink. The ClassWizard generated a new random GUID for your
interface, but because you are implementing a specific interface that
already has a GUID, you need to change yours to match. Modify the
declaration for IID_IMyEventSink as follows:
static const GUID IID_IMyEventSink =
{0x000209f7,0x000,0x0000,{0xc0,0x00,0x0,0x00,0x00,0x00,0x00,0x46}};
- Add the following public member variables to your WordEventsDlg class in
WordEventsDlg.h:
COleDispatchDriver m_app;
IConnectionPoint *m_pConnectionPoint;
DWORD m_adviseCookie;
MyEventSink m_myEventSink;
- Add the following line to WordEventsDlg.h right before the class
CWordEventsDlg declaration:
#include "MyEventSink.h"
- Open the file MyEventSink.h and find the declaration of the destructor;
it will appear as follows:
// Implementation
protected:
virtual ~MyEventSink();
- Move that declaration above the word "Protected" so that the lines of
code appear as follows:
virtual ~MyEventSink();
// Implementation
protected:
// virtual ~MyEventSink(); // Or this line may be removed.
- Finally, make sure the OLE/COM libraries get a chance to initialize.
Add the following code right before your "Start and Setup" button
handler. This creates a global class that gets created at application
startup, and destroyed at exit. The constructor and destructor of this
class provide a handy way to perform initialization and cleanup:
// Ole-initialization class.
class OleInitClass {
public:
OleInitClass() {
OleInitialize(NULL);
}
~OleInitClass() {
OleUninitialize();
}
};
// This global class calls OleInitialize() at
// application startup, and calls OleUninitialize()
// at application exit.
OleInitClass g_OleInitClass;
- Compile and run.
After running the application, click the "Start and Setup" button to start
Microsoft Word and set up event notifications. In Microsoft Word, on the
File menu, click New to create a new document. Your DocumentChange() event
should get fired. Open another document, and notice that it also gets fired
when you switch activation from one document to another. You can click the
"Quit and Clean Up" button to stop the event notifications and quit
Microsoft Word, or you can Exit from Microsoft Word (on the File menu,
click Exit) and notice the Quit notification.
You might notice that the Startup event is never triggered. This is because
it fired before you set up the events. Note that there is really no reason
to handle this event, because the application must have been started before
you could call and set Automation methods and properties.
Microsoft Excel supports many interesting and helpful events, and you can
follow the steps here to catch them. However, there are a few points to
remember:
- Use the OLE/COM Object Viewer that comes with Microsoft Visual C++ 5.0
to view the type library of the server you are interested in. To find
the events, open the coclass declarations (usually at the bottom of the
tree); the associated events for each coclass will be listed. When you
click an event interface you can see what events are available, their
DISPIDs, and how they are declared in the view on the right.
- It was not necessary to modify the DISPIDs for our methods in our
MyEventSink class because Microsoft Word's Application events,
Startup(), Quit(), and DocumentChange() have DISPIDs 1, 2, and 3,
respectively. If you create these methods in order, you don't have to
modify them to match the type library because ClassWizard starts at
DISPID 1. However, most events, such as Microsoft Excel's Workbook
events, do not start with DISPID 1. In such cases, you must explicitly
modify the dispatch map in MyEventSink.cpp to match the DISPIDs with the
correct methods.
© Microsoft Corporation 1999, All Rights Reserved. Contributions by Joe Crump, Microsoft Corporation
REFERENCES
For more information about creating sink interfaces, and simplifying the connection process, click on the article number below to view it in the Microsoft Knowledge Base:
Q181845 HOWTO: Create a Sink Interface in MFC-Based COM Client
For more information about how to catch events in Microsoft Excel, click on the article number below to view it in the Microsoft Knowledge Base:
Q186427 HOWTO: Catch Microsoft Excel Application Events Using VC++
For a general example of, and more information about, connection points,
see the Connpts.exe sample described in the following article in the
Microsoft Knowledge Base:
Q152087 SAMPLE: Connpts.exe Implements Connection Points in MFC Apps
Additional query words:
IConnectionPointContainer IConnectionPoint advise IAdviseSink coclass dispinterface sink
Keywords : kbcode kbinterop kbole kbAutomation kbMFC kbVC kbVC500 kbVC600 kbWord kbGrpDSO kbfaq kbOffice2000
Version : WINDOWS:97; winnt:5.0,6.0
Platform : WINDOWS winnt
Issue type : kbhowto
|