ManagementEventWatcher.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Wmi / managed / System / Management / ManagementEventWatcher.cs / 1305376 / ManagementEventWatcher.cs

                            using System; 
using System.Collections;
using System.Runtime.InteropServices;
using WbemClient_v1;
using System.Diagnostics; 
using System.ComponentModel;
using System.Threading; 
 
namespace System.Management
{ 
    /// 
    /// Represents the method that will handle the  event.
    /// 
    public delegate void EventArrivedEventHandler(object sender, EventArrivedEventArgs e); 

    ///  
    /// Represents the method that will handle the  event. 
    /// 
    public delegate void StoppedEventHandler (object sender, StoppedEventArgs e); 

    //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC//
    /// 
    ///     Subscribes to temporary event notifications 
    ///       based on a specified event query.
    ///  
    ///  
    ///    using System;
    /// using System.Management; 
    ///
    /// // This example demonstrates how to subscribe to an event using the ManagementEventWatcher object.
    /// class Sample_ManagementEventWatcher
    /// { 
    ///     public static int Main(string[] args) {
    /// 
    ///         //For the example, we'll put a class into the repository, and watch 
    ///         //for class deletion events when the class is deleted.
    ///         ManagementClass newClass = new ManagementClass(); 
    ///         newClass["__CLASS"] = "TestDeletionClass";
    ///         newClass.Put();
    ///
    ///         //Set up an event watcher and a handler for the event 
    ///         ManagementEventWatcher watcher = new ManagementEventWatcher(
    ///             new WqlEventQuery("__ClassDeletionEvent")); 
    ///         MyHandler handler = new MyHandler(); 
    ///         watcher.EventArrived += new EventArrivedEventHandler(handler.Arrived);
    /// 
    ///         //Start watching for events
    ///         watcher.Start();
    ///
    ///         // For the purpose of this sample, we delete the class to trigger the event 
    ///         // and wait for two seconds before terminating the consumer
    ///         newClass.Delete(); 
    /// 
    ///         System.Threading.Thread.Sleep(2000);
    /// 
    ///         //Stop watching
    ///         watcher.Stop();
    ///
    ///         return 0; 
    ///     }
    /// 
    ///     public class MyHandler { 
    ///         public void Arrived(object sender, EventArrivedEventArgs e) {
    ///             Console.WriteLine("Class Deleted = " + 
    ///                ((ManagementBaseObject)e.NewEvent["TargetClass"])["__CLASS"]);
    ///         }
    ///     }
    /// } 
    ///    
    ///    Imports System 
    /// Imports System.Management 
    ///
    /// ' This example demonstrates how to subscribe an event using the ManagementEventWatcher object. 
    /// Class Sample_ManagementEventWatcher
    ///     Public Shared Sub Main()
    ///
    ///         ' For the example, we'll put a class into the repository, and watch 
    ///         ' for class deletion events when the class is deleted.
    ///         Dim newClass As New ManagementClass() 
    ///         newClass("__CLASS") = "TestDeletionClass" 
    ///         newClass.Put()
    /// 
    ///         ' Set up an event watcher and a handler for the event
    ///         Dim watcher As _
    ///             New ManagementEventWatcher(New WqlEventQuery("__ClassDeletionEvent"))
    ///         Dim handler As New MyHandler() 
    ///         AddHandler watcher.EventArrived, AddressOf handler.Arrived
    /// 
    ///         ' Start watching for events 
    ///         watcher.Start()
    /// 
    ///         ' For the purpose of this sample, we delete the class to trigger the event
    ///         ' and wait for two seconds before terminating the consumer
    ///         newClass.Delete()
    /// 
    ///         System.Threading.Thread.Sleep(2000)
    /// 
    ///         ' Stop watching 
    ///         watcher.Stop()
    /// 
    ///     End Sub
    ///
    ///     Public Class MyHandler
    ///         Public Sub Arrived(sender As Object, e As EventArrivedEventArgs) 
    ///             Console.WriteLine("Class Deleted = " & _
    ///                 CType(e.NewEvent("TargetClass"), ManagementBaseObject)("__CLASS")) 
    ///         End Sub 
    ///     End Class
    /// End Class 
    ///    
    /// 
    //CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC//
    [ToolboxItem(false)] 
    public class ManagementEventWatcher : Component
    { 
        //fields 
        private ManagementScope         scope;
        private EventQuery              query; 
        private EventWatcherOptions     options;
        private IEnumWbemClassObject    enumWbem;
        private IWbemClassObjectFreeThreaded[]      cachedObjects; //points to objects currently available in cache
        private uint                    cachedCount; //says how many objects are in the cache (when using BlockSize option) 
        private uint                    cacheIndex; //used to walk the cache
        private SinkForEventQuery       sink; // the sink implementation for event queries 
        private WmiDelegateInvoker      delegateInvoker; 

