P
Phillip N Rounds
Sorry to start a new thread on this topic, but I think I've made sufficient
progress to warrant it.
Anyway, what I am trying to do in this automation project is to have Word
initiated from within a C++ ( VC++ 6) app, trap some user activity and
perform some background functions based on these traps. I've followed MS's
KB183599 with some modifications.
My problem is that when I run the code as is, it works fine. However, the
code as is utilizes the UUID of the dispApplicationEvents interface
( UUID 000209F7-0000-0000-C000-000000000049), which doesn't include some of
the specific events I want to trap ( specifically, the Save function ).
When I change to the UUID of dispApplicationEvents2 interface ( UUID
000209FE-0000-0000-C000-000000000049) I still can't trap the BeforeSave
interface in MyEventSink.
I think I'm missing something simple but fundemantal here. Anyone know what
It is?
Thanks
My code to start Word: // This is exactly as it appears in the KB
article with a few exceptions.
// I have included all of Word from the type library
MSWORD.OLB ( Word 11 )
// m_app is declared as _Application, where _Application
is defined in msword.h
//
void CWordEventsDlg::OnBUTTONStart()
{
// 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.11", &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,0x0000,0x0000,{0xC0,0x00,0x00,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;
m_myEventSink.pParent = (CWnd*) this; // This is different, I have
a field in MyEventSink referencing the calling window so It can post back a
message
// 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();
}
My implimentation of MyEventSink is as follows: Here I have changed some of
the DISP_FUNCTION declarations to DISP_FUNCTION_ID declarations
BEGIN_DISPATCH_MAP(MyEventSink, CCmdTarget)
//{{AFX_DISPATCH_MAP(MyEventSink)
DISP_FUNCTION_ID(MyEventSink, "Quit", 2, Quit, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(MyEventSink, "Save", 8, Save, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(MyEventSink, "Startup", 1, Startup, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(MyEventSink, "DocumentChange", 3, DocumentChange,
VT_EMPTY, VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
void MyEventSink::Startup()
{
TRACE0("\r\nWord Started");
WPARAM wpar = 0;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar); //
SinkReceived = WM_USER + 101 is my MessageID
}
void MyEventSink::Quit()
{
TRACE0("\r\nWord Quit");
WPARAM wpar = 1;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar);
}
void MyEventSink:ocumentChange()
{
TRACE0("\r\nDocument Changed!!!!!!!");
WPARAM wpar = 2;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar);
}
void MyEventSink::Save()
{
WPARAM wpar= 8;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar);
}
void MyEventSink::SaveAs()
{
// TODO: Add your dispatch handler code here
}
progress to warrant it.
Anyway, what I am trying to do in this automation project is to have Word
initiated from within a C++ ( VC++ 6) app, trap some user activity and
perform some background functions based on these traps. I've followed MS's
KB183599 with some modifications.
My problem is that when I run the code as is, it works fine. However, the
code as is utilizes the UUID of the dispApplicationEvents interface
( UUID 000209F7-0000-0000-C000-000000000049), which doesn't include some of
the specific events I want to trap ( specifically, the Save function ).
When I change to the UUID of dispApplicationEvents2 interface ( UUID
000209FE-0000-0000-C000-000000000049) I still can't trap the BeforeSave
interface in MyEventSink.
I think I'm missing something simple but fundemantal here. Anyone know what
It is?
Thanks
My code to start Word: // This is exactly as it appears in the KB
article with a few exceptions.
// I have included all of Word from the type library
MSWORD.OLB ( Word 11 )
// m_app is declared as _Application, where _Application
is defined in msword.h
//
void CWordEventsDlg::OnBUTTONStart()
{
// 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.11", &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,0x0000,0x0000,{0xC0,0x00,0x00,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;
m_myEventSink.pParent = (CWnd*) this; // This is different, I have
a field in MyEventSink referencing the calling window so It can post back a
message
// 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();
}
My implimentation of MyEventSink is as follows: Here I have changed some of
the DISP_FUNCTION declarations to DISP_FUNCTION_ID declarations
BEGIN_DISPATCH_MAP(MyEventSink, CCmdTarget)
//{{AFX_DISPATCH_MAP(MyEventSink)
DISP_FUNCTION_ID(MyEventSink, "Quit", 2, Quit, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(MyEventSink, "Save", 8, Save, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(MyEventSink, "Startup", 1, Startup, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(MyEventSink, "DocumentChange", 3, DocumentChange,
VT_EMPTY, VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
void MyEventSink::Startup()
{
TRACE0("\r\nWord Started");
WPARAM wpar = 0;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar); //
SinkReceived = WM_USER + 101 is my MessageID
}
void MyEventSink::Quit()
{
TRACE0("\r\nWord Quit");
WPARAM wpar = 1;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar);
}
void MyEventSink:ocumentChange()
{
TRACE0("\r\nDocument Changed!!!!!!!");
WPARAM wpar = 2;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar);
}
void MyEventSink::Save()
{
WPARAM wpar= 8;
LPARAM lpar = NULL;
:ostMessage( pParent->m_hWnd, SinkReceived, wpar, lpar);
}
void MyEventSink::SaveAs()
{
// TODO: Add your dispatch handler code here
}