DotNetNewsgroup.com  
web access to complete list of Microsoft.NET newsgroups
   home   |   control panel login   |   archive  |  
 
  carried group
academic
adonet
aspnet
aspnet.announcements
aspnet.buildingcontrols
aspnet.caching
aspnet.datagridcontrol
aspnet.mobile
aspnet.security
aspnet.webcontrols
aspnet.webservices
assignment_manager
datatools
dotnet.distributed_apps
dotnet.general
dotnet.myservices
dotnet.nternationalization
dotnet.scripting
dotnet.security
dotnet.vjsharp
dotnet.vsa
dotnet.xml
dotnetfaqs
framework
framework.clr
framework.compactframework
framework.component_services
framework.controls
framework.databinding
framework.drawing
framework.enhancements
framework.interop
framework.odbcnet
framework.performance
framework.remoting
framework.sdk
framework.setup
framework.webservices
framework.windowsforms
framework.wmi
frwk.windowsforms.designtime
lang.csharp
lang.jscript
lang.vb
lang.vb.controls
lang.vb.data
lang.vb.upgrade
lang.vc
lang.vc.libraries
  
 
start date: Fri, 06 Jul 2007 17:07:35 -0700,    posted on: microsoft.public.dotnet.framework.interop        back       

Thread Index
  1    unknown


Is this a CCW interop bug: C++ event sink for C# delegate   
Hi all,

In the process of investigating interop between .NET and COM, I have
discovered a problem that I am as of yet unable to solve.  Basically,
I am trying to use a .NET delegate as an outbound interface.  I am
able to get the functionality I would expect, but I am also observing
an interface leak.  My .NET COM object is constructed thus:

using System;
using System.Runtime.InteropServices;

namespace CCWTestObject
{
	[ComVisible(false)]
	public delegate void TestEventDelegate(string strMsg);

