Trace PRISM / CAL events (best practice?) - c#

Ok,
this question is for people with either a deep knowledge of PRISM or some magic skills I just lack (yet). The Background is simple: Prism allows the declaration of events to which the user can subscribe or publish. In code this looks like this:
_eventAggregator.GetEvent<LayoutChangedEvent>().Subscribe(UpdateUi, true);
_eventAggregator.GetEvent<LayoutChangedEvent>().Publish("Some argument");
Now this is nice, especially because these events are strongly typed, and the declaration is a piece of cake:
public class LayoutChangedEvent : CompositePresentationEvent<string>
{
}
But now comes the hard part: I want to trace events in some way. I had the idea to subscribe using a lambda expression calling a simple log message. Worked perfectly in WPF, but in Silverlight there is some method access error (took me some time to figure out the reason).. If you want to see for yourself, try this in Silverlight:
eA.GetEvent<VideoStartedEvent>().Subscribe(obj => TraceEvent(obj, "vSe", log));
If this would be possible, I would be happy, because I could easily trace all events using a single line to subscribe. But it does not... The alternative approach is writing a different functions for each event, and assign this function to the events. Why different functions? Well, I need to know WHICH event was published. If I use the same function for two different events I only get the payload as argument. I have now way to figure out which event caused the tracing message.
I tried:
using Reflection to get the causing event (not working)
using a constructor in the event to enable each event to trace itself (not allowed)
Any other ideas?
Chris
PS: Writing this text took me most likely longer than writing 20 functions for my 20 events, but I refuse to give up :-) I just had the idea to use postsharp, that would most likely work (although I am not sure, perhaps I end up having only information about the base class).. Tricky and so unimportant topic...

Probably the easiest thing would be to subclass CompositePresentationEvent and override the behavior of the Publish event. Here's the source for CompositePresentationEvent:
http://compositewpf.codeplex.com/SourceControl/changeset/view/26112#496659
Here's the current Publish behavior:
public virtual void Publish(TPayload payload)
{
base.InternalPublish(payload);
}
So you could just add a little to this:
public virtual override void Publish(TPayload payload)
{
ILoggerFacade logger = ServiceLocator.Current.GetInstance<ILoggerFacade>();
logger.Log("Publishing " + payload.ToString(), Category.Debug, Priority.Low);
base.InternalPublish(payload);
}
Here I'm using the logger facility built into Prism, but feel free to substitute your own (or better, just implement ILoggerFacade!).
I was surprised that there were any default messages being published or places to plug in tracing in this system... as much as EventAggregator is abused by people, you'd think this would be a big request!

