dbcarter
2005-07-07 21:01:06 UTC
Hi All,
I have been trying for quite some time to generate events in
a managed C# assembly and Sink those events in an ATL COM client.
I have been able to duplicate a phone example from Adam Nathan's book
".NET and COM" where "WithEvents" was used in a VB6 Client.
While the VB6 client works great, I get a First-chance exception
0xE0434F4D (generic COM exception) when I attempt to start my C#
FireEventThread from the COM client, and I never hit a breakpoint
at the start of the COM Sink Invoke method.
So... I know the events are being generated, but they are being
lost somewhere in the CLR managed app. I have included all the
relevant code sections in my experiment below. Is there a code
example that anyone could refer me to that uses ATL COM to sink
C# events, not just the simple VB6 client?
Any help would be greatly appreciated!
Dan Carter
//-------------------------------------------------------------------------
// C# PhoneTest event source
using System;
using System.Runtime.InteropServices;
using System.Threading;
// Source interface with "event handlers" for COM objects to implement
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IPhoneEvents
{
void Ring();
}
// Delegate for the event
public delegate void RingEventHandler();
[ComSourceInterfaces(typeof(IPhoneEvents))]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Phone
{
public event RingEventHandler Ring;
public Thread fireMyEventThread;
public void BeginMyFireEventThread()
{
fireMyEventThread = new Thread(new ThreadStart(this.onRing));
fireMyEventThread.Start();
}
public void onRing()
{
int ii = 0;
while (ii < 3)
{
Ring();
Thread.Sleep(1000);
ii++;
}
}
}
//-------------------------------------------------------------------------
Simple VB6 Client that sinks the events as expected:
This works great... I get three pop up msgBoxes
Private WithEvents myPhone As Phone.Phone
Private Sub Form_Load()
Set myPhone = CreateObject("Phone")
End Sub
Private Sub myPhone_Ring()
MsgBox "Ring!"
End Sub
Private Sub Start_Click()
myPhone.BeginMyFireEventThread
End Sub
//-------------------------------------------------------------------------
// COM ATL EVENT SINK CLASS (CPhoneTestSink.h)
// NOTE: CPhoneTestSink.cpp has only the standard Invoke method implemented.
using namespace Phone;
class ATL_NO_VTABLE CPhoneTestSink :
public CComObjectRootEx<CComMultiThreadModel>,
public IDispatch
{
public:
BEGIN_COM_MAP(CPhoneTestSink)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IID(DIID_IPhoneEvents, IDispatch)
END_COM_MAP()
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
STDMETHOD(GetTypeInfo)(UINT itinfo,
LCID lcid,
ITypeInfo** pptinfo);
STDMETHOD(GetIDsOfNames)(REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgdispid);
STDMETHOD(Invoke)(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr);
};
//-------------------------------------------------------------------------
// COM ATL CLIENT SIDE
HRESULT hr;
CComPtr<_Phone> m_pIPhone;
DWORD dwPhoneCookie;
CComPtr<IUnknown> pPhoneUnknown;
IUnknown *pUnkPhoneDispatchSink;
CComObject<CPhoneTestSink> *m_pPhoneEvents;
pPhoneUnknown = NULL;
dwPhoneCookie = NULL;
pUnkPhoneDispatchSink = NULL;
m_pPhoneEvents = NULL;
hr = m_pIPhone.CoCreateInstance(CLSID_Phone, NULL);
// hr is S_OK!
m_pIPhone->QueryInterface(IID_IUnknown,(void **)&m_pPhoneUnknown);
CComObject<CPhoneTestSink>::CreateInstance(&m_pPhoneEvents);
m_pPhoneEvents->QueryInterface(IID_IUnknown,
(void**)&m_pUnkPhoneDispatchSink);
if (m_pUnkPhoneDispatchSink)
{
hr = AtlAdvise(m_pPhoneUnknown,
m_pUnkPhoneDispatchSink,
DIID_IPhoneEvents,
&m_dwPhoneCookie);
// again, hr is S_OK! so AtlAdvise seems happy
}
m_pIPhone->BeginMyFireEventThread();
// I get an exception when I exit this thread
**If you made it this far, thank you!!**
I have been trying for quite some time to generate events in
a managed C# assembly and Sink those events in an ATL COM client.
I have been able to duplicate a phone example from Adam Nathan's book
".NET and COM" where "WithEvents" was used in a VB6 Client.
While the VB6 client works great, I get a First-chance exception
0xE0434F4D (generic COM exception) when I attempt to start my C#
FireEventThread from the COM client, and I never hit a breakpoint
at the start of the COM Sink Invoke method.
So... I know the events are being generated, but they are being
lost somewhere in the CLR managed app. I have included all the
relevant code sections in my experiment below. Is there a code
example that anyone could refer me to that uses ATL COM to sink
C# events, not just the simple VB6 client?
Any help would be greatly appreciated!
Dan Carter
//-------------------------------------------------------------------------
// C# PhoneTest event source
using System;
using System.Runtime.InteropServices;
using System.Threading;
// Source interface with "event handlers" for COM objects to implement
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IPhoneEvents
{
void Ring();
}
// Delegate for the event
public delegate void RingEventHandler();
[ComSourceInterfaces(typeof(IPhoneEvents))]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Phone
{
public event RingEventHandler Ring;
public Thread fireMyEventThread;
public void BeginMyFireEventThread()
{
fireMyEventThread = new Thread(new ThreadStart(this.onRing));
fireMyEventThread.Start();
}
public void onRing()
{
int ii = 0;
while (ii < 3)
{
Ring();
Thread.Sleep(1000);
ii++;
}
}
}
//-------------------------------------------------------------------------
Simple VB6 Client that sinks the events as expected:
This works great... I get three pop up msgBoxes
Private WithEvents myPhone As Phone.Phone
Private Sub Form_Load()
Set myPhone = CreateObject("Phone")
End Sub
Private Sub myPhone_Ring()
MsgBox "Ring!"
End Sub
Private Sub Start_Click()
myPhone.BeginMyFireEventThread
End Sub
//-------------------------------------------------------------------------
// COM ATL EVENT SINK CLASS (CPhoneTestSink.h)
// NOTE: CPhoneTestSink.cpp has only the standard Invoke method implemented.
using namespace Phone;
class ATL_NO_VTABLE CPhoneTestSink :
public CComObjectRootEx<CComMultiThreadModel>,
public IDispatch
{
public:
BEGIN_COM_MAP(CPhoneTestSink)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY_IID(DIID_IPhoneEvents, IDispatch)
END_COM_MAP()
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
STDMETHOD(GetTypeInfo)(UINT itinfo,
LCID lcid,
ITypeInfo** pptinfo);
STDMETHOD(GetIDsOfNames)(REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgdispid);
STDMETHOD(Invoke)(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr);
};
//-------------------------------------------------------------------------
// COM ATL CLIENT SIDE
HRESULT hr;
CComPtr<_Phone> m_pIPhone;
DWORD dwPhoneCookie;
CComPtr<IUnknown> pPhoneUnknown;
IUnknown *pUnkPhoneDispatchSink;
CComObject<CPhoneTestSink> *m_pPhoneEvents;
pPhoneUnknown = NULL;
dwPhoneCookie = NULL;
pUnkPhoneDispatchSink = NULL;
m_pPhoneEvents = NULL;
hr = m_pIPhone.CoCreateInstance(CLSID_Phone, NULL);
// hr is S_OK!
m_pIPhone->QueryInterface(IID_IUnknown,(void **)&m_pPhoneUnknown);
CComObject<CPhoneTestSink>::CreateInstance(&m_pPhoneEvents);
m_pPhoneEvents->QueryInterface(IID_IUnknown,
(void**)&m_pUnkPhoneDispatchSink);
if (m_pUnkPhoneDispatchSink)
{
hr = AtlAdvise(m_pPhoneUnknown,
m_pUnkPhoneDispatchSink,
DIID_IPhoneEvents,
&m_dwPhoneCookie);
// again, hr is S_OK! so AtlAdvise seems happy
}
m_pIPhone->BeginMyFireEventThread();
// I get an exception when I exit this thread
**If you made it this far, thank you!!**
--
dbcarter
dbcarter