	[Guid("8138740E-7A5E-4cfe-BE77-328B8EAFEE77")]
	[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
	[ComVisible(true)]
	public interface IComInterfaceEvents
	{
		[DispId(1)]
		void TestEvent(string strMsg);
	}

	[Guid("3967980E-C065-42ca-92C1-46EAE21795C8")]
	[InterfaceType(ComInterfaceType.InterfaceIsDual)]
	public interface _ICOMInterface
	{
		[DispId(1)]
		void FireTestEvent();
	}

	[Guid("6269664E-A0AA-4f1c-936B-6E362679136C")]
	[ClassInterface(ClassInterfaceType.None)]
	[ComSourceInterfaces(typeof(IComInterfaceEvents))]
	[ProgId("CCWTestObject.ICOMInterface")]
	public class ICOMInterface : _ICOMInterface
	{
		public event TestEventDelegate TestEvent;

		public ICOMInterface() {}

		public void FireTestEvent()
		{
			TestEvent("Event fired.");
		}
	};
}

I import the typelib produced from this with #import
"CCWTestObject.tlb" named_guids.

My client is a simple C++ console app.  I'm using ATL to reduce the
complexity.  For the sink event object, I have:

#define TESTEVENT_ID 1

#define DISPID_TESTEVENT 0x00000001

static _ATL_FUNC_INFO OnTestEventInfo = {CC_STDCALL,VT_EMPTY,1,
{VT_BSTR} };

class CTestDotNetEventSink :
	IDispEventImpl<TESTEVENT_ID,CTestDotNetEventSink,
		&CCWTestObject::DIID_IComInterfaceEvents,
		&CCWTestObject::LIBID_CCWTestObject,
		1,0>
{
public:
	BEGIN_SINK_MAP(CTestDotNetEventSink)
		SINK_ENTRY_INFO(TESTEVENT_ID,
			CCWTestObject::DIID_IComInterfaceEvents,
			DISPID_TESTEVENT, OnTestEvent, &OnTestEventInfo)
	END_SINK_MAP()

	CTestDotNetEventSink(CCWTestObject::_ICOMInterface* spDotNetObject)
	{
		m_spDotNetObject = spDotNetObject;
		m_spDotNetObject->AddRef();

		HRESULT hr = DispEventAdvise((IUnknown*)m_spDotNetObject);
		if (FAILED(hr))
		{
			ATLTRACE("Error in Advise.\n");
		}
	}

	~CTestDotNetEventSink(void)
	{
		m_spDotNetObject->Release();
		HRESULT hr = DispEventUnadvise((IUnknown*)m_spDotNetObject);

		if (FAILED(hr))
		{
			ATLTRACE("Error in Unadvise.\n");
		}
	}

	void __stdcall OnTestEvent(BSTR strMsg)
	{
		ATLTRACE(strMsg);
		ATLTRACE("\n");
		SysFreeString(strMsg);
	}

private:
	CCWTestObject::_ICOMInterface* m_spDotNetObject;
};

my main looks like:

int _tmain(int argc, _TCHAR* argv[])
{
	CoInitialize(NULL);

	CCWTestObject::_ICOMInterfacePtr
		spDotNetObject(__uuidof(CCWTestObject::ICOMInterface));

	if (spDotNetObject)
	{
		CTestDotNetEventSink* pTestEvent =
			new
CTestDotNetEventSink((CCWTestObject::_ICOMInterface*)spDotNetObject);

		//spDotNetObject->FireTestEvent();

		delete pTestEvent;
	}

	spDotNetObject = NULL;

	CoUninitialize();

	return 0;
}

Note that I have the call to the FireTestEvent method commented out.
Running this in debug mode with _ATL_DEBUG_INTERFACES defined and
stepping over the line

HRESULT hr = DispEventAdvise((IUnknown*)m_spDotNetObject);

in the CTestDotNetEventSink constructor, I see the following:

QIThunk - 1         	AddRef  :	Object = 0x009d3be0	Refcount = 1
IDispEventImpl - IComInterfaceEvents
QIThunk - 2         	AddRef  :	Object = 0x009d3be0	Refcount = 1
IDispEventImpl - IUnknown
QIThunk - 2         	AddRef  :	Object = 0x009d3be0	Refcount = 2
IDispEventImpl - IUnknown
QIThunk - 2         	AddRef  :	Object = 0x009d3be0	Refcount = 3
IDispEventImpl - IUnknown
QIThunk - 2         	Release :	Object = 0x009d3be0	Refcount = 2
IDispEventImpl - IUnknown
QIThunk - 2         	Release :	Object = 0x009d3be0	Refcount = 1
IDispEventImpl - IUnknown
QIThunk - 1         	Release :	Object = 0x009d3be0	Refcount = 0
IDispEventImpl - IComInterfaceEvents

When the event sink is destroyed, invoking the DispEventUnadvise,
nothing happens.  When the program exits, I see the following:

ATL: QIThunk - 2         	LEAK    :	Object = 0x009c3be0	Refcount = 1
MaxRefCount = 3	IDispEventImpl - IUnknown
First-chance exception at 0x79e8db38 in CCWTestClient.exe: 0xC0000005:
Access violation reading location 0xfeeefef6.

with the FireThestEvent method uncommented I see:

QIThunk - 1         	AddRef  :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IComInterfaceEvents
QIThunk - 2         	AddRef  :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IUnknown
QIThunk - 2         	AddRef  :	Object = 0x009c3be0	Refcount = 2
IDispEventImpl - IUnknown
QIThunk - 2         	AddRef  :	Object = 0x009c3be0	Refcount = 3
IDispEventImpl - IUnknown
QIThunk - 2         	Release :	Object = 0x009c3be0	Refcount = 2
IDispEventImpl - IUnknown
QIThunk - 2         	Release :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IUnknown
QIThunk - 1         	Release :	Object = 0x009c3be0	Refcount = 0
IDispEventImpl - IComInterfaceEvents
QIThunk - 2         	AddRef  :	Object = 0x009c3be0	Refcount = 2
IDispEventImpl - IUnknown
QIThunk - 3         	AddRef  :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IComInterfaceEvents
QIThunk - 2         	Release :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IUnknown
QIThunk - 3         	Release :	Object = 0x009c3be0	Refcount = 0
IDispEventImpl - IComInterfaceEvents
QIThunk - 2         	AddRef  :	Object = 0x009c3be0	Refcount = 2
IDispEventImpl - IUnknown
QIThunk - 4         	AddRef  :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IComInterfaceEvents
QIThunk - 2         	Release :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IUnknown
QIThunk - 4         	AddRef  :	Object = 0x009c3be0	Refcount = 2
IDispEventImpl - IComInterfaceEvents
Event fired.
QIThunk - 4         	Release :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - IComInterfaceEvents
ATL: QIThunk - 2         	LEAK    :	Object = 0x009c3be0	Refcount = 1
MaxRefCount = 3	IDispEventImpl - IUnknown
ATL: QIThunk - 4         	LEAK    :	Object = 0x009c3be0	Refcount = 1
MaxRefCount = 2	IDispEventImpl - IComInterfaceEvents
First-chance exception at 0x79e8db38 in CCWTestClient.exe: 0xC0000005:
Access violation reading location 0xfeeefef6.
First-chance exception at 0x79e8db38 in CCWTestClient.exe: 0xC0000005:
Access violation reading location 0xfeeefef6.

Using a standard ATL-based COM server I see (with FireTestEvent method
enabled):

QIThunk - 1         	AddRef  :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - _ICOMInterfaceEvents
QIThunk - 1         	AddRef  :	Object = 0x009c3be0	Refcount = 2
IDispEventImpl - _ICOMInterfaceEvents
Event fired.
QIThunk - 1         	Release :	Object = 0x009c3be0	Refcount = 1
IDispEventImpl - _ICOMInterfaceEvents
QIThunk - 1         	Release :	Object = 0x009c3be0	Refcount = 0
IDispEventImpl - _ICOMInterfaceEvents

I have downloaded a couple of other code examples from the web and
analyzed them as well; they too exhibit this behavior.  So the
question is:  could there be something hinky with my system, or is
this a CCW interop bug?  Or am I missing something here like a
difference in threading models etc.

Other data points of possible interest:

(1) My machine is a pretty "well-used" dev machine -- vs6, 2003, and
2005 installed.
(2) In addition, v1, v1.1, v2, AND v3 of .NET are installed!!
(3) I have run this code on a "v1.1 only" virtual machine with the
same result.

If the code looks okay, I'd appreciate some feedback and/or
independent verification.  Or, obviously, clue me in to my
ignorance! ;-)

Thanks in advance,

Steve
Date:Fri, 06 Jul 2007 17:07:35 -0700   Author:  

Google
 
Web dotnetnewsgroup.com


COPYRIGHT ?2005, EUROFRONT WORLDWIDE LTD., ALL RIGHT RESERVE  |   Contact us