Sinking Managed Events in VC7

F

fshawish

Hello all,

I have a C# Class Lib that exposes a public events, methods and
properties. I am referencing this ClassLib from an MFC DIalog app that
is compiled with /CLR. I use gcroot<T> to create and connect and call
the method in the C# Class Lib w/o problems. However, I am having a
hard time sinking the C# events in my managed C++ app. I am using .NET
1.1.

Note that I can do this through COM Interop; however, I am would prefer
to do it natively in the managed environment.

The following code compiles and all works except I never receive the
event.

// CoCreate the CoClass...
m_pConfigAgent = new MyComponents::ConfigDataServerAgent();

// QI for the interface; make sure it's not NULL!
m_pIDvcoConfig = (MyComponents::IDVCOConfigData*) m_pConfigAgent;
if (m_pIDvcoConfig != NULL)
{
CMfcClientSink* sink = new CMfcClientSink;
m_pConfigAgent->DataChanged += new
MyComponents::ConfigDataEventHandler(sink,
&CMfcClientSink::OnDataChanged);

// Intialize the server
m_pIDvcoConfig->Initialize();
m_bDvcoSim = m_pIDvcoConfig->Simulation;
:
}

where, CMfcClientSink is declared as follows:

// __gc sink class object that will act as the event sink
__gc class CMfcClientSink
{
public:
void OnDataChanged();
};


and the interface pointers are delcared as follows:

gcroot<My:Components::ConfigDataServerAgent*> m_pConfigAgent;
gcroot<MyComponents::IDVCOConfigData*> m_pIDvcoConfig;

and lastely, the event handler/method:

void CMfcClientSink::OnDataChanged()
{
AfxMessageBox("DataChanged");
}

Any help would be appreciated...

Fadi
 
M

Marcus Heege

Hi Fadi,

Hello all,

I have a C# Class Lib that exposes a public events, methods and
properties. I am referencing this ClassLib from an MFC DIalog app that
is compiled with /CLR. I use gcroot<T> to create and connect and call
the method in the C# Class Lib w/o problems. However, I am having a
hard time sinking the C# events in my managed C++ app. I am using .NET
1.1.

Note that I can do this through COM Interop; however, I am would prefer
to do it natively in the managed environment.

The following code compiles and all works except I never receive the
event.

// CoCreate the CoClass...
m_pConfigAgent = new MyComponents::ConfigDataServerAgent();

// QI for the interface; make sure it's not NULL!
m_pIDvcoConfig = (MyComponents::IDVCOConfigData*) m_pConfigAgent;
if (m_pIDvcoConfig != NULL)
{
CMfcClientSink* sink = new CMfcClientSink;
m_pConfigAgent->DataChanged += new
MyComponents::ConfigDataEventHandler(sink,
&CMfcClientSink::OnDataChanged);

// Intialize the server
m_pIDvcoConfig->Initialize();
m_bDvcoSim = m_pIDvcoConfig->Simulation;
:
}

where, CMfcClientSink is declared as follows:

// __gc sink class object that will act as the event sink
__gc class CMfcClientSink
{
public:
void OnDataChanged();
};


and the interface pointers are delcared as follows:

gcroot<My:Components::ConfigDataServerAgent*> m_pConfigAgent;
gcroot<MyComponents::IDVCOConfigData*> m_pIDvcoConfig;

and lastely, the event handler/method:

void CMfcClientSink::OnDataChanged()
{
AfxMessageBox("DataChanged");
}

Any help would be appreciated...

Fadi

Ad hoc, I can not see anything that look wrong in your code.
Are you sure that the event is actually fired?
How are you referencing the C# class library in your C++/CLI project?
 
F

fshawish

Thanks Marcus for the quick reply.

The 'agent' in my code snipit is actually a Remoted Singleton server.
I have verified that the event is actually happening by observing
behaviour in C# client(s) and I expect the same behaviour in my VC++
Client. My only reservation of the code I lisetd is that my instance
of CMfcClientSink is on the stack and will be marked for GC at the end
of the function (InitDialog(), in this case.) When I move the instance
to be a member of the my unmanaged class (the dilaog) I get a bunch of
errors like: "cannot declare a managed 'sink' in an unmanaged...".
Remember that by problem client is an VC7 Unmanaged MFC application
that is compiled to Uses Managed Extensions (/CLR).