A little late but better late than never! I recently had the same problem and this is how I solved it.
First, I didn't like the Prism method of publishing/subscribing to events, so I used a method like this instead:
http://neverindoubtnet.blogspot.com/2009/07/simplify-prism-event-aggregator.html
This post above suggests using Extension methods on Event Aggregator to simplify the call to publish/subscribe. As a result your client code looks like this:
IEventAggregator ev;
ev.Publish<MyCustomMessage>();
//or
ev.Publish(new MyCustomMessage(someData));
//and similarly subscription
ev.Subscribe<MyCustomMessage(this.OnCustomMessageReceived);
// ...
private void OnCustomMessageReceived(MyCustomMessage message)
{
// ...
}
// With a BaseMessageEvent class as follows (see the blog post above for where this comes from)
/// <summary>
/// Base class for all messages (events)
/// </summary>
/// <typeparam name="TMessage">The message type (payload delivered to subscribers)</typeparam>
public class BaseEventMessage<TMessage> : CompositePresentationEvent<TMessage>
{
}
Ok this is great, but rather than hacky extension methods I implemented my own event service as follows:
/// <summary>
/// The EventService instance
/// </summary>
public class EventService : IEventService
{
private readonly IEventAggregator eventAggregator;
private readonly ILoggerFacade logger;
/// <summary>
/// Initializes a new instance of the <see cref="EventService"/> class.
/// </summary>
/// <param name="logger">The logger instance.</param>
/// <param name="eventAggregator">The event aggregator instance.</param>
public EventService(IEventAggregator eventAggregator, ILoggerFacade logger)
{
this.logger = logger;
this.eventAggregator = eventAggregator;
}
#region IEventService Members
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
public void Publish<TMessageType>() where TMessageType : BaseEventMessage<TMessageType>, new()
{
TMessageType message = Activator.CreateInstance<TMessageType>();
this.Publish(message);
}
/// <summary>
/// Publishes the event of type TMessageType to all subscribers
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="message">The message to publish</param>
public void Publish<TMessageType>(TMessageType message) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Publish(message);
}
/// <summary>
/// Subscribes to the event of type TMessage
/// </summary>
/// <typeparam name="TMessageType">The message type (Payload), must inherit CompositeEvent</typeparam>
/// <param name="action">The action to execute when the event is raised</param>
public void Subscribe<TMessageType>(Action<TMessageType> action) where TMessageType : BaseEventMessage<TMessageType>, new()
{
// Here we can log our message publications
if (this.logger != null)
{
// logger.log etc..
}
this.eventAggregator.GetEvent<TMessageType>().Subscribe(action);
}
#endregion
}
Then I register IEventService/EventService as a singleton in the bootstrapper and forget about using the IEventAggregator, just use this (however if someone uses the IEventAggregator, its the same instance as that used by the EventService so will still work).
Finally, another trick to add is to use the Stack Frame to tell me where publications and subscriptions are coming from. Note this is a slow process (unwinding the stack frame) so use it sparingly. If you are
raising an event regularly then perhaps put a flag in your BaseEventMessage and check that to see whether to log publications for certain event types.
// Inside Publish method ... Log the subscription
if (this.logger != null)
{
Type messageType = typeof(TMessageType);
Type callingType = GetCallingType();
string methodName = GetCallingMethod().Name;
// Log the publication of this event
this.logger.Log(
string.Format("Event {0} was published by {1}.{2}()",
messageType.Name,
callingType.Name,
methodName),
Category.Debug,
Priority.Low));
}
// Additional methods to add to EventService to get the calling type/class
//
/// <summary>
/// Gets the Type that called the method or property where GetCallingType is called
/// </summary>
/// <returns>The class type that called</returns>
[MethodImplAttribute(MethodImplOptions.NoInlining)]
public static Type GetCallingType()
{
int skip = 2;
MethodBase method = new StackFrame(skip, false).GetMethod();
return method.DeclaringType;
}
/// <summary>
/// Gets the Method that called the method or property where GetCallingMethod is called
/// </summary>
/// <returns>The method type that was called</returns>
public static MethodBase GetCallingMethod()
{
return new StackFrame(2, false).GetMethod();
}
Note the above won't work in Silverlight (the use of the StackFrame), but the rest does. I've found this invaluable when debugging the multitude of events flying around a Prism app!

Related

Is it possible to create a LifeTimeScope per eventhandler with Autofac and MediatR using a decorator class

