SDL Tridion EventSubscription UnSubscribe Issue - c#

We are trying to Unsubscribe from the eventSubscripton in our Eventing Code. We are using the following code
[TcmExtension("EventHandlerExtension")]
public class EventHandler : TcmExtension, IDisposable
{
private EventSubscription componentSaveSubscription = null;
private EventSubscription componentPublishSubscription = null;
#region Public Methods
/// <summary>
/// Handle for Eventing System
/// </summary>
public EventHandler()
{
Subscribe();
}
/// <summary>
/// Subscribe Events
/// </summary>
public void Subscribe()
{
//News Article Page created when component Save
componentSaveSubscription = EventSystem.Subscribe<Component, SaveEventArgs>(OnComponentSavePost, EventPhases.TransactionCommitted);
//EventSystem.Subscribe<Component, SaveEventArgs>(OnComponentSavePost, EventPhases.TransactionCommitted);
componentPublishSubscription = EventSystem.Subscribe<Component, PublishOrUnPublishEventArgs>(OnComponentPublishOrUnPublishPost, EventPhases.TransactionCommitted);
//EventSystem.Subscribe<StructureGroup, PublishOrUnPublishEventArgs>(OnStructureGroupPublishInitiated, EventPhases.TransactionCommitted);
}
/// <summary>
/// IDisposable Implementation
/// </summary>
public void Dispose()
{
if (componentSaveSubscription != null) componentSaveSubscription.Unsubscribe();
if (componentPublishSubscription != null) componentPublishSubscription.Unsubscribe();
}}
What we have observed is that once “{EventSubsciption}.Unsubscribe” is called, eventing stops working for the subsequent events for which it is supposed to work. Once the event system related services are restarted, the eventing code works as expected for the first time only and never gets called for subsequent events(for which it was supposed to work).

Try removing the Dispose method and see if that makes a difference. It's possible that Tridion instantiates the Event Handler upon the first instance of an event being fired, and then never does it again until the system is restarted. So if you unsubscribe and dispose, then your class won't be instantiated again. It's also possible that something else in your environment is interfering. Hard to say, but try by removing the Dispose first.
My boilerplate Handler looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Tridion.ContentManager;
using Tridion.ContentManager.CommunicationManagement;
using Tridion.ContentManager.ContentManagement;
using Tridion.ContentManager.ContentManagement.Fields;
using Tridion.ContentManager.Extensibility;
using Tridion.ContentManager.Extensibility.Events;
namespace NicksEventSystem
{
[TcmExtension("NicksEventSystemExtension")]
public class NicksEventHandler : TcmExtension
{
public NicksEventHandler()
{
Subscribe();
}
private void Subscribe()
{
EventSystem.Subscribe<Component, FinishActivityEventArgs>(MyEvent, EventPhases.TransactionCommitted);
}
private void MyEvent(Component wfComponent)
{
//... Do stuff!
}
}
}

Related

How to prevent InitializeComponent code from constructing a member field / property

Firstly I'm very new to WPF (coming from WinForms) and I'm facing quite a difficult problem. Sorry if it's rather lengthy, I've tried to give it structure and shorten it down.
Environment (brief)
I'm using a custom user control CaptureControl inside my MainWindow. This control has an encapsuled field / property of an InputManager object. This InputManager object is instantiated a single time in the global App class. It is later assigned to user control members (e.g. the CaptureControl).
Problem
My problem with this is that for some reason the InitializeComponent() call inside my CaptureControl constructs an instance of InputManager somewhere in external code. The InputManager registers event handlers to a static class during construction and needs to be disposed in order to unregister these handlers again. Sadly whatever constructs an instance of the InputManager class does not dispose it properly again or keeps it referenced at some place where I can't access it. The _InputManager field remains being null.
This is the problematic code:
public partial class CaptureControl : UserControl
{
/// <summary>
/// <para>Gets or sets the Input Manager to be used</para>
/// </summary>
public InputManager InputManager
{
get => _InputManager;
set
{
_InputManager = value;
lstTriggeredKeys.DataContext = _InputManager;
}
}
private InputManager _InputManager;
public CaptureControl()
{
InitializeComponent(); // This calls external code which constructs an instance of InputManager
}
}
This invokes the constructor of my InputManager class:
public class InputManager : IDisposable
{
/// <summary>
/// <para>Constructs the input helper</para>
/// <para>Registers static keyboard events</para>
/// </summary>
public InputManager()
{
Interceptor.OnKeyInput += Interceptor_OnKeyInput;
}
/// <summary>
/// <para>Releases static keyboard events</para>
/// </summary>
public void Dispose()
{
Interceptor.OnKeyInput -= Interceptor_OnKeyInput;
}
private void Interceptor_OnKeyInput(KeyInput input)
{ ... }
}
Which results in two problems:
There is an event handler unnecessarily being invoked on an anonymous object
The dependent clean-up logic on the static class is not fired if the event handler count is higher than 0
Question
I have difficulties understanding why the InitializeComponent() call would construct an instance of InputManager in the first place.
How can this be prevented? How can I prevent the external code behind
InitializeComponent() to construct an instance of
InputManager for whatever reason?
Call Stack
The external code call stack behind InitializeComponent() is huge and ends with an invisible native to managed transition. The last visible record before reaching the InputManager constructor is:
> InputData.dll!InputData.InputManager.InputManager() Line 40 C#
> [Native to Managed Transition]
> [Managed to Native Transition]
> mscorlib.dll!System.RuntimeType.CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache = true, ref System.Threading.StackCrawlMark stackMark)
> ...
> InputH.exe!InputH.CaptureControl.InitializeComponent() Line 1 C#
> ...
Environment (detailed)
The App class which inherits the System.Windows.Application class and constructs the InputManager object. The object is assigned to one or more user controls later on:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
// Single instance / global / shared object
public InputManager InputManager { get; private set; } = new InputManager();
// Disposing of the object
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
InputManager.Dispose();
}
}
The global InputManger instance is assigned to the user control in the Constructor of my MainWindow. It also assigns the shared single instance InputManager object to it:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
captureControl.InputManager = ((App)Application.Current).InputManager;
}
}

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

