http://www.ms-news.net/f3295/mfc-event-fired-vbulletin-app-2526796.html
This question is similar to some others I've seen, but I've never seen a
good answer yet.
I'm adding Event support to an existing Automation .EXE that uses MFC (and
hence Dispatch interfaces).
I want to add a ConnectionPoint to the executable to fire an event when a
selection changes. The problem is, I'm not sure what magic is needed
(probably in the MIDL) to tie a VB application sink into the MFC connection
point. I've declared a variable within VB of the notification class type,
and implemented a handler but no go.
The details:
I've added the connection point within the existing code like (CTable is an
existing automation object):
static const IID IID_INotifySel =
{ 0xa9544120, 0x9965, 0x11d3, { 0xa0, 0xfd, 0x0, 0x60, 0x97, 0x3c, 0xf2,
CLSID_5S } };
BEGIN_CONNECTION_MAP(CTable, CCmdTarget)
CONNECTION_PART(CTable, IID_INotifySel, NotifySelCP)
END_CONNECTION_MAP ()
Where the declaration in the .h file is:
BEGIN_CONNECTION_PART(CTable, NotifySelCP)
CONNECTION_IID(IID_INotifySel)
END_CONNECTION_PART(NotifySelCP)
DECLARE_CONNECTION_MAP()
Within the CTable constructor, I have:
EnableConnections();
I'll spare you the INotifySel implementation, but it has one event called
"NewSel". Within my ODL file I've got the
following for INotifySel
[ uuid(A9544120-9965-11D3-A0FD-CLSID_5), hidden ]
dispinterface INotifySel
{
properties:
// NOTE - ClassWizard will maintain property information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_PROP(XNotifySelCP)
//}}AFX_ODL_PROP
methods:
// NOTE - ClassWizard will maintain method information here.
// Use extreme caution when editing this section.
//{{AFX_ODL_METHOD(XNotifySelCP)
[id(1)] void NewSel(long rownum);
//}}AFX_ODL_METHOD
};
// Class information for NotifySel
[ uuid(A9544121-9965-11D4-A0FD-CLSID_5) ]
coclass NotifySel
{
[default, source] dispinterface INotifySel;
};
I added the CLSID for the INotifySel DISPINTERFACE to the Interface section
of the Registry.
Within VB, I've got
Dim MyNotify As App.NotifySel
Dim MyTab As Table
Set MyTab = App.GetTable
Private Sub Table_NewSel(Row As Long)
{
Do Something
}
The big question is how do I get the VB Sink to know about the Table
object's source ? Thanks very much.
========================================
in the ODL file, add a new outgoing interface like this:
// - Added following interface
[ uuid(62D28AE0-05F5-11d6-8871-000102C677B1) ]
dispinterface _HybridEvents
{
properties:
// Event interface has no properties
methods:
[id(1)] void OnTestEvent();
}
// Class information for CHybridDoc
[ uuid(73A4D503-05DA-11D6-8871-000102C677B1) ]
coclass Document
{
[default] dispinterface IHybrid;
// -- Added following line
[default, source] dispinterface _HybridEvents;
};
In the constructor for your document class add the following call:
EnableConnections();
Add this near the top of the document's .H file:
// {62D28AE0-05F5-11d6-8871-000102C677B1}
static const IID IID_IHybridEvents =
{ 0x62d28ae0, 0x5f5, 0x11d6, { 0x88, 0x71, 0x0, 0x1, 0x2, 0xc6, 0x77,
0xb1 } };
Add this in a protected section of the .H file (near the top of
protected):
BEGIN_CONNECTION_PART(CHybridDoc, TestEvent)
CONNECTION_IID(IID_IHybridEvents)
END_CONNECTION_PART(TestEvent)
void FireTestEvent();
Add this near the end of the document class in the .H file:
DECLARE_CONNECTION_MAP()
Add this just before your interface map in the .CPP file and add the
outgoing interface to the map too as follows:
BEGIN_CONNECTION_MAP(CHybridDoc, CDocument)
// - The following line will create the nested object m_xTestEvent in
the document class... add more as needed.
CONNECTION_PART(CHybridDoc, IID_IHybridEvents, TestEvent)
END_CONNECTION_MAP()
BEGIN_INTERFACE_MAP(CHybridDoc, CDocument)
INTERFACE_PART(CHybridDoc, IID_IHybrid, Dispatch)
INTERFACE_PART(CHybridDoc, IID_IConnectionPointContainer,
ConnPtContainer)
END_INTERFACE_MAP()
Finally, here is the code that fires the simple event:
void CHybridDoc::FireTestEvent()
{
// Retrieve the array of connected interfaces
int p;
const CPtrArray* pConnections = m_xTestEvent.GetConnections();
// Iterate through array, calling OnTestEvent on each
interface
for (p = 0; p < pConnections->GetSize(); p++) {
IDispatch * pDispatch =
(IDispatch*)pConnections->GetAt(p);
if (pDispatch != NULL) {
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x1, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
Here is the VB code I used to test this:
Option Explicit
Private WithEvents oHybrid As Hybrid.Document
Private Sub Command1_Click()
oHybrid.MyMethod
End Sub
Private Sub Form_Load()
Set oHybrid = New Hybrid.Document
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set oHybrid = Nothing
End Sub
Private Sub oHybrid_OnTestEvent()
MsgBox "The event Fired!"
End Sub
You must set a reference to the .TLB and run the EXE once standalone
in order to register it.