|
version: 1.0.0.0 last updated: 4 January 2003 You would expect regular delegates to work when using SQL-DMO events in C#. The code that does not workSo you would expect the following C# code to work as expected: using System;using System.Runtime.InteropServices; using SQLDMO; namespace BackupWithEvents { /// <summary> /// Class that drives from SQLDMO.BackupSink to implement event handlers /// </summary> class App : SQLDMO.BackupSink { [MTAThread] static void Main(string[] args) { App app = new App(); app.Backup(); } public void Backup() databases = server.Databases; #region Implementation of BackupSinkpublic void Complete(string Message) { Console.WriteLine("Complete {0}", Message); } public void PercentComplete(string Message, int Percent) { Console.WriteLine("PercentComplete {0} {1}", Message, Percent); } public void NextMedia(string Message) { Console.WriteLine("NextMedia {0}", Message); } #endregion } } Download BackupWithEvents.zip The wrong resultsHowever when you look at the results is shows like: As you can see there is something wrong, the CompleteEventHandler fires 3 times, the other ones never fires at all. If you would change the order of linking the delegates you will see that the first event handler that you register is the one and only one that fires. The problemThis behavior is caused by the fact that TLBIMP.EXE, which generates the Runtime Callable Wrappers (RCW) for the SQL-DMO COM library, creates a separate event interface implementation for each delegate rather than having a single interface with multiple functions. It appears that for every event that you want to be advised on, a new “sinkhelper” object, is being created and advised. As a result if one of the events is *dependent* on return value of another event you get unexpected results. The solutionTechnically there are two solutions, the first is to create your own RCW which implements the events handlers correctly using a single interface with multiple functions. This however requires a lot of coding for just handling events. The second solution provides a workaround for the problem using the UCOM interfaces to Advise our own implementation of the sink interface. To properly handle the events, the Visual C# .NET application must implement the IConnectionPointContainer and IConnectionPoint interfaces. The .NET Framework provides managed definitions of these interfaces through the UCOMIConnectionPointContainer and UCOMIConnectionPoint interfaces. These interfaces are part of the System.Runtime.InteropServices namespace. Using the workaroundWhen using the UCOM interfaces the code will look like this: using System;using System.Runtime.InteropServices; using SQLDMO; namespace BackupWithEvents{ /// <summary> /// SQL-DMO events using UCOMIConnectionPoint* /// </summary> class App { [MTAThread] static void Main(string[] args) { App app = new App(); app.Backup(); } public void Backup() server.LoginSecure = true; backup.Database = database.Name; #region Implementation of BackupSink public void PercentComplete(string Message, int Percent) public void NextMedia(string Message) Download BackupWithEventsFix.zip The correct resultsserver (local) Other SQL-DMO event sink interfacesIf you want to apply the same technique for other SQL-DMO objects that emit events you need to know the GUID of the interface for the event sink, this is the list of objects and the interface UUID for the various event sink interfaces in SQL-DMO
In case you are wondering how to find this information yourself, use OLEVIEW to find the sink interfaces in the type library. *** |
Questions or problems regarding this web site should be directed to Web Master. |