I've been trying to expose and fire an event to a VBA client. So far on the VBA client side, the event is exposed and I see the method event handling method added to my module class however the VBA event handling method does not fire. For some reason, when debugging the event is null. Modifying my code with synchronously did not help either.
For the record, I've checked other SO questions but they didn't help.
Any good answers will be appreciated.
[ComVisible(true)]
[Guid("56C41646-10CB-4188-979D-23F70E0FFDF5")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IWebEvents))]
[ProgId("MyAssembly.MyClass")]
public class MyClass : ServicedComponent, IMyClass
{
public string _address { get; private set; }
public string _filename { get; private set; }
[DispId(4)]
public void DownloadFileAsync(string address, string filename)
{
_address = address;
_filename = filename;
System.Net.WebClient wc = new System.Net.WebClient();
Task.Factory.StartNew(() => wc.DownloadFile(_address, _filename))
.ContinueWith((t) =>
{
if (null != this.OnDownloadCompleted)
OnDownloadCompleted();
});
}
public event OnDownloadCompletedEventHandler OnDownloadCompleted;
}
[ComVisible(false)]
public delegate void OnDownloadCompletedEventHandler();
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IWebEvents
{
[DispId(1)]
void OnDownloadCompleted();
}
This is a good gig for you all bounty hunters, 200 rep points
The key concept in .net code is to define event(s) as method(s) on a separate interface and connect it to the class via
[ComSourceInterfacesAttribute]
. In the example this is done with this code[ComSourceInterfaces(typeof(IEvents))]
whereIEvents
interface defines the event(s) which should be handled on COM client.Note to event naming: Event names defined in c# class and interface method names defined on interface must be the same. In this example
IEvents::OnDownloadCompleted
corresponds withDemoEvents::OnDownloadCompleted
.Then a second interface is defined which represents the public API of the class itself, here it is called
IDemoEvents
. On this interface methods are defined which are called on COM client.Note to file download: To download the file the method
WebClient.DownloadFileTaskAsync
is used. It downloads the specified resource to a local file as an asynchronous operation using a task object. Task object typically executes asynchronously on a thread pool thread rather than synchronously on the main application thread. Therefore it is necessary to callContinueWith
on main thread because otherwise it won't be possible to execute theOnDownloadCompleted
event. Thats whyContinueWith(continuationAction, TaskScheduler.FromCurrentSynchronizationContext)
is used.Add reference to
*tlb
which was generated byregasm
. Here the name of thistlb
file isCOMVisibleEvents
.Here Excel User Form was used as VBA client. After the button was clicked, the method
DownloadFileAsync
was executed and when this method completes the event was caught in handlerm_eventSource_OnDownloadCompleted
. In this example you can download the source codes of the c# project COMVisibleEvents.dll from my dropbox.