        //Called when IdentifierChanged() event fires 
        private void HandleIdentifierChange(object sender,
            IdentifierChangedEventArgs e)
        {
            // Invalidate any [....] or async call in progress 
            Stop();
        } 
 
        //default constructor
        ///  
        ///    Initializes a new instance of the  class.
        /// 
        /// 
        ///  Initializes a new instance of the  class. For further 
        ///    initialization, set the properties on the object. This is the default constructor.
        ///  
        public ManagementEventWatcher() : this((ManagementScope)null, null, null) {} 

        //parameterized constructors 
        /// 
        /// Initializes a new instance of the  class when given a WMI event query.
        /// 
        /// An  object representing a WMI event query, which determines the events for which the watcher will listen. 
        /// 
        ///    The namespace in which the watcher will be listening for 
        ///       events is the default namespace that is currently set. 
        /// 
        public ManagementEventWatcher ( 
            EventQuery query) : this(null, query, null) {}

        /// 
        /// Initializes a new instance of the  class when given a WMI event query in the 
        ///    form of a string.
        ///  
        ///  A WMI event query, which defines the events for which the watcher will listen. 
        /// 
        ///    The namespace in which the watcher will be listening for 
        ///       events is the default namespace that is currently set.
        /// 
        public ManagementEventWatcher (
            string query) : this(null, new EventQuery(query), null) {} 

        ///  
        ///  Initializes a new instance of the  
        /// class that listens for events conforming to the given WMI event query.
        ///  
        /// A  object representing the scope (namespace) in which the watcher will listen for events.
        /// An  object representing a WMI event query, which determines the events for which the watcher will listen.
        public ManagementEventWatcher(
            ManagementScope scope, 
            EventQuery query) : this(scope, query, null) {}
 
        ///  
        ///  Initializes a new instance of the 
        /// class that listens for events conforming to the given WMI event query. For this 
        /// variant, the query and the scope are specified as strings.
        /// 
        ///  The management scope (namespace) in which the watcher will listen for events.
        ///  The query that defines the events for which the watcher will listen. 
        public ManagementEventWatcher(
            string scope, 
            string query) : this(new ManagementScope(scope), new EventQuery(query), null) {} 

        ///  
        ///  Initializes a new instance of the  class that listens for
        ///    events conforming to the given WMI event query, according to the specified options. For
        ///    this variant, the query and the scope are specified as strings. The options
        ///    object can specify options such as a timeout and context information. 
        /// 
        /// The management scope (namespace) in which the watcher will listen for events. 
        /// The query that defines the events for which the watcher will listen. 
        /// An  object representing additional options used to watch for events. 
        public ManagementEventWatcher( 
            string scope,
            string query,
            EventWatcherOptions options) : this(new ManagementScope(scope), new EventQuery(query), options) {}
 
        /// 
        ///  Initializes a new instance of the  class 
        ///    that listens for events conforming to the given WMI event query, according to the specified 
        ///    options. For this variant, the query and the scope are specified objects. The
        ///    options object can specify options such as timeout and context information. 
        /// 
        /// A  object representing the scope (namespace) in which the watcher will listen for events.
        /// An  object representing a WMI event query, which determines the events for which the watcher will listen.
        /// An  object representing additional options used to watch for events.  
        public ManagementEventWatcher(
            ManagementScope scope, 
            EventQuery query, 
            EventWatcherOptions options)
        { 
            if (null != scope)
                this.scope = ManagementScope._Clone(scope, new IdentifierChangedEventHandler(HandleIdentifierChange));
            else
                this.scope = ManagementScope._Clone(null, new IdentifierChangedEventHandler(HandleIdentifierChange)); 

            if (null != query) 
                this.query = (EventQuery)query.Clone(); 
            else
                this.query = new EventQuery(); 
            this.query.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange);