Trace PRISM / CAL events (best practice?)

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!

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.

Preventing same Event handler assignment multiple times

If I am assigning an event handler at runtime and it is in a spot that can be called multiple times, what is the recommended practice to prevent multiple assignments of the same handler to the same event.
object.Event += MyFunction
Adding this in a spot that will be called more than once will execute the handler 'n' times (of course).
I have resorted to removing any previous handler before trying to add via
object.Event -= MyFunction;
object.Event += MyFunction;
This works but seems off somehow. Any suggestions on proper handling ;) of this scenario.
Baget is right about using an explicitly implemented event (although there's a mixture there of explicit interface implementation and the full event syntax). You can probably get away with this:
private EventHandler foo;
public event EventHandler Foo
{
add
{
// First try to remove the handler, then re-add it
foo -= value;
foo += value;
}
remove
{
foo -= value;
}
}
That may have some odd edge cases if you ever add or remove multicast delegates, but that's unlikely. It also needs careful documentation as it's not the way that events normally work.
I tend to add an event handler in a path that's executed once, for example in a constructor.
You can implement your own storage of the delgates, and check for uniqueness when adding them to the event. See EventOwner2 class below for an example. I don't know how this is doing performance wise, but than again, that is not always an issue.
using System;
using System.Collections.Generic;
namespace EventExperiment
{
class Program
{
static void Main(string[] args)
{
IEventOwner e=new EventOwner2();
Subscriber s=new Subscriber(e);
e.RaiseSome();
Console.ReadKey();
}
}
/// <summary>
/// A consumer class, subscribing twice to the event in it's constructor.
/// </summary>
public class Subscriber
{
public Subscriber(IEventOwner eventOwner)
{
eventOwner.SomeEvent += eventOwner_SomeEvent;
eventOwner.SomeEvent += eventOwner_SomeEvent;
}
void eventOwner_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine(DateTimeOffset.Now);
}
}
/// <summary>
/// This interface is not essensial to this point. it is just added for conveniance.
/// </summary>
public interface IEventOwner
{
event EventHandler<EventArgs> SomeEvent;
void RaiseSome();
}
/// <summary>
/// A traditional event. This is raised for each subscription.
/// </summary>
public class EventOwner1 : IEventOwner
{
public event EventHandler<EventArgs> SomeEvent = delegate { };
public void RaiseSome()
{
SomeEvent(this,new EventArgs());
}
}
/// <summary>
/// A custom event. This is raised only once for each subscriber.
/// </summary>
public class EventOwner2 : IEventOwner
{
private readonly List<EventHandler<EventArgs>> handlers=new List<EventHandler<EventArgs>>();
public event EventHandler<EventArgs> SomeEvent
{
add
{
lock (handlers)
if (handlers!=null&&!handlers.Contains(value))
{
handlers.Add(value);
}
}
remove
{
handlers.Remove(value);
}
}
public void RaiseSome()
{
EventArgs args=new EventArgs();
lock(handlers)
foreach (EventHandler<EventArgs> handler in handlers)
{
handler(this,args);
}
}
}
}
What is the access modifier of 'object'?
If it's private, you only need to worry about the containing object setting the event handler.
If it's internal, you only need to worry about the containing assembly setting the event handler.
If it's public, then it's wide-open.
If 'object' can be made private on the containing class, you can make your checks much more efficient by controlling event handler assignment in the local class.
If 'internal' or 'public' and uniqueness is a requirement, go with a wrapper class that hides 'object' and instead exposes a method for assigning an event handler with your checks behind it to ensure uniqueness.

Categories