Is it possible to create a LifeTimeScope per eventhandler with Autofac and MediatR using a decorator class?
So we have two Eventhandlers listening to the same event.
A decorator is supposed to create a LifteTimeScope, resolve the decorated eventhandler and invoke the Handle method of the decorated eventhandler.
I found lot of examples of doing this with CommandHandlers.
I have played around with code similar to what is shown below.
But I cant make it work. Some posts also suggest making a autofac registrationsource.
Ihave placed a fiddle here https://dotnetfiddle.net/fw4IBw
class EventHandlerA : IAsyncNotificationHandler<AnEvent>
{
public void Handle(AnEvent theEvent)
{
}
}
class EventHandlerB : IAsyncNotificationHandler<AnEvent>
{
public void Handle(AnEvent theEvent)
{
}
}
/// <summary>
/// Wraps inner Notification Handler in Autofac Lifetime scope named
PerEventHandlerScope"
/// </summary>
/// <typeparam name="TNotification"></typeparam>
public class LifetimeScopeEventHandlerDecorator<TNotification> :
IAsyncNotificationHandler<TNotification> where TNotification : class,
IAsyncNotification
{
private readonly ILifetimeScope _scope;
private readonly Type _decoratedType;
/// <summary>
/// Const Name of Scope that dependencies can Match using
PerMatchingLifeTimeScope(LifetimeScopeEventHandlerDecorator.ScopeName)
/// </summary>
public const string ScopeName = LifeTimeScopeKeys.PerHandlerKey;
/// <summary>
/// constructor
/// </summary>
/// <param name="scope"></param>
public LifetimeScopeEventHandlerDecorator( ILifetimeScope scope, Type
decoratedType )
{
_decoratedType = decoratedType;
_scope = scope;
}
/// <summary>
/// Wraps inner Notification Handler in Autofac Lifetime scope
/// </summary>
/// <param name="notification"></param>
/// <returns></returns>
public async Task Handle( TNotification notification )
{
using ( var perHandlerScope = _scope.BeginLifetimeScope(
LifeTimeScopeKeys.PerHandlerKey ) )
{
var decoratedHandler =
perHandlerScope.ResolveKeyed<IAsyncNotificationHandler<TNotification>>(
"IAsyncNotificationHandlerKey" );
await decoratedHandler.Handle( notification );
}
}
}
Yes its possible.
Finally I figured out a solution. The code can bee seen here https://dotnetfiddle.net/fw4IBw
It involves the following steps for registration
iterate all your assemblies and get all eventHandler types
iterate all eventhandler types and register them as
Named("EventHandler", eventHandlerType) and with
.InstancePerMatchingLifetimeScope( "PerHandlerKey" );
in same loop get notification type
in same loop register a
eventhandlerFactory per eventhandler AsSelf and as
implementedInterfaces
in same loop register only one
eventHandlerDecorator per notification type .Named(
"EventHandlerDecorator", interfaceType
).AsSelf().InstancePerLifetimeScope();
For MultiInstanceFactory resolve only one decorator for the
notification c.ResolveKeyed( "EventHandlerDecorator"...
In EventHandlerDecorator do..
Resolve all Factories for NotificationType
for each factory create per handler lifetimescope
create handler
invoke handler

Pub/sub dependency on message type causing indirect coupling between publisher and subscriber

I have an pub/sub mechanism in a C# solution for event aggregation.
While the coupling between publishers and subscribers in theory is low, it seems to me that the messages themselves might introduction tight coupling.
This is what I have done so far:
public class A : ISubscribe<StartMessage> {
void Handle(StartMessage msg){
...
}
}
public class B {
void PublishStart() {
eventAggregator.Publish(new StartMessage())
...
}
}
which is good because it doesn't depend on magic strings, but no matter where StartMessage resides (it being in A, B or probably more sensible in it's own class) both A and B will depend on StartMessage.
Is there a way of avoiding this without resorting to magic strings?
I have thought about using some interface e.g. IMessage that messages implement but then all listeners would be receive IMessages and would have to filter on something like if (IMessage is StartMessage) which is horrible.
EDIT:
A problem is that ViewModels in MVVM-ish patterns that communicate via en EventAggregator will have to depends on specific messages. This means that reusing such VM's can be difficult when adding new messages.
Answering more generally: I think that it is logically unavoidable that the specific producer of a message as well as the specific consumer/subscriber have a tight dependency on the actual message implementations they produce or consume.
By contrast, The message distribution framework can of course be generic and needs no knowledge beyond simple interfaces (i.e. queue opaque subscribers and pass opaque messages to them).
This allows to limit knowledge of message implementation details to the places where they are actually consumed. One way to achieve this is to put subscribers and messages in inheritance hierarchies. Each concrete subscriber type is only concerned with specific message types. Knowledge about that specific message implementation is only necessary in that specific subscriber class, and in that specific subscriber class no knowledge about any other messages is needed. (That seemed to be your valid concern.) This makes it possible to extend existing message/subscriber systems: One can add messages and corresponding subscribers without changes to existing code by implementing the appropriate classes, and the pre-existing user code plays well when formerly unknown messages (and their subscribers) are added.
This will, as far as I can see, involve a run time type check, which you seem to dread. To use function overloads is not possible because overload resolution is a compile time feature which cannot be used for types unknown at compile time. Virtual function calls, by contrast, are a run time feature. But the explicit run time check can be made painless if subscribers inherit from a base class which is a template whose type parameter is the message type; the template code implements the message type check for all derived subscribers. Perhaps this example is an inspiration:
//// part of the library code for message passing ////////////////////////////
public interface IMessage{}
public interface ISubscribe
{
void Handle(IMessage msg);
}
/// <summary>
/// A base class for IMessage subscribers who are only
/// interested in a particular message type.
/// The Handle() funciton performs the type check and
/// calls HandleImpl() if and only if the message has the proper type
/// given in the type parameter. Derived
/// subscribers only need to implement HandleImpl().
/// </summary>
/// <typeparam name="MessageT">The message type the derived subscriber
/// is interested in.</typeparam>
public abstract class SubscriberBaseT<MessageT>: ISubscribe
where MessageT: class, IMessage
{
/// <summary>
/// Check whether the message is of the type we are interested in.
/// If yes, call our handling implementation.
/// Note: No knowledge of specific message types or internals.
/// </summary>
/// <param name="msg">The IMessage to check</param>
public void Handle(IMessage msg)
{
var messageTmsg = msg as MessageT;
if( msg != null )
{
HandleImpl(messageTmsg);
}
}
/// <summary>
/// To be implemented by derived classes.
/// Do something with the message type we are concerned about.
/// </summary>
/// <param name="concreteMsg">A message of the type we are
/// interested in.</param>
public abstract void HandleImpl(MessageT concreteMsg);
}
//// user code file1.cs ////////////////////////////
/// <summary>
/// A user defined message
/// </summary>
public class Msg1T: IMessage { /***/ }
/// <summary>
/// A user defined handler interested only in Msg1T messages.
/// Note: No knowledge of other message types.
/// </summary>
public class Msg1SubscrT: SubscriberBaseT<Msg1T>
{
public override void HandleImpl(Msg1T msg)
{
// do something with this particular message
}
}
//// user code file2.cs ////////////////////////////
/// <summary>
/// Another user defined message
/// </summary>
public class Msg2T: IMessage { /***/ }
/// <summary>
/// Another user defined handler,
/// interested only in Msg2T messages
/// </summary>
public class Msg2SubscrT: SubscriberBaseT<Msg2T>
{
public override void HandleImpl(Msg2T msg)
{
// do something with this Msg2T
}
}
//// user code file3.cs ////////////////////////////
// ...
If there is only a finite number of different message types which are known at compile time you can exploit the virtual dispatching mechanism to different implementations of a IMessage interface:
interface IMessage
{
void CallSubscriberHandle(ISubscribe subscriber);
}
class StartMsg: IMessage
{
public void CallSubscriberHandle(ISubscribe subscr) { subscr.Handle(this); }
}
class ProgressMsg: IMessage
{
public void CallSubscriberHandle(ISubscribe subscr) { subscr.Handle(this); }
}
class EndMsg: IMessage
{
public void CallSubscriberHandle(ISubscribe subscr) { subscr.Handle(this); }
}
This pattern calls different Handle() functions depending on the run time type of the message, because that type is statically known at the point in the code where Handle is called. The subscriber must implement a new method for every message type, so these types must be known (or a fallback will be called). This is a tighter coupling than just a one-method interface, but still the message knows only an interface. (If you have a large number of message types or these types are not known at compile time you are out of luck, but I do not see how you get away then without strings or other data criteria.)
All subsriber implementations must implement all Handle() overloads:
interface ISubscribe
{
void Handle(IMessage msg);
void Handle(StartMsg msg);
//void Handle(ProgressMsg msg); // don't handle this one
void Handle(EndMsg msg);
}
Perhaps somewhat surprisingly, this code results in each message being handled differently:
foreach(IMessage msg in msgList)
{
// Use virtual function dispatch in the message instead of
// a switch/case at the message handler
msg.CallSubscriberHandle(msgHandler);
}
For completeness here is a working example, not exactly minimal though:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace VirtualDispatchTest
{
interface ISubscribe
{
void Handle(IMessage msg);
void Handle(StartMsg msg);
//void Handle(ProgressMsg msg); // don't handle this one
void Handle(EndMsg msg);
}
class MsgHandler: ISubscribe
{
// fall back, for unknown message types
public void Handle(IMessage msg) { Console.Out.WriteLine("I'm not sure what I'm doing right now"); }
public void Handle(StartMsg sm) { Console.Out.WriteLine("Here we go!"); }
// Let's make his message type unknown
//public void Handle(ProgressMsg pm) { Console.Out.WriteLine("Having fun making progress..."); }
public void Handle(EndMsg em) { Console.Out.WriteLine("Bummer, already over."); }
}
interface IMessage
{
void CallSubscriberHandle(ISubscribe subscriber);
}
class StartMsg: IMessage
{
public void CallSubscriberHandle(ISubscribe subscr) { subscr.Handle(this); }
}
class ProgressMsg: IMessage
{
public void CallSubscriberHandle(ISubscribe subscr) { subscr.Handle(this); }
}
class EndMsg: IMessage
{
public void CallSubscriberHandle(ISubscribe subscr) { subscr.Handle(this); }
}
class Program
{
static List<IMessage> msgList = new List<IMessage>();
static MsgHandler msgHandler = new MsgHandler();
static void Main(string[] args)
{
msgList.Add(new StartMsg());
msgList.Add(new ProgressMsg());
msgList.Add(new EndMsg());
msgList.Add(new StartMsg());
msgList.Add(new EndMsg());
foreach(IMessage msg in msgList)
{
// Use virtual function dispatch in the message instead of
// a switch/case at the message handler
msg.CallSubscriberHandle(msgHandler);
}
}
}
}
Output:
Here we go!
I'm not sure what I'm doing right now
Bummer, already over.
Here we go!
Bummer, already over.