            if (null != options)
                this.options = (EventWatcherOptions)options.Clone(); 
            else
                this.options = new EventWatcherOptions(); 
            this.options.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); 

            enumWbem = null; 
            cachedCount = 0;
            cacheIndex = 0;
            sink = null;
            delegateInvoker = new WmiDelegateInvoker (this); 
        }
 
        ///  
        ///    Ensures that outstanding calls are cleared. This is the destructor for the object.
        ///  
        ~ManagementEventWatcher ()
        {
            // Ensure any outstanding calls are cleared
            Stop (); 

            if (null != scope) 
                scope.IdentifierChanged -= new IdentifierChangedEventHandler (HandleIdentifierChange); 

            if (null != options) 
                options.IdentifierChanged -= new IdentifierChangedEventHandler (HandleIdentifierChange);

            if (null != query)
                query.IdentifierChanged -= new IdentifierChangedEventHandler (HandleIdentifierChange); 
        }
 
        // 
        // Events
        // 

        /// 
        ///     Occurs when a new event arrives.
        ///  
        public event EventArrivedEventHandler       EventArrived;
 
        ///  
        ///     Occurs when a subscription is canceled.
        ///  
        public event StoppedEventHandler            Stopped;

        //
        //Public Properties 
        //
 
        ///  
        ///    Gets or sets the scope in which to watch for events (namespace or scope).
        ///  
        /// 
        ///     The scope in which to watch for events (namespace or scope).
        /// 
        public ManagementScope Scope 
        {
            get 
            { 
                return scope;
            } 
            set
            {
                if (null != value)
                { 
                    ManagementScope oldScope = scope;
                    scope = (ManagementScope)value.Clone (); 
 
                    // Unregister ourselves from the previous scope object
                    if (null != oldScope) 
                        oldScope.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange);

                    //register for change events in this object
                    scope.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange); 
                    //the scope property has changed so act like we fired the event
                    HandleIdentifierChange(this, null); 
                } 
                else
                    throw new ArgumentNullException("value"); 
            }
        }

        ///  
        ///    Gets or sets the criteria to apply to events.
        ///  
        ///  
        ///     The criteria to apply to the events, which is equal to the event query.
        ///  
        public EventQuery Query
        {
            get
            { 
                return query;
            } 
            set 
            {
                if (null != value) 
                {
                    ManagementQuery oldQuery = query;
                    query = (EventQuery)value.Clone ();
 
                    // Unregister ourselves from the previous query object
                    if (null != oldQuery) 
                        oldQuery.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); 

                    //register for change events in this object 
                    query.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange);
                    //the query property has changed so act like we fired the event
                    HandleIdentifierChange(this, null);
                } 
                else
                    throw new ArgumentNullException("value"); 
            } 
        }
 
        /// 
        ///    Gets or sets the options used to watch for events.
        /// 
        ///  
        ///    The options used to watch for events.
        ///  
        public EventWatcherOptions Options 
        {
            get 
            {
                return options;
            }
            set 
            {
                if (null != value) 
                { 
                    EventWatcherOptions oldOptions = options;
                    options = (EventWatcherOptions)value.Clone (); 

                    // Unregister ourselves from the previous scope object
                    if (null != oldOptions)
                        oldOptions.IdentifierChanged -= new IdentifierChangedEventHandler(HandleIdentifierChange); 

                    cachedObjects = new IWbemClassObjectFreeThreaded[options.BlockSize]; 
                    //register for change events in this object 
                    options.IdentifierChanged += new IdentifierChangedEventHandler(HandleIdentifierChange);
                    //the options property has changed so act like we fired the event 
                    HandleIdentifierChange(this, null);
                }
                else
                    throw new ArgumentNullException("value"); 
            }
        } 
 
        /// 
        ///    Waits for the next event that matches the specified query to arrive, and 
        ///       then returns it.
        /// 
        /// 
        /// A  representing the 
        ///    newly arrived event.
        ///  
        ///  
        ///    If the event watcher object contains options with
        ///       a specified timeout, the API will wait for the next event only for the specified 
        ///       amount of time; otherwise, the API will be blocked until the next event occurs.
        /// 
        public ManagementBaseObject WaitForNextEvent()
        { 
            ManagementBaseObject obj = null;
 
            Initialize (); 

            lock(this) 
            {
                SecurityHandler securityHandler = Scope.GetSecurityHandler();

                int status = (int)ManagementStatus.NoError; 

                try 
                { 
                    if (null == enumWbem)   //don't have an enumerator yet - get it
                    { 
                        //Execute the query
                        status = scope.GetSecuredIWbemServicesHandler( Scope.GetIWbemServices() ).ExecNotificationQuery_(
                            query.QueryLanguage,
                            query.QueryString, 
                            options.Flags,
                            options.GetContext (), 
                            ref enumWbem); 
                    }
 
                    if (status >= 0)
                    {
                        if ((cachedCount - cacheIndex) == 0) //cache is empty - need to get more objects
                        { 
#if true
                            //Because Interop doesn't support custom marshalling for arrays, we have to use 
                            //the "DoNotMarshal" objects in the interop and then convert to the "FreeThreaded" 
                            //counterparts afterwards.
                            IWbemClassObject_DoNotMarshal[] tempArray = new IWbemClassObject_DoNotMarshal[options.BlockSize]; 

                            int timeout = (ManagementOptions.InfiniteTimeout == options.Timeout)
                                ? (int) tag_WBEM_TIMEOUT_TYPE.WBEM_INFINITE :
                                (int) options.Timeout.TotalMilliseconds; 

                            status = scope.GetSecuredIEnumWbemClassObjectHandler(enumWbem).Next_(timeout, (uint)options.BlockSize, tempArray, ref cachedCount); 
                            cacheIndex = 0; 

                            if (status >= 0) 
                            {
                                //Convert results and put them in cache. Note that we may have timed out
                                //in which case we might not have all the objects. If no object can be returned
                                //we throw a timeout exception... - 

                                if (cachedCount == 0) 
                                    ManagementException.ThrowWithExtendedInfo(ManagementStatus.Timedout); 

                                for (int i = 0; i < cachedCount; i++) 
                                    cachedObjects[i] = new IWbemClassObjectFreeThreaded(Marshal.GetIUnknownForObject(tempArray[i]));
                            }

#else 
                            //This was workaround when using TLBIMP we couldn't pass in arrays...
 
                            IWbemClassObjectFreeThreaded cachedObject = cachedObjects[0]; 
                            int timeout = (ManagementOptions.InfiniteTimeout == options.Timeout)
                                ? (int) tag_WBEM_TIMEOUT_TYPE.WBEM_INFINITE : 
                                (int) options.Timeout.TotalMilliseconds;
                            status = scope.GetSecuredIEnumWbemClassObjectHandler(enumWbem).Next_(timeout, 1, out cachedObjects, out cachedCount);

                            cacheIndex = 0; 

                            if (status >= 0) 
                            { 
                                //Create ManagementObject for result. Note that we may have timed out
                                //in which case we won't have an object 
                                if (null == cachedObject)
                                    ManagementException.ThrowWithExtendedInfo(ManagementStatus.Timedout);

                                cachedObjects[0] = cachedObject; 
                            }
#endif 
                        } 

                        if (status >= 0) 
                        {
                            obj = new ManagementBaseObject(cachedObjects[cacheIndex]);
                            cacheIndex++;
                        } 
                    }
                } 
                finally 
                {
                    securityHandler.Reset(); 
                }

                if (status < 0)
                { 
                    if ((status & 0xfffff000) == 0x80041000)
                        ManagementException.ThrowWithExtendedInfo((ManagementStatus)status); 
                    else 
                        Marshal.ThrowExceptionForHR(status);
                } 
            }

            return obj;
        } 

 
        //******************************************** 
        //Start
        //******************************************** 
        /// 
        ///    Subscribes to events with the given query and delivers
        ///       them, asynchronously, through the  event.
        ///  
        public void Start()
        { 
            Initialize (); 

            // Cancel any current event query 
            Stop ();

            // Submit a new query
            SecurityHandler securityHandler = Scope.GetSecurityHandler(); 
            IWbemServices wbemServices = scope.GetIWbemServices();
 
            try 
            {
                sink = new SinkForEventQuery(this, options.Context, wbemServices); 
                if (sink.Status < 0)
                {
                    Marshal.ThrowExceptionForHR(sink.Status);
                } 

                // For async event queries we should ensure 0 flags as this is 
                // the only legal value 
                int status = scope.GetSecuredIWbemServicesHandler(wbemServices).ExecNotificationQueryAsync_(
                    query.QueryLanguage, 
                    query.QueryString,
                    0,
                    options.GetContext(),
                    sink.Stub); 

 
                if (status < 0) 
                {
                    if (sink != null) 
                    {
                        sink.ReleaseStub();
                        sink = null;
                    } 

                    if ((status & 0xfffff000) == 0x80041000) 
 
                        ManagementException.ThrowWithExtendedInfo((ManagementStatus)status);
                    else 
                        Marshal.ThrowExceptionForHR(status);
                }
            }
            finally 
            {
                securityHandler.Reset(); 
            } 
        }
 
        //********************************************
        //Stop
        //********************************************
        ///  
        ///    Cancels the subscription whether it is synchronous or asynchronous.
        ///  
        public void Stop() 
        {
            //For semi-synchronous, release the WMI enumerator to cancel the subscription 
            if (null != enumWbem)
            {
                Marshal.ReleaseComObject(enumWbem);
                enumWbem = null; 
                FireStopped (new StoppedEventArgs (options.Context, (int)ManagementStatus.OperationCanceled));
            } 
 
            // In async mode cancel the call to the sink - this will
            // unwind the operation and cause a Stopped message 
            if (null != sink)
            {
                sink.Cancel ();
                sink = null; 
            }
        } 
 
        private void Initialize ()
        { 
            //If the query is not set yet we can't do it
            if (null == query)
                throw new InvalidOperationException();
 
            if (null == options)
                Options = new EventWatcherOptions (); 
 
            //If we're not connected yet, this is the time to do it...
            lock (this) 
            {
                if (null == scope)
                    Scope = new ManagementScope ();
 
                if (null == cachedObjects)
                    cachedObjects = new IWbemClassObjectFreeThreaded[options.BlockSize]; 
            } 

            lock (scope) 
            {
                scope.Initialize ();
            }
        } 

 
        internal void FireStopped (StoppedEventArgs args) 
        {
            try 
            {
                delegateInvoker.FireEventToDelegates (Stopped, args);
            }
            catch 
            {
            } 
        } 

        internal void FireEventArrived (EventArrivedEventArgs args) 
        {
            try
            {
                delegateInvoker.FireEventToDelegates (EventArrived, args); 
            }
            catch 
            { 
            }
        } 



    } 

    internal class SinkForEventQuery : IWmiEventSource 
    { 
        private ManagementEventWatcher          eventWatcher;
        private object                          context; 
        private IWbemServices                   services;
        private IWbemObjectSink stub;           // The secured IWbemObjectSink
        private int status;
        private bool isLocal; 

        public int Status {get {return status;} set {status=value;}} 
 
        public SinkForEventQuery (ManagementEventWatcher eventWatcher,
            object context, 
            IWbemServices services)
        {
            this.services = services;
            this.context = context; 
            this.eventWatcher = eventWatcher;
            this.status = 0; 
            this.isLocal = false; 

            // determine if the server is local, and if so don't create a real stub using unsecap 
            if((0==String.Compare(eventWatcher.Scope.Path.Server, ".", StringComparison.OrdinalIgnoreCase)) ||
                (0==String.Compare(eventWatcher.Scope.Path.Server, System.Environment.MachineName, StringComparison.OrdinalIgnoreCase)))
            {
                this.isLocal = true; 
            }
 
            if(MTAHelper.IsNoContextMTA())  // Bug#110141 - Checking for MTA is not enough.  We need to make sure we are not in a COM+ Context 
                HackToCreateStubInMTA(this);
            else 
            {
                //
                // [marioh, RAID: 111108]
                // Ensure we are able to trap exceptions from worker thread. 
                //
                ThreadDispatch disp = new ThreadDispatch ( new ThreadDispatch.ThreadWorkerMethodWithParam ( HackToCreateStubInMTA ) ) ; 
                disp.Parameter = this ; 
                disp.Start ( ) ;
                //            Thread thread = new Thread(new ThreadStart(HackToCreateStubInMTA)); 
                //            thread.ApartmentState = ApartmentState.MTA;
                //            thread.Start();
                //            thread.Join();
            } 

        } 
 
        void HackToCreateStubInMTA(object param)
        { 
            SinkForEventQuery obj = (SinkForEventQuery) param ;
            object dmuxStub = null;
            obj.Status = WmiNetUtilsHelper.GetDemultiplexedStub_f (obj, obj.isLocal, out dmuxStub);
            obj.stub = (IWbemObjectSink) dmuxStub; 
        }
 
        internal IWbemObjectSink Stub 
        {
            get { return stub; } 
        }

        public void Indicate(IntPtr pWbemClassObject)
        { 
            Marshal.AddRef(pWbemClassObject);
            IWbemClassObjectFreeThreaded obj = new IWbemClassObjectFreeThreaded(pWbemClassObject); 
            try 

            { 
                EventArrivedEventArgs args = new EventArrivedEventArgs(context, new ManagementBaseObject(obj));

                eventWatcher.FireEventArrived(args);
            } 
            catch
            { 
            } 
        }
 
        public void SetStatus (
            int flags,
            int hResult,
            String message, 
            IntPtr pErrObj)
        { 
#if TODO_ERROBJ_NEVER_USED 
            IWbemClassObjectFreeThreaded errObj = null;
            if(pErrObj != IntPtr.Zero) 
            {
                Marshal.AddRef(pErrObj);
                errObj = new IWbemClassObjectFreeThreaded(pErrObj);
            } 
#endif //
 
            try 
            {
 

                // Fire Stopped event
                eventWatcher.FireStopped(new StoppedEventArgs(context, hResult));
 
                //This handles cases in which WMI calls SetStatus to indicate a problem, for example
                //a queue overflow due to slow client processing. 
                //Currently we just cancel the subscription in this case. 
                //
 

                if (    hResult != (int)tag_WBEMSTATUS.WBEM_E_CALL_CANCELLED
                    && hResult != (int)tag_WBEMSTATUS.WBEM_S_OPERATION_CANCELLED)
                    ThreadPool.QueueUserWorkItem(new WaitCallback(Cancel2));// Cancel(); // 
            }
            catch 
            { 
            }
        } 

        //

        void Cancel2(object o) 
        {
            // 
            // Try catch the call to cancel. In this case the cancel is being done without the client 
            // knowing about it so catching all exceptions is not a bad thing to do. If a client calls
            // Stop (which calls Cancel), they will still recieve any exceptions that may have occured. 
            //
            try
            {
                Cancel(); 
            }
            catch 
            { 
            }
        } 

        internal void Cancel ()
        {
            if (null != stub) 
            {
                lock(this) 
                { 
                    if (null != stub)
                    { 

                        int status = services.CancelAsyncCall_(stub);

                        // Release prior to throwing an exception. 
                        ReleaseStub();
 
                        if (status < 0) 
                        {
                            if ((status & 0xfffff000) == 0x80041000) 
                                ManagementException.ThrowWithExtendedInfo((ManagementStatus)status);
                            else
                                Marshal.ThrowExceptionForHR(status);
                        } 
                    }
                } 
            } 
        }
 
        internal void ReleaseStub ()
        {
            if (null != stub)
            { 
                lock(this)
                { 
                    /* 
                     * We force a release of the stub here so as to allow
                     * unsecapp.exe to die as soon as possible. 
                     * however if it is local, unsecap won't be started
                     */
                    if (null != stub)
                    { 
                        try
                        { 
                            System.Runtime.InteropServices.Marshal.ReleaseComObject(stub); 
                            stub = null;
                        } 
                        catch
                        {
                        }
                    } 
                }
            } 
        } 
    }
 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK