I have a c++ class that triggers some method like an event.
class Blah {
virtual void Event(EventArgs e);
}
How can I wrap it so whenever the method is called a C# (managed) event will be called?
I thought of inheriting that class and overloading the event method, and then somehow call the managed event.
I'm just not sure how to actually do it.
Something like this (now compile-tested):
#include <vcclr.h>
struct blah_args
{
int x, y;
};
struct blah
{
virtual void Event(const blah_args& e) = 0;
};
public ref class BlahEventArgs : public System::EventArgs
{
public:
int x, y;
};
public ref class BlahDotNet
{
public:
event System::EventHandler<BlahEventArgs^>^ ItHappened;
internal:
void RaiseItHappened(BlahEventArgs^ e) { ItHappened(this, e); }
};
class blah_event_forwarder : public blah
{
gcroot<BlahDotNet^> m_managed;
public:
blah_event_forwarder(BlahDotNet^ managed) : m_managed(managed) {}
protected:
virtual void Event(const blah_args& e)
{
BlahEventArgs^ e2 = gcnew BlahEventArgs();
e2->x = e.x;
e2->y = e.y;
m_managed->RaiseItHappened(e2);
}
};
You need to do some work to reflect the Event() method call so it can be hooked by a managed class. Lets implement a concrete blah class that does so:
#pragma managed(push, off)
struct EvenAtrgs {};
class blah {
public:
virtual void Event (EvenAtrgs e) = 0;
};
typedef void (* CallBack)(EvenAtrgs);
class blahImpl : blah {
CallBack callback;
public:
blahImpl(CallBack fp) {
this->callback = fp;
}
virtual void Event(EvenAtrgs e) {
callback(e);
}
};
#pragma managed(pop)
You can now construct a blahImpl and pass it a function pointer that is called when the Event() method is called. You can use Marshal::GetFunctionPointerForDelegate() to get such a function pointer, it creates a stub for a delegate that makes the transition from unmanaged code to managed code and can store a instance as well. Combined with the boilerplate code to wrap an unmanaged class:
public ref class blahWrapper {
blahImpl* instance;
delegate void managedCallback(EvenAtrgs e);
managedCallback^ callback;
void fireEvent(EvenAtrgs e) {
// Todo: convert e to a managed EventArgs derived class
//...
Event(this, EventArgs::Empty);
}
public:
event EventHandler<EventArgs^>^ Event;
blahWrapper() {
callback = gcnew managedCallback(this, &blahWrapper::fireEvent);
instance = new blahImpl((CallBack)(void*)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback));
}
~blahWrapper() { delete instance; }
!blahWrapper() { delete instance; }
};
The C# code can now write an event handler for the Event event. I left the spelling error in tact, you need to do some work to convert EvenAtrgs to a managed class that derives from EventArgs. Modify the managed Event accordingly.
Create a class that inherits from blah and have it take a reference to your managed wrapper in the constructor. Override the Event() method and when it gets called you can just forward that method on to the managed wrapper class instance you are storing.
Note that you can't raise an event from outside of the containing class, so you'll have to either make it a plain delegate or call a helper method on the managed class to raise it for you.
Related
I'm writing a Python script in IronPython that uses a C# DLL implementing the observer/observable pattern, the C# library has the observable class and I want to implement the observer in Python.
I'm registering my Python function as the observer method, but when it gets called, I get an "System.MissingMemberException: 'PeripheralDiscoveredEventArgs' object has no attribute 'ChangeType' exception thrown.
I can't find any documentation on how to use custom arguments with C# events when using IronPython, looked here, here, here and here.
My Python code is:
central = None
def MyCallback(sender, event_args):
print event_args.ChangeType, event_args.Name
def main():
global central
central = objFactory.GetCentral();
d = System.EventHandler[CustomEventArgs](MyCallback)
central.myHandler += d
(do something)
if __name__ == "__main__":
main()
I also tried to do just this:
central.myHandler += MyCallback
But I got the same exception.
The central variable as a Central instantance and the central.myHandler property in the is defined in the Central class as:
public event EventHandler<CustomEventArgs> myHandler = delegate { };
The CustomEventArgs class is defined as:
public class CustomEventArgs: EventArgs
{
public CustomEventArgs(Class1 obj1, int i, Class2 obj2, bool tf);
public Class1 Obj1 { get; }
public int I{ get; }
public Class2 Obj2t { get; }
public bool Tf{ get; }
}
The observable class method that calls my callback is:
internal abstract class EventHelper
{
internal static void TriggerEvent<T>(EventHandler<T> eventHandler, object source, T args)
{
// event handle no used ?
if (eventHandler == null) return;
try
{
// call event handler
eventHandler(source, args);
}
catch (Exception ex)
{
var target = eventHandler.Target != null ? eventHandler.Target : "unknown";
var methodName = (eventHandler.Method != null && eventHandler.Method.Name != null) ? eventHandler.Method.Name : "unknown";
Log.Error(string.Format("Exception in event handler '{0}' in '{1}'", methodName, target), ex);
}
}
The whole exception is:
"System.MissingMemberException: 'CustomEventArgs' object has no attribute 'ChangeType'
at IronPython.Runtime.Binding.PythonGetMemberBinder.FastErrorGet`1.GetError(CallSite site, TSelfType target, CodeContext context)
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at __main__$1.MyCallback$74(PythonFunction $function, Object sender, Object event_args) in C:\\code\\Launcher.py:line 51
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
at _Scripting_(Object[] , Object , CustomEventArgs )
at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
at EventHelper.TriggerEvent[T](EventHandler`1 eventHandler, Object source, T args) in C:\\code\\Utility\\EventHelper.cs:line 27"
Any pointers on how to use custom event arguments in C# with IronPython? Thanks!!
Environment:
Windows 7 Enterprise 64 bits
IronPython 2.7.8 (2.7.8.0) on .NET 4.0.30319.42000 (32-bit)
.NET Framework 4.5.2
Nevermind, it was my mistake (as usual). Trying to access a non-existent member of a class usually leads to errors/exceptions...
Just changing the observer code to :
def centralOnPeripheralDiscovered_callback(sender, event_args):
print sender
print event_args
Solves the problem. Thanks for the help!
I am using Xamarin.Mac, and I am writing a text to speech project.
In Xamarin.Mac, the class NSSpeechSynthesizer does not provide the event DidFinishSpeaking. May I know how I can get notified when the speaking is finished?
Thank you very much
NSSpeechSynthesizer provides a delegate in the form of an interface (INSSpeechSynthesizerDelegate) that contains the DidFinishSpeaking method:
public partial class ViewController : NSViewController, INSSpeechSynthesizerDelegate
{
~~~~~~
public override void ViewDidLoad()
{
base.ViewDidLoad();
var s = new NSSpeechSynthesizer(NSSpeechSynthesizer.DefaultVoice)
{
Delegate = this
};
s.StartSpeakingString("StackOverflow");
}
[Export("speechSynthesizer:didFinishSpeaking:")]
public void DidFinishSpeaking(NSSpeechSynthesizer sender, bool finishedSpeaking)
{
Console.WriteLine("Done speaking");
}
~~~~~~
}
I have an issue with the google mock EXPECT_CALL macro.
The following code gives compilation error on the EXPECT_CALL Statement:
error C2660: 'testing::Eq' : function does not take 1 arguments
\gmock-1.6.0\include\gmock\gmock-matchers.h
Basically I have a base container and base data object for that container, both abstract and a cache which has a pointer to base container and an Add method that takes a reference to base data object. I have created a basic program to demonstrate the issue. Thanks a lot if anyone can help.
#include "gtest/gtest.h"
#include "gmock/gmock.h"
namespace
{
class BaseData
{
public:
virtual void SetValue(const int value) = 0;
};
class BaseContainer
{
public:
virtual void Add(const BaseData& data) = 0;
};
class MockContainer : public BaseContainer
{
public:
MOCK_METHOD1(Add, void (const BaseData& data));
};
class MockData : public BaseData
{
public:
MOCK_METHOD1(SetValue, void (int));
};
class Cache
{
private:
BaseContainer* container;
public:
Cache(BaseContainer* c)
{
container = c;
}
~Cache()
{
}
void AddToContainer(const BaseData& data)
{
container->Add(data);
}
};
class CacheTestFixture : public ::testing::Test
{
protected:
CacheTestFixture() {}
virtual ~CacheTestFixture() {}
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(CacheTestFixture, TestAdd)
{
MockData data;
MockContainer container;
EXPECT_CALL(container, Add(data)).WillRepeatedly(::testing::Return());
Cache c(&container);
ASSERT_NO_THROW(c.AddToContainer(data));
}
}
int _tmain(int argc, _TCHAR* argv[])
{
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
EXPECT_CALL(container, Add(testing::Ref(data))).WillRepeatedly(::testing::Return());
To send the mock implementation as a base class reference, testing::Eq would require the == operator to be implemented on the abstract base class which is not desirable.
You need to use ::testing::Eq(ByRef(data))
::testing::Eqis Matcher that needs to be used, read about matchers on Google Mock Cookbook.
The answer from Blackhole is correct, you have to use
::testing::Eq(ByRef(data))
But in order for a correct copmarison you have to define the operator== for your BaseData since it is an abstract class. You can do this in your testing code as a free function:
bool operator==(const BaseData& left, const BaseData& right)
{
return ...;
}
I once asked a similar question, see here
We have a custom TraceListener implementation which only logs when a specific object (LogMessage) is received. This all works well when using directly with the Trace.Write(object) method.
Due to Performance reason, I want to separate the Listener, so all non-relevant Trace messages are not passed to the listener. Therefore I created a specific TraceSource whith only this listener attached.
Now I struggle to pass my custom log object (LogMessage) to the listener using the TraceSource. The TraceSource.TraceData(TraceEventType, int, object) always invokes the TraceListener.Write(string) method, not the TraceListener.Write(object) method.
Is there any way I can pass the custom object to the Listener using the TraceSource?
Sample code:
using System.Diagnostics;
namespace Sample
{
public class LogMessage
{
public byte[] Data { get; set; }
//...
}
public class Sample
{
public void Foo()
{
var ts = new TraceSource("Test");
var lm = new LogMessage();
//lm.Data = ...;
//this works: calls the Write(object) method in listener
Trace.Write(lm);
//this doesn't work: calls the Write(string) method in listener
ts.TraceData(TraceEventType.Information, 0, lm);
}
}
public class MyListener : TraceListener
{
public override void Write(string message)
{
//not in use
}
public override void WriteLine(string message)
{
//not in use
}
public sealed override void Write(object o)
{
if (o is LogMessage)
{
//do someting with the LogMessage
}
}
}
}
Thanks
Thomas
maybe it's too late for an answer but anyway :
By using a tool like JustDecompile you can easily see that TraceSource.TraceData uses TraceListener.TraceData method which itself basically calls WriteLine with object.ToString() for message.
So you'll have to override the ToString method for your class LogMessage in order to do as you want.
This is quite straight forward(ish) to do is the event is 'real' as in now created by DynamicProxy, but I can't work anything out for a mocked event.
The best way to explain what I'm trying to achieve is with code, please see the comment lines in the test method:
using System;
using Moq;
using NUnit.Framework;
namespace MOQTest
{
[TestFixture]
public class EventsMoqTest
{
[Test]
public void DetachTest()
{
var hasEventMock = new Mock<IHasEvent>();
using (var observer = new Observer(hasEventMock.Object))
{
//Assert that hasEventMock.Object has handler attached
}
//Assert that hasEventMock.Object DOES NOT have handler attached
}
}
public interface IHasEvent
{
event EventHandler AnEvent;
}
public class Observer : IDisposable
{
private readonly IHasEvent _hasEvent;
private readonly EventHandler _hasEventOnAnEvent;
public Observer(IHasEvent hasEvent)
{
_hasEvent = hasEvent;
_hasEventOnAnEvent = _hasEvent_AnEvent;
_hasEvent.AnEvent += _hasEventOnAnEvent;
}
void _hasEvent_AnEvent(object sender, EventArgs e)
{}
public void Dispose()
{
_hasEvent.AnEvent -= _hasEventOnAnEvent;
}
}
}
Unfortunately, you can't. This isn't really a moq issue, but the way the C# event keyword works with delegates. See this SO answer for more information.