Default the LifetimeManager to the singleton manager (ContainerControlledLifetimeManager)?

I'm using a Unity IoC container to do Dependency Injection. I designed my system around the idea that, at least for a single resolution, all types in the hierarchy would behave as singletons, that is, same type resolutions within that hierarchy would lead to the same instances.
However, I (a) would like to scan my assemblies to find types and (b) don't want to explicitly tell unity that every type is to be resolved as a singleton when registering types in the configuration file.
So, is there a way to tell unity to treat all registered mappings as singleton?
In case anyone is still looking for this... The following extension will change the default, while still allowing you to override with some other manager:
/// <summary>
/// This extension allows the changing of the default lifetime manager in unity.
/// </summary>
public class DefaultLifetimeManagerExtension<T> : UnityContainerExtension where T : LifetimeManager
{
/// <summary>
/// Handle the registering event
/// </summary>
protected override void Initialize()
{
Context.Registering += this.OnRegister;
}
/// <summary>
/// Remove the registering event
/// </summary>
public override void Remove()
{
Context.Registering -= this.OnRegister;
}
/// <summary>
/// Handle the registration event by checking for null registration
/// </summary>
private void OnRegister(object sender, RegisterEventArgs e)
{
if (e.LifetimeManager == null)
{
var lifetimeManager = (LifetimeManager)Activator.CreateInstance(typeof (T));
// Set this internal property using reflection
lifetimeManager
.GetType()
.GetProperty("InUse", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(lifetimeManager, true);
Context.Policies.Set<ILifetimePolicy>(lifetimeManager, new NamedTypeBuildKey(e.TypeTo, e.Name));
if (lifetimeManager is IDisposable)
{
Context.Lifetime.Add(lifetimeManager);
}
}
}
}
You could add a Unity extension at the 'Lifetime' stage of the resolution pipeline and in it always use a ContainerControlledLifetimeManager instance.
Edit: In fact this post has the exact example:
https://unity.codeplex.com/discussions/352179

What am I doing wrong with my Workflow bookmark pick trigger?

I have a NativeActivity derived activity that I wrote that is to use bookmarks as a trigger for a pick branch. Using something I found on MSDN I tried writing this to trigger the branch. The branch contains activities that fire service callbacks to remote clients via send activities. If I set a delay for the trigger, callbacks fire to the clients successfully. If I use my code activity, the pick branch activities don't fire.
public sealed class UpdateListener : NativeActivity<ClientUpdate>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(this.OnResumeBookmark));
}
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj )
{
Result.Set(context, (ClientUpdate)obj);
}
}
So it takes an arg to set the bookmark name for future bookmark references to execute the trigger. OnResumeBoookmark() takes in a ClientUpdate object that is passed by my application that is hosting the workflowapp. The activity is to return the object so the ClientUpdate can be passed to the workflow and have it sent to the remote clients via the send activity in the pick branch. In theory anyways.
For some reason it seems to be correct but feels wrong. I'm not sure if I should write the Activity in a different way to take care of what I need for my WF service.
I think your intentions would be a bit clearer if you created an extension (that implements IWorkflowInstanceExtension) to perform your action here.
For example:
public sealed class AsyncWorkExtension
: IWorkflowInstanceExtension
{
// only one extension per workflow
private WorkflowInstanceProxy _proxy;
private Bookmark _lastBookmark;
/// <summary>
/// Request the extension does some work for an activity
/// during which the activity will idle the workflow
/// </summary>
/// <param name="toResumeMe"></param>
public void DoWork(Bookmark toResumeMe)
{
_lastBookmark = toResumeMe;
// imagine I kick off some async op here
// when complete system calls WorkCompleted below
// NOTE: you CANNOT block here or you block the WF!
}
/// <summary>
/// Called by the system when long-running work is complete
/// </summary>
/// <param name="result"></param>
internal void WorkCompleted(object result)
{
//NOT good practice! example only
//this leaks resources search APM for details
_proxy.BeginResumeBookmark(_lastBookmark, result, null, null);
}
/// <summary>
/// When implemented, returns any additional extensions
/// the implementing class requires.
/// </summary>
/// <returns>
/// A collection of additional workflow extensions.
/// </returns>
IEnumerable<object> IWorkflowInstanceExtension
.GetAdditionalExtensions()
{
return new object[0];
}
/// <summary>
/// Sets the specified target
/// <see cref="WorkflowInstanceProxy"/>.
/// </summary>
/// <param name="instance">The target workflow instance to set.</param>
void IWorkflowInstanceExtension
.SetInstance(WorkflowInstanceProxy instance)
{
_proxy = instance;
}
}
Within the Activity, you'd use this thusly:
var ext = context.GetExtension<AsyncWorkExtension>();
var bookmark = context.CreateBookmark(BookmarkCallback);
ext.DoWork(bookmark);
return;
This way is much more explicit (instead of using the bookmark name to convey meaning to the "outside" world) and is much easier to extend if, say, you require to send out more information than a bookmark name.
Is there something actually resuming the bookmark here? If not the workflow will wait very patiently and nothing will happen.

