If I have interface (example from apress book) which implements IDisposable like this
public interface IArchitectRepository : IDisposable
{
IEnumerable<Architect> GetArchitects();
Architect GetAchitectDetails(int id);
void Create(Achitect a);
void Update(Achitect a);
void Delete(int id);
void Save();
}
how would I implement Dispose method in derived class?
In book the it's left as NotImplementedException.
There is no reason why you would implement the Dispose pattern any differently than normal.
See Implementing a Dispose Method
Unlike most interfaces which imply obligations on the part of an implementing class, the only obligation of a class which implements IDisposable is to regard a call to Dispose as adequate notice that an instance is going to be abandoned. The real obligations imparted by IDisposable fall upon instance owners: if a type implements IDisposable, each instance should have one owner who is obligated to ensure that its Dispose method will gets called before it's abandoned; the owner may do this either by calling Dispose itself, or passing ownership to a new owner who would then assume the obligation.
Note that the owner of an IDisposable instance doesn't care what Dispose does provided that the instance may be safely abandoned after it returns. If some particular object can be safely abandoned before Dispose is called, there's no need for the Dispose method to do anything. It should not throw a NotImplementedException or NotSupportedException, since it should be no able to doing everything it needs to do (i.e. nothing).
You should look at MSDN for the complete example: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx
The minimum would be:
public class ArchitectRepository : IArchitectRepository
{
// implement all the IArchitectRepository methods
public void Dispose()
{
// assuming your repository has a connection property
if (this.Connection != null)
this.Connection.Close();
// do the same for all other disposable objects your repository has created.
}
}
Whiting the concrete implementation you can overwrite as follows
public bool IsDisposed { get; set; }
public bool IsLockedForDisposing { get; set; }
/// <summary>
/// Dispose the Loaded Context
/// </summary>
///
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
// Disposing of managed code
context.Dispose();
// GC.Collect(context);
}
this.disposed = true;
}
}
public void Dispose()
{
if (!IsLockedForDisposing)
{
IsDisposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
I have some example code posted here (and included below): How do you properly implement the IDisposable pattern?
Microsoft has slightly more in-depth details here: Implement a Dispose method
#region IDisposable base-class implementation
//TODO remember to make this class inherit from IDisposable -> MyDisposableClass : IDisposable
/// <summary>
/// Gets or sets a value indicating whether this instance is disposed.
/// </summary>
/// <value>
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
/// </value>
/// <remarks>Default initialization for a bool is 'false'</remarks>
private bool IsDisposed { get; set; }
/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
// Always use SuppressFinalize() in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize(this);
}
/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"><c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.</param>
/// <remarks>
/// <list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> equals <c>false</c>, the method has been called
/// by the runtime from inside the finalizer and you should not reference
/// other objects. Only unmanaged resources can be disposed.</item></list>
/// </remarks>
protected virtual void Dispose(bool isDisposing)
{
// TODO If you need thread safety, use a lock around these
// operations, as well as in your methods that use the resource.
try
{
if (!this.IsDisposed)
{
// Explicitly set root references to null to expressly tell the GarbageCollector
// that the resources have been disposed of and its ok to release the memory
// allocated for them.
if (isDisposing)
{
// Release all managed resources here
// Need to unregister/detach yourself from the events. Always make sure
// the object is not null first before trying to unregister/detach them!
// Failure to unregister can be a BIG source of memory leaks
if (someDisposableObjectWithAnEventHandler != null)
{
someDisposableObjectWithAnEventHandler.SomeEvent -= someDelegate;
someDisposableObjectWithAnEventHandler.Dispose();
someDisposableObjectWithAnEventHandler = null;
}
// If this is a WinForm/UI control, uncomment this code
//if (components != null)
//{
// components.Dispose();
//}
}
// Release all unmanaged resources here
// (example) if (someComObject != null && Marshal.IsComObject(someComObject))
{
Marshal.FinalReleaseComObject(someComObject);
someComObject = null;
}
}
}
finally
{
this.IsDisposed = true;
}
}
//TODO Uncomment this code if this class will contain members which are UNmanaged
///// <summary>Finalizer for MyDisposableClass</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
// ~MyDisposableClass()
// {
// Dispose( false );
// }
#endregion IDisposable base-class implementation
Here is the code for properly implementing the IDisposable pattern in a derived class:
#region IDisposable derived-class implementation
/// <summary>
/// Gets or sets a value indicating whether this instance is disposed.
/// </summary>
/// <value>
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
/// </value>
/// <remarks>Default initialization for a bool is 'false'</remarks>
private bool IsDisposed { get; set; }
/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"><c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.</param>
/// <remarks>
/// <list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> equals <c>false</c>, the method has been called
/// by the runtime from inside the finalizer and you should not reference
/// other objects. Only unmanaged resources can be disposed.</item></list>
/// </remarks>
protected override void Dispose(bool isDisposing)
{
// TODO If you need thread safety, use a lock around these
// operations, as well as in your methods that use the resource.
try
{
if (!this.IsDisposed)
{
// Explicitly set root references to null to expressly tell the GarbageCollector
// that the resources have been disposed of and its ok to release the memory
// allocated for them.
if (isDisposing)
{
// Release all managed resources here
// Need to unregister/detach yourself from the events. Always make sure
// the object is not null first before trying to unregister/detach them!
// Failure to unregister can be a BIG source of memory leaks
if (someDisposableObjectWithAnEventHandler != null)
{
someDisposableObjectWithAnEventHandler.SomeEvent -= someDelegate;
someDisposableObjectWithAnEventHandler.Dispose();
someDisposableObjectWithAnEventHandler = null;
}
// If this is a WinForm/UI contrlol, uncomment this code
//if (components != null)
//{
// components.Dispose();
//}
}
// Release all unmanaged resources here
// (example)
if (someComObject != null && Marshal.IsComObject(someComObject))
{
Marshal.FinalReleaseComObject(someComObject);
someComObject = null;
}
}
}
finally
{
this.IsDisposed = true;
// explicitly call the base class Dispose implementation
base.Dispose(isDisposing);
}
}
#endregion IDisposable derived-class implementation
Related
I have a long running operation in my Winforms project. I am calling a long running synchronization operation from one to another class.
So, In Class1, I am performing a single run of the synchronization operation and calling synchronizer.Synchronize(); which is implemented in Class2.
Any type of MemberShipSynchronization service interupption or Stopped is disposed in Class1 in its Dispose().
From Class2, I am calling the populator.PopulateMembership(); which is implemented in Class3, So, the PopulateMembership() in class3 is long running process.
I want to make this operation Cancellable, means, I want to send the CancellableToken from Class1 to Class3 based on MemberShipSynchronization Service request ( i.e. Stop/Interuppted) and
when the long running operation in Class3 receive the Cancellation request that time I want to Cancel this operation.
So finally, I want to handle this operation gracefully in Class1.
Please suggest a better workaround to achieve this.
=>Class1.
public sealed class MembershipSynchronizationService : IDisposable
{
private readonly object _sync = new object();
/// <summary>
/// Performs a single run of the synchronization service.
/// </summary>
public void RunOnce()
{
lock (_sync)
{
var synchronizer = new MembershipSynchronizer(MembershipManagerUnsecured.SoleInstance.MembershipManager);
synchronizer.Synchronize();
}
}
#region Dispose pattern
/// <summary>
/// Finalizer. Cleans up resources.
/// </summary>
~MembershipSynchronizationService()
{
Dispose(false);
}
/// <summary>
/// Cleans up resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Cleans up resources.
/// </summary>
private void Dispose(bool disposing)
{
if (disposing)
{
lock (_sync)
{
// nothing to do
_ = this;
}
}
}
#endregion
}
=> Class2.
internal sealed class MembershipSynchronizer
{
/// <summary>
/// Performs a single run of the synchronization service.
/// </summary>
public void Synchronize()
{
try
{
TimeSpan elapsed;
var stopwatch = Stopwatch.StartNew();
var populator = _membershipPopulatorFactory.CreatePopulator();
populator.PopulateMembership(); // Time consuming operation.
}
catch (Exception e)
{
LoggingUtility.LogTrace(LoggingCategory.Membership, e);
}
}
=> Class3.
/// <summary>
/// The main implementation of the membership populator.
/// </summary>
public sealed class Populator : IDisposable, IMembershipPopulator
{
public void PopulateMembership()
{
// Code for some time consuming operations.
// ........................................
// ........................................
// ........................................
}
}
}
I am currently developing a unit of work and repository pattern on top of NHibernate (As a remark: I didn't made the decision for one or another pattern, therefore please don't discuss the usefulness of the repository pattern in regards to am ORM that already implements one).
I first build per documentation a singleton (using IoC configuration) Sessionmanager that creates Units of Work instances (private methods removed for readability):
public sealed class SessionManager : ISessionManager
{
#region Members
/// <summary>
/// lock to manage thread safe access to the dictionary.
/// </summary>
private readonly ReaderWriterLockSlim _lock;
/// <summary>
/// NHibernate sessionfactory to create sessions.
/// </summary>
private readonly ISessionFactory _factory;
/// <summary>
/// Units of Work that are already in use.
/// </summary>
private readonly Dictionary<string, IUnitOfWork> _units;
/// <summary>
/// Flag indicating if the manager got disposed.
/// </summary>
private bool _disposed;
#endregion
#region Constructors
/// <summary>
/// Creates a new Manager with the given Config.
/// </summary>
/// <param name="config"></param>
public SessionManager(NHibernate.Cfg.Configuration config)
{
_lock = new ReaderWriterLockSlim();
_units = new Dictionary<string, IUnitOfWork>();
_factory = config
.Configure()
.AddAssembly(typeof(SessionManager).Assembly.FullName)
.BuildSessionFactory();
_disposed = false;
}
#endregion
#region Methods
/// <summary>
/// Either creates or returns an existing unit of work.
/// </summary>
/// <param name="identifier">identifier of the uow</param>
/// <param name="access">the data access that is needed for this uow.</param>
/// <returns></returns>
public IUnitOfWork Create(string identifier, DataAccess access)
{
Throw.IfNull(identifier, nameof(identifier));
if (TryGetUnitOfWork(identifier, out var unit))
return unit;
return CreateOrReturn(identifier, access);
}
/// <summary>
/// Disposes the given instance.
/// </summary>
/// <param name="unitOfWork">The unit of work that should get disposed.</param>
public void DisposeUnitOfWork(IUnitOfWork unitOfWork)
{
Throw.IfNull(unitOfWork, nameof(unitOfWork));
try
{
_lock.EnterWriteLock();
if (_units.ContainsValue(unitOfWork))
{
var key = _units.FirstOrDefault(x => x.Value.Equals(unitOfWork)).Key;
if (!string.IsNullOrWhiteSpace(key))
_units.Remove(key);
}
unitOfWork.Dispose();
}
finally
{
_lock.ExitWriteLock();
}
unitOfWork.Dispose();
}
/// <summary>
/// Disposes managed and unmanaged ressources.
/// </summary>
/// <param name="disposing">Flag indicating if the call happened in the public <see cref="Dispose"/> method or the Finalizer.</param>
void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
foreach (var unit in _units)
unit.Value.Dispose();
_factory.Dispose();
_lock.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_disposed = true;
}
}
/// <summary>
/// Disposes managed and unmanaged ressources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}
The unit of work contains either a ISession or a IStatelessSession to create Transactions or the needed repositories.
public sealed class UnitOfWork : IUnitOfWork
{
#region Members
private bool _disposed;
public string Identifier { get; }
public DataAccess Access { get; }
private readonly ISession _session;
private readonly IStatelessSession _statelessSession;
#endregion
#region Constructors
private UnitOfWork(DataAccess access, string identifier)
{
Access = access;
Identifier = identifier;
_disposed = false;
}
internal UnitOfWork(DataAccess access, string identifier, ISession session)
: this(access, identifier)
{
_session = session;
}
internal UnitOfWork(DataAccess access, string identifier, IStatelessSession session)
: this(access, identifier)
{
_statelessSession = session;
}
#endregion
#region Methods
public void CloseTransaction(IGenericTransaction transaction)
{
transaction.Dispose();
}
public IGenericTransaction OpenTransaction()
{
if (_session != null)
return new GenericTransaction(_session.BeginTransaction());
if (_statelessSession != null)
return new GenericTransaction(_statelessSession.BeginTransaction());
throw new InvalidOperationException("You tried to create a transaction without having a vaild session.");
}
public IGenericTransaction OpenTransaction(IsolationLevel level)
{
if (_session != null)
return new GenericTransaction(_session.BeginTransaction(level), level);
if (_statelessSession != null)
return new GenericTransaction(_statelessSession.BeginTransaction(level), level);
throw new InvalidOperationException("You tried to create a transaction without having a vaild session.");
}
public bool Equals(IUnitOfWork other)
{
if (other == null)
return false;
return Access == other.Access;
}
public IRepository<T> GetRepository<T>() where T : Entity<T>
{
switch (Access)
{
case DataAccess.Statefull:
return new StatefullRepository<T>(_session);
case DataAccess.Stateless:
return new StatelessRepository<T>(_statelessSession);
default:
throw new ArgumentException("Cannot determine which repository is needed.", nameof(Access));
}
}
#region IDisposable Support
void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if (_session != null)
_session.Dispose();
if (_statelessSession != null)
_statelessSession.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_disposed = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~UnitOfWork() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
#endregion
}
As one can see the Repositories get created using either the ISession or the IStatelessSession. Both of these Interfaces implement the IDisposable interfaces meaning they should get disposed. Therefore my repositories implement IDisposable as well. Here however is the problem. In theory I can create as many repositories as I want from one unit of work e.g. :
public void UpdatePets(List<Cat> felines, List<Dog> carnines, ISessionManager manager)
{
var uow = manager.Create("Pets", DataAccess.Statefull);
using (var t = uow.OpenTransaction())
{
using (var catRepo = uow.GetRepository<Cat>())
{
catRepo.Add(felines);
t.Commit();
}
}
//Some Businesslogic that forbids using one Transaction to mitigate the problem of an already disposed ISession or IStatelessSession.
using(var t = uow.OpenTransaction())
{
using (var dogRepo = uow.GetRepository<Dog>())
{
dogRepo.Add(carnines);
t.Commit();
}
}
}
If that would be the service consuming my unit of work and I would use the "standard" IDisposable Pattern like this:
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if (_session != null)
_session.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
The second Repository would throw an ObjectDisposedexception because the ISession I reference in the second one is coming from my unit of work and that one was already disposed when my first repository left the using block. So to overcome this Problem I would do the following in my repository:
public void Dispose()
{
_session = null;
GC.SuppressFinalize(this);
}
This however feels wrong because instead of properly disposing the reference to an object that I should dispose I just "close my Eyes and forget about it" which is probably never to good of a solution in programming.
So my question is basically: "Is there a good way to properly dispose objects that might live longer than the object holding the reference?"
You need to establish ownership, and use that to determine which class is responsible for cleaning each "thing" up. It's the owner and only the owner that should call Dispose, no matter how many other classes may have interacted with it.
Ownership needs to be exclusive and (except for wrapper classes) nested1 - the lifetime of the owner needs to be at least as long as the owned object. It's by following these two rules that we can ensure that Dispose is a) called once2 b) when the object is no longer required.
The repositories are the wrong thing to "own" the session. Multiple repositories can be given the same session (so if they're the owners, we've lost the exclusive property), and as your own title alludes to, they have a shorter lifetime than it.
Some general rules of thumb
an object passed as a parameter to an instance method will not normally become owned by the instance.
an object created by an instance and that lives longer than the lifetime of a single method call will be owned by the instance
an object created by an instance method and that doesn't live longer than the lifetime of a single method call will be owned by the method
1For wrappers, the inner object will tend to be created first, possibly configured, then passed to the wrapper object. So the wrapper object's lifetime starts after the inner object.
2Not strictly required. Disposables should tolerate Dispose being called multiple times, but that's more for belt-and-braces defensiveness than a desired pattern.
I'm using Ninject and AOP to do some caching. I have a Attribute that I can apply to any method in my repository and on BeforeInvoke it will return my cached Object if there is one and AfterInvoke create a cached object. This all works great but I can't figure out how to stop initial method from being called, ie if there is a cached object the return that instead of calling a the intyercepted method. My interceptor is here:
public class CacheInterceptor : SimpleInterceptor
{
protected override void BeforeInvoke(IInvocation invocation)
{
Type returnType = invocation.Request.Method.ReturnType;
string cacheKey = CacheKeyBuilder.GetCacheKey(invocation, serializer);
object cachedValue = cache.Get(cacheKey);
if (cachedValue == null)
{
invocation.Proceed();
}
else
{
object returnValue = serializer.Deserialize(returnType, cachedValue);
invocation.ReturnValue = returnValue;
returnedCachedResult = true;
}
}
}
Even though in the else statement I am clearly not saying to call the invoked Method 'invocation.Proceed();' it still invokes it. How do I tell ninject to just return with the invocation.ReturnValue ?
You can't use SimpleInterceptor in this case because that is meant as base class for the most common scenario where you want to do an action before or after the actual method call. Also you are not allowed to call Proceed Instead implement the IInterceptor interface and put your code into the Intercept method.
But probably we should extend SimpleInterceptor in a future version so that you can prevent that the actual method is called:
public abstract class SimpleInterceptor : IInterceptor
{
private bool proceedInvocation = true;
/// <summary>
/// Intercepts the specified invocation.
/// </summary>
/// <param name="invocation">The invocation to intercept.</param>
public void Intercept( IInvocation invocation )
{
BeforeInvoke( invocation );
if (proceedInvocation)
{
invocation.Proceed();
AfterInvoke( invocation );
}
}
/// <summary>
/// When called in BeforeInvoke then the invokation in not proceeded anymore.
/// Or in other words the decorated method and AfterInvoke won't be called anymore.
/// Make sure you have assigned the return value in case it is not void.
/// </summary>
protected void DontProceedInvokation()
{
this.proceedInvocation = false;
}
/// <summary>
/// Takes some action before the invocation proceeds.
/// </summary>
/// <param name="invocation">The invocation that is being intercepted.</param>
protected virtual void BeforeInvoke( IInvocation invocation )
{
}
/// <summary>
/// Takes some action after the invocation proceeds.
/// </summary>
/// <param name="invocation">The invocation that is being intercepted.</param>
protected virtual void AfterInvoke( IInvocation invocation )
{
}
}
I have a generic base class that wraps COM objects and want to constrain it to COM objects.
I tried where TComObject : IUnknown but the compiler does not like it. Is there a way to do this?
I use the code below, please give feedback if you spot something stupid.
public abstract class ComWrapper<TComObject> : IDisposable, IComWrapper<TComObject> where TComObject : class
{
protected ComWrapper(TComObject comObject)
{
ComObject = comObject;
}
protected TComObject ComObject { get; set; }
/// <summary>
/// Use with caution, this accesses the COM object, cleanup might be needed
/// </summary>
/// <returns></returns>
public TComObject GetComObject()
{
return ComObject;
}
public void SetComObject(TComObject comObject)
{
CheckAndReleaseComObject();
ComObject=comObject;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
}
// free native resources if there are any.
CheckAndReleaseComObject();
}
private void CheckAndReleaseComObject()
{
if (ComObject != null)
{
if (ComObject.GetType().IsCOMObject)
Marshal.ReleaseComObject(ComObject);
ComObject = null;
}
}
}
You cannot set such generic constraint, you could, however, consider using something like Marshal.IsComObject to ensure the object is a COM object. If you specifically want a compile time error, I think you're out of luck.
Also, note that SetComObject can be called multiple times, in which case you don't "clean up" any com object that may already have been set. Also, you can now remove GC.SuppressFinalize, as you removed your finalizer/destructor, which is indeed not needed because the framework will release the com object for you if it would get to a garbage collection.
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.