I am simply adding Reference to the C# rojects in the same solution as
the MFC Client.

Thanks again for your response.

Fadi
 
M

Marcus Heege

Thanks Marcus for the quick reply.

The 'agent' in my code snipit is actually a Remoted Singleton server.
I have verified that the event is actually happening by observing
behaviour in C# client(s) and I expect the same behaviour in my VC++
Client. My only reservation of the code I lisetd is that my instance
of CMfcClientSink is on the stack and will be marked for GC at the end
of the function (InitDialog(), in this case.) When I move the instance
to be a member of the my unmanaged class (the dilaog) I get a bunch of
errors like: "cannot declare a managed 'sink' in an unmanaged...".
Remember that by problem client is an VC7 Unmanaged MFC application
that is compiled to Uses Managed Extensions (/CLR).


I am simply adding Reference to the C# rojects in the same solution as
the MFC Client.

Thanks again for your response.

Fadi

Maybe it helps to see a working project:

Here is my C# test library:

<code language="c#">
using System;

public class TestEvent
{
public event EventHandler _theEvent;
public event EventHandler TheEvent
{
add { _theEvent += value; }
remove { _theEvent -= value; }
}
public void Fire()
{
_theEvent(this, EventArgs.Empty);
}
}
</code>

Notice that it uses a special C# event syntax that allows you to set a
breakpoint in the event registration method.

Here is the Managed C++ client:

<code language="MC++">
// This is the main project file for VC++ application project
// generated using an Application Wizard.

#include "stdafx.h"

#using <mscorlib.dll>

using namespace System;

__gc struct Evt_Proxy
{
void TheEvent_Handler(Object __gc* sender, EventArgs __gc* e)
{
Console::WriteLine("Event handled");
}
};

class TestCls
{
public:
void Test()
{
TestEvent* pTE = __gc new TestEvent();
pTE->add_TheEvent(__gc new EventHandler(__gc new Evt_Proxy(),
&Evt_Proxy::TheEvent_Handler));
pTE->Fire();
}
};

int _tmain()
{
(new TestCls())->Test();
}
</code>

Hope this helps.

Marcus
 
F

fshawish

Hello Marcus,

Thanks for the code example; I like the add, remove methods to help in
debugging.

I modified my code to test-fire the event prior immediately following
the add_eventhandler() and, in this case, I get the event!!! this
confirmed my initial suspecion that, in my case, the _gc "sink" class
is getting disposed when I exit the InitDialog() method because it is
new'ed as a stack variable.

I find the managed extension in VC7 very convoluted and difficult to
master. So the question is now: how to properly declare and use the
managed sink class in my unmanaged CDialog class? I tried wrapping in
it in gcroot<> but the add_eventhandler complains that I "cannot
declare a managed 'sink' in an unmanaged class...".

Thanks again and best regards,

Fadi
 
M

Marcus Heege

Hello Marcus,

Thanks for the code example; I like the add, remove methods to help in
debugging.

I modified my code to test-fire the event prior immediately following
the add_eventhandler() and, in this case, I get the event!!! this
confirmed my initial suspecion that, in my case, the _gc "sink" class
is getting disposed when I exit the InitDialog() method because it is
new'ed as a stack variable.

This is not the case:
a) __gc classes can not be created on the stack
b) the delegate you pass to the C# class holds a strong reference to the
target object => the sink class will *not* be GC'ed

There must be still another problem. Maybe you check in you C# code that
(_eventDelegate->GetInvocationList()->Length == 1) to ensure that the event
is still registered.

Is is possible that you have two instances of you C# class. One where you
register the event and the other one that fires an event but noboday has
registered i`t?
I find the managed extension in VC7 very convoluted and difficult to
master. So the question is now: how to properly declare and use the
managed sink class in my unmanaged CDialog class? I tried wrapping in
it in gcroot<> but the add_eventhandler complains that I "cannot
declare a managed 'sink' in an unmanaged class...".

gcroot should be ok for that:

gcroot<Sink*> sink;

Marcus
 
F

fshawish

Thanks Marcus! you are correct, I was using an in-proc instance of the
server (event source). My Managed MFC app's remoting .config file was
out-of-sync with my server host's .config... it all works great now.

Also thanks for the insight into creating and using managed types in
VC7 and the events debugging tips, I really appreciate the help.

Regards,

Fadi
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top