raising a vb6 event using interop

I have a legacy VB6 component that I've imported into VS using tlbimp.exe to generate my interop assembly. The VB6 component defines an event that allows me to pass messages within VB6.
Public Event Message(ByVal iMsg As Variant, oCancel As Variant)
I would really like to be able to raise this even in my C# program, but its getting imported as an event, not a delegate or something else useful. So, I can only listen, but never fire. Does anyone know how to fire an event contained within VB6? The C# event looks like
[TypeLibType(16)]
[ComVisible(false)]
public interface __MyObj_Event
{
event __MyObj_MessageEventHandler Message;
}
I unfortunately cannot change the VB6 code. Thanks.
Actually, hope is not lost yet. It is possible to raise an event on a COM object from outside of the object's class. This functionality is actually provided by COM itself, although in an indirect manner.
In COM, events work on a publish/subscribe model. A COM object that has events (the "event source") publishes events, and one or more other COM objects subscribe to the event by attaching an event handler to the source object (the handlers are called "event sinks"). Normally, the source object raises an event by simply looping through all the event sinks and calling the appropriate handler method.
So how does this help you? It just so happens that COM lets you query an event source for a list of all the event sink objects currently subscribed to the source object's events. Once you have a list of event sink objects, you can simulate raising an event by invoking each of the sink object's event handlers.
Note: I'm over-simplifying the details and being liberal with some of the terminology, but that's the short (and somewhat politically incorrect) version of how events work in COM.
You can take advantage of this knowledge to raise events on a COM object from external code. In fact, it is possible to do all of this in C#, with the help of the COM interop support in the System.Runtime.Interop and System.Runtime.Interop.ComTypes namespaces.
EDIT
I wrote a utility class that will allow you to raise events on a COM object from .NET. It's pretty easy to use. Here is an example using the event interface from your question:
MyObj legacyComObject = new MyObj();
// The following code assumes other COM objects have already subscribed to the
// MyObj class's Message event at this point.
//
// NOTE: VB6 objects have two hidden interfaces for classes that raise events:
//
// _MyObj (with one underscore): The default interface.
// __MyObj (with two underscores): The event interface.
//
// We want the second interface, because it gives us a delegate
// that we can use to raise the event.
// The ComEventUtils.GetEventSinks<T> method is a convenience method
// that returns all the objects listening to events from the legacy COM object.
// set up the params for the event
string messageData = "Hello, world!";
bool cancel = false;
// raise the event by invoking the event delegate for each connected object...
foreach(__MyObj sink in ComEventUtils.GetEventSinks<__MyObj>(legacyComObject))
{
// raise the event via the event delegate
sink.Message(messageData, ref cancel);
if(cancel == true)
{
// do cancel processing (just an example)
break;
}
}
Below is the code for the ComEventUtils class (as well as helper class, SafeIntPtr, because I'm paranoid and wanted a nice way to deal with the IntPtrS needed by the COM-related code):
Disclaimer: I haven't thoroughly tested the code below. The code performs manual memory management in a few places, and therefore there is the possibility that it could introduce memory leaks into your code. Also, I didn't add error-handling to the code, because this is only an example. Use with care.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using COM = System.Runtime.InteropServices.ComTypes;
namespace YourNamespaceHere
{
/// <summary>
/// A utility class for dealing with COM events.
/// Needs error-handling and could potentially be refactored
/// into a regular class. Also, I haven't extensively tested this code;
/// there may be a memory leak somewhere due to the rather
/// low-level stuff going on in the class, but I think I covered everything.
/// </summary>
public static class ComEventUtils
{
/// <summary>
/// Get a list of all objects implementing an event sink interface T
/// that are listening for events on a specified COM object.
/// </summary>
/// <typeparam name="T">The event sink interface.</typeparam>
/// <param name="comObject">The COM object whose event sinks you want to retrieve.</param>
/// <returns>A List of objects that implement the given event sink interface and which
/// are actively listening for events from the specified COM object.</returns>
public static List<T> GetEventSinks<T>(object comObject)
{
List<T> sinks = new List<T>();
List<COM.IConnectionPoint> connectionPoints = GetConnectionPoints(comObject);
// Loop through the source object's connection points,
// find the objects that are listening for events at each connection point,
// and add the objects we are interested in to the list.
foreach(COM.IConnectionPoint connectionPoint in connectionPoints)
{
List<COM.CONNECTDATA> connections = GetConnectionData(connectionPoint);
foreach (COM.CONNECTDATA connection in connections)
{
object candidate = connection.pUnk;
// I tried to avoid relying on try/catch for this
// part, but candidate.GetType().GetInterfaces() kept
// returning an empty array.
try
{
sinks.Add((T)candidate);
}
catch { }
}
// Need to release the interface pointer in each CONNECTDATA instance
// because GetConnectionData implicitly AddRef's it.
foreach (COM.CONNECTDATA connection in connections)
{
Marshal.ReleaseComObject(connection.pUnk);
}
}
return sinks;
}
/// <summary>
/// Get all the event connection points for a given COM object.
/// </summary>
/// <param name="comObject">A COM object that raises events.</param>
/// <returns>A List of IConnectionPoint instances for the COM object.</returns>
private static List<COM.IConnectionPoint> GetConnectionPoints(object comObject)
{
COM.IConnectionPointContainer connectionPointContainer = (COM.IConnectionPointContainer)comObject;
COM.IEnumConnectionPoints enumConnectionPoints;
COM.IConnectionPoint[] oneConnectionPoint = new COM.IConnectionPoint[1];
List<COM.IConnectionPoint> connectionPoints = new List<COM.IConnectionPoint>();
connectionPointContainer.EnumConnectionPoints(out enumConnectionPoints);
enumConnectionPoints.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnectionPoints.Next(1, oneConnectionPoint, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectionPoints.Add(oneConnectionPoint[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectionPoints;
}
/// <summary>
/// Returns a list of CONNECTDATA instances representing the current
/// event sink connections to the given IConnectionPoint.
/// </summary>
/// <param name="connectionPoint">The IConnectionPoint to return connection data for.</param>
/// <returns>A List of CONNECTDATA instances representing all the current event sink connections to the
/// given connection point.</returns>
private static List<COM.CONNECTDATA> GetConnectionData(COM.IConnectionPoint connectionPoint)
{
COM.IEnumConnections enumConnections;
COM.CONNECTDATA[] oneConnectData = new COM.CONNECTDATA[1];
List<COM.CONNECTDATA> connectDataObjects = new List<COM.CONNECTDATA>();
connectionPoint.EnumConnections(out enumConnections);
enumConnections.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnections.Next(1, oneConnectData, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectDataObjects.Add(oneConnectData[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectDataObjects;
}
} //end class ComEventUtils
/// <summary>
/// A simple wrapper class around an IntPtr that
/// manages its own memory.
/// </summary>
public class SafeIntPtr : IDisposable
{
private bool _disposed = false;
private IntPtr _pInt = IntPtr.Zero;
/// <summary>
/// Allocates storage for an int and assigns it to this pointer.
/// The pointed-to value defaults to 0.
/// </summary>
public SafeIntPtr()
: this(0)
{
//
}
/// <summary>
/// Allocates storage for an int, assigns it to this pointer,
/// and initializes the pointed-to memory to known value.
/// <param name="value">The value this that this <tt>SafeIntPtr</tt> points to initially.</param>
/// </summary>
public SafeIntPtr(int value)
{
_pInt = Marshal.AllocHGlobal(sizeof(int));
this.Value = value;
}
/// <summary>
/// Gets or sets the value this pointer is pointing to.
/// </summary>
public int Value
{
get
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
return Marshal.ReadInt32(_pInt);
}
set
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
Marshal.WriteInt32(_pInt, Value);
}
}
/// <summary>
/// Returns an IntPtr representation of this SafeIntPtr.
/// </summary>
/// <returns></returns>
public IntPtr ToIntPtr()
{
return _pInt;
}
/// <summary>
/// Deallocates the memory for this pointer.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
Marshal.FreeHGlobal(_pInt);
_disposed = true;
}
}
~SafeIntPtr()
{
if (!_disposed)
Dispose();
}
} //end class SafeIntPtr
} //end namespace YourNamespaceHere
In VB6 the event can only be raised from within the class (or Form as the case may be) declaring the Event. To force a event to be raised in VB6 you need to expose a method on the class to do this. If you don't have the source code the you are out of luck.
From the documentation
RaiseEvent eventname [(argumentlist)]
The required eventname is the name of
an event declared within the module
and follows Basic variable naming
conventions.
For example
Option Explicit
Private FText As String
Public Event OnChange(ByVal Text As String)
'This exposes the raising the event
Private Sub Change(ByVal Text As String)
RaiseEvent OnChange(Text)
End Sub
Public Property Get Text() As String
Text = FText
End Property
Public Property Let Text(ByVal Value As String)
FText = Value
Call Change(Value)
End Property
Sorry to be the bearer of bad news.

Categories