|
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
namespace ManagedOffice
{
/// <summary>
/// A class which provides events for a COM object
/// </summary>
/// <remarks>
/// For more information on late-bound COM events, see
/// <a href="http://www.codeproject.com/csharp/ZetaLateBindingComEvents.asp" target="_blank">Uwe Keim's article</a>
/// </remarks>
internal sealed class ComEventProvider : IDisposable
{
private UCOMIConnectionPointContainer _connectionPointContainer;
private UCOMIConnectionPoint _connectionPoint;
private ComEventSink _eventSink;
private Type _eventSinkType;
private Guid _connectionPointGuid;
private Hashtable _eventKeys;
private WeakReference _owner;
/// <summary>
/// Constructor
/// </summary>
/// <param name="target">
/// The target COM object
/// </param>
/// <param name="interceptType">
/// The intercepted type.
/// </param>
/// <param name="owner">
/// The owner <see cref="COMWrapper"/>
/// </param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="target"/> is <see langword="null"/></para>
/// <para>-or-</para>
/// <para><paramref name="interceptType"/> is <see langword="null"/></para>
/// </exception>
/// <exception cref="NotImplementedException">
/// <paramref name="interceptType"/> does not define the
/// <see cref="ComEventsAttribute"/> attribute.
/// </exception>
public ComEventProvider(object target, Type interceptType, COMWrapper owner)
{
if (null == target) throw new ArgumentNullException("target");
if (null == interceptType) throw new ArgumentNullException("interceptType");
ComEventsAttribute value = ComEventsAttribute.GetAttribute(interceptType);
if (null == value) throw new NotImplementedException("Event provider attribute not found.");
_connectionPointContainer = (UCOMIConnectionPointContainer)target;
_eventSinkType = value.EventSinkType;
_connectionPointGuid = value.EventsGuid;
if (null != owner) _owner = new WeakReference(owner);
}
/// <summary>
/// Finalizer
/// </summary>
~ComEventProvider()
{
Debug.WriteLine("Finalize ComEventProvider");
Dispose(false);
}
/// <summary>
/// Cleans up the COM object references.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Cleans up the COM object references.
/// </summary>
/// <param name="disposing">
/// <see langword="true"/> if this was called from the
/// <see cref="IDisposable"/> interface.
/// </param>
private void Dispose(bool disposing)
{
lock(this)
{
if (null != _eventSink)
{
_eventSink.Dispose();
_eventSink = null;
}
if (null != _connectionPoint)
{
while( Marshal.ReleaseComObject(_connectionPoint) > 0 );
_connectionPoint = null;
}
if (null != _connectionPointContainer)
{
while( Marshal.ReleaseComObject(_connectionPointContainer) > 0 );
_connectionPointContainer = null;
}
}
}
/// <summary>
/// Initialize the event sink.
/// </summary>
private void Initialize()
{
if (null == _connectionPointContainer)
throw new ObjectDisposedException("ComEventProvider");
UCOMIConnectionPoint connectionPoint;
Guid pointGuid = _connectionPointGuid;
_connectionPointContainer.FindConnectionPoint(ref pointGuid, out connectionPoint);
_connectionPoint = connectionPoint;
_eventSink = (ComEventSink)Activator.CreateInstance(_eventSinkType);
_eventSink.Connect(_connectionPoint);
_eventSink.Owner = _owner;
}
/// <summary>
/// Returns the key for the specified event.
/// </summary>
/// <param name="eventName">
/// The name of the event.
/// </param>
/// <returns>
/// The event key.
/// </returns>
/// <remarks>
/// The event sink class should have a static readonly object
/// field called {EventName}Event, which is used as the
/// key for the event. If it doesn't, a new object will be
/// created and used as the key.
/// </remarks>
private object GetEventKey(string eventName)
{
if (null != _eventKeys && _eventKeys.ContainsKey(eventName))
return _eventKeys[eventName];
object key = null;
FieldInfo field = _eventSinkType.GetField(eventName + "Event",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (null != field) key = field.GetValue(_eventSink);
if (null == key)
{
key = new object();
_eventSink.AddUnmappedEventKey(eventName, key);
}
if (null == _eventKeys)
_eventKeys = new Hashtable(
CaseInsensitiveHashCodeProvider.Default,
CaseInsensitiveComparer.Default);
_eventKeys[eventName] = key;
return key;
}
/// <summary>
/// Adds an event handler.
/// </summary>
/// <param name="eventName">
/// The name of the event.
/// </param>
/// <param name="value">
/// The event handler delegate.
/// </param>
public void AddEventHandler(string eventName, Delegate value)
{
lock(this)
{
if (null == _eventSink) Initialize();
_eventSink.AddEventHandler(GetEventKey(eventName), value);
}
}
/// <summary>
/// Removes an event handler.
/// </summary>
/// <param name="eventName">
/// The name of the event.
/// </param>
/// <param name="value">
/// The event handler delegate.
/// </param>
public void RemoveEventHandler(string eventName, Delegate value)
{
lock(this)
{
if (null == _eventSink) return;
_eventSink.RemoveEventHandler(GetEventKey(eventName), value);
}
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.