P
Phillip N Rounds
I'm trying to build an app ( VC++ 6 ) to automate word. The intent is to
have the user, from a menu within the app, initiate Word, write their
document, save it and quit. This app is to trap the word commands and
perform additional work in the background. I am basing my approach on
Microsoft's KB articles #238611, 196776, 183599 and 181745, particularly the
last two. I copied the code from these two KB articles. The only changes I
made to the code were to change the GUID of IWord11AppEvents to match the
GUID of the version of Word I have (
{000209FF-0000-0000-C000-000000000046} ).
The example program crashes at
// Find connection point for events you're interested in.
hr = pConnPtContainer->FindConnectionPoint( IID_IWord11AppEvents,
&m_pConnectionPoint );
ASSERT(!FAILED(hr));
I have a valid pConnPtContainer, so that isn't the problem.
My previous experience with automation is simple loading of data into excel,
so this is somewhat over my head. Any assistance would be appreciated.
Code is as follows:
// This is supposed to start Word, specify the events to catch, and point
these events to the CMySink class
void CMemoWriter::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.
// {000209FF-0000-0000-C000-000000000046}
static const GUID IID_IWord11AppEvents =
{0x000209FF,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));
IEnumConnectionPoints* ppEnum;
// HRESULT hr2 = pConnPtContainer->EnumConnectionPoints( &ppEnum );
// Find connection point for events you're interested in.
hr = pConnPtContainer->FindConnectionPoint(
IID_IWord11AppEvents,
&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();
}
Here is the MySink Class
#include "stdafx.h"
#include "RFQ Distributer.h"
#include "MyEventSink.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// MyEventSink
IMPLEMENT_DYNCREATE(MyEventSink, CCmdTarget)
MyEventSink::MyEventSink()
{
EnableAutomation();
}
MyEventSink::~MyEventSink()
{
}
void MyEventSink::OnFinalRelease()
{
// When the last reference for an automation object is released
// OnFinalRelease is called. The base class will automatically
// deletes the object. Add additional cleanup required for your
// object before calling the base class.
CCmdTarget::OnFinalRelease();
}
BEGIN_MESSAGE_MAP(MyEventSink, CCmdTarget)
//{{AFX_MSG_MAP(MyEventSink)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_DISPATCH_MAP(MyEventSink, CCmdTarget)
//{{AFX_DISPATCH_MAP(MyEventSink)
DISP_FUNCTION(MyEventSink, "Startup", Startup, VT_EMPTY, VTS_NONE)
DISP_FUNCTION(MyEventSink, "Quit", Quit, VT_EMPTY, VTS_NONE)
DISP_FUNCTION(MyEventSink, "DocumentChange", DocumentChange, VT_EMPTY,
VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
// Note: we add support for IID_IMyEventSink to support typesafe binding
// from VBA. This IID must match the GUID that is attached to the
// dispinterface in the .ODL file.
// {FD6BB110-2541-474D-88F5-D64061846531}
static const IID IID_IMyEventSink =
{ 0x000209ff, 0x000, 0x000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x46 } };
BEGIN_INTERFACE_MAP(MyEventSink, CCmdTarget)
INTERFACE_PART(MyEventSink, IID_IMyEventSink, Dispatch)
END_INTERFACE_MAP()
/////////////////////////////////////////////////////////////////////////////
// MyEventSink message handlers
void MyEventSink::Startup()
{
::AfxMessageBox("WOrd Started");
}
void MyEventSink::Quit()
{
::AfxMessageBox("Word QUit");
}
void MyEventSink:ocumentChange()
{
::AfxMessageBox("Document Change");
}
have the user, from a menu within the app, initiate Word, write their
document, save it and quit. This app is to trap the word commands and
perform additional work in the background. I am basing my approach on
Microsoft's KB articles #238611, 196776, 183599 and 181745, particularly the
last two. I copied the code from these two KB articles. The only changes I
made to the code were to change the GUID of IWord11AppEvents to match the
GUID of the version of Word I have (
{000209FF-0000-0000-C000-000000000046} ).
The example program crashes at
// Find connection point for events you're interested in.
hr = pConnPtContainer->FindConnectionPoint( IID_IWord11AppEvents,
&m_pConnectionPoint );
ASSERT(!FAILED(hr));
I have a valid pConnPtContainer, so that isn't the problem.
My previous experience with automation is simple loading of data into excel,
so this is somewhat over my head. Any assistance would be appreciated.
Code is as follows:
// This is supposed to start Word, specify the events to catch, and point
these events to the CMySink class
void CMemoWriter::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.
// {000209FF-0000-0000-C000-000000000046}
static const GUID IID_IWord11AppEvents =
{0x000209FF,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));
IEnumConnectionPoints* ppEnum;
// HRESULT hr2 = pConnPtContainer->EnumConnectionPoints( &ppEnum );
// Find connection point for events you're interested in.
hr = pConnPtContainer->FindConnectionPoint(
IID_IWord11AppEvents,
&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();
}
Here is the MySink Class
#include "stdafx.h"
#include "RFQ Distributer.h"
#include "MyEventSink.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// MyEventSink
IMPLEMENT_DYNCREATE(MyEventSink, CCmdTarget)
MyEventSink::MyEventSink()
{
EnableAutomation();
}
MyEventSink::~MyEventSink()
{
}
void MyEventSink::OnFinalRelease()
{
// When the last reference for an automation object is released
// OnFinalRelease is called. The base class will automatically
// deletes the object. Add additional cleanup required for your
// object before calling the base class.
CCmdTarget::OnFinalRelease();
}
BEGIN_MESSAGE_MAP(MyEventSink, CCmdTarget)
//{{AFX_MSG_MAP(MyEventSink)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_DISPATCH_MAP(MyEventSink, CCmdTarget)
//{{AFX_DISPATCH_MAP(MyEventSink)
DISP_FUNCTION(MyEventSink, "Startup", Startup, VT_EMPTY, VTS_NONE)
DISP_FUNCTION(MyEventSink, "Quit", Quit, VT_EMPTY, VTS_NONE)
DISP_FUNCTION(MyEventSink, "DocumentChange", DocumentChange, VT_EMPTY,
VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
// Note: we add support for IID_IMyEventSink to support typesafe binding
// from VBA. This IID must match the GUID that is attached to the
// dispinterface in the .ODL file.
// {FD6BB110-2541-474D-88F5-D64061846531}
static const IID IID_IMyEventSink =
{ 0x000209ff, 0x000, 0x000, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x46 } };
BEGIN_INTERFACE_MAP(MyEventSink, CCmdTarget)
INTERFACE_PART(MyEventSink, IID_IMyEventSink, Dispatch)
END_INTERFACE_MAP()
/////////////////////////////////////////////////////////////////////////////
// MyEventSink message handlers
void MyEventSink::Startup()
{
::AfxMessageBox("WOrd Started");
}
void MyEventSink::Quit()
{
::AfxMessageBox("Word QUit");
}
void MyEventSink:ocumentChange()
{
::AfxMessageBox("Document Change");
}