|
using System;
using System.Collections;
using System.Runtime.InteropServices;
using WbemClient_v1;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
namespace System.Management
{
/// <summary>
/// <para>Represents the method that will handle the <see cref='E:System.Management.ManagementEventWatcher.EventArrived'/> event.</para>
/// </summary>
public delegate void EventArrivedEventHandler(object sender, EventArrivedEventArgs e);
/// <summary>
/// <para>Represents the method that will handle the <see cref='E:System.Management.ManagementEventWatcher.Stopped'/> event.</para>
/// </summary>
public delegate void StoppedEventHandler (object sender, StoppedEventArgs e);
//CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC//
/// <summary>
/// <para> Subscribes to temporary event notifications
/// based on a specified event query.</para>
/// </summary>
/// <example>
/// <code lang='C#'>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"]);
/// }
/// }
/// }
/// </code>
/// <code lang='VB'>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
/// </code>
/// </example>
//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 sync or async call in progress
Stop();
}
//default constructor
/// <overload>
/// Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/> class.
/// </overload>
/// <summary>
/// <para> Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/> class. For further
/// initialization, set the properties on the object. This is the default constructor.</para>
/// </summary>
public ManagementEventWatcher() : this((ManagementScope)null, null, null) {}
//parameterized constructors
/// <summary>
/// <para>Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/> class when given a WMI event query.</para>
/// </summary>
/// <param name='query'>An <see cref='System.Management.EventQuery'/> object representing a WMI event query, which determines the events for which the watcher will listen.</param>
/// <remarks>
/// <para>The namespace in which the watcher will be listening for
/// events is the default namespace that is currently set.</para>
/// </remarks>
public ManagementEventWatcher (
EventQuery query) : this(null, query, null) {}
/// <summary>
/// <para>Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/> class when given a WMI event query in the
/// form of a string.</para>
/// </summary>
/// <param name='query'> A WMI event query, which defines the events for which the watcher will listen.</param>
/// <remarks>
/// <para>The namespace in which the watcher will be listening for
/// events is the default namespace that is currently set.</para>
/// </remarks>
public ManagementEventWatcher (
string query) : this(null, new EventQuery(query), null) {}
/// <summary>
/// <para> Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/>
/// class that listens for events conforming to the given WMI event query.</para>
/// </summary>
/// <param name='scope'>A <see cref='System.Management.ManagementScope'/> object representing the scope (namespace) in which the watcher will listen for events.</param>
/// <param name=' query'>An <see cref='System.Management.EventQuery'/> object representing a WMI event query, which determines the events for which the watcher will listen.</param>
public ManagementEventWatcher(
ManagementScope scope,
EventQuery query) : this(scope, query, null) {}
/// <summary>
/// <para> Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/>
/// class that listens for events conforming to the given WMI event query. For this
/// variant, the query and the scope are specified as strings.</para>
/// </summary>
/// <param name='scope'> The management scope (namespace) in which the watcher will listen for events.</param>
/// <param name=' query'> The query that defines the events for which the watcher will listen.</param>
public ManagementEventWatcher(
string scope,
string query) : this(new ManagementScope(scope), new EventQuery(query), null) {}
/// <summary>
/// <para> Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/> 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.</para>
/// </summary>
/// <param name='scope'>The management scope (namespace) in which the watcher will listen for events.</param>
/// <param name=' query'>The query that defines the events for which the watcher will listen.</param>
/// <param name='options'>An <see cref='System.Management.EventWatcherOptions'/> object representing additional options used to watch for events. </param>
public ManagementEventWatcher(
string scope,
string query,
EventWatcherOptions options) : this(new ManagementScope(scope), new EventQuery(query), options) {}
/// <summary>
/// <para> Initializes a new instance of the <see cref='System.Management.ManagementEventWatcher'/> 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.</para>
/// </summary>
/// <param name='scope'>A <see cref='System.Management.ManagementScope'/> object representing the scope (namespace) in which the watcher will listen for events.</param>
/// <param name=' query'>An <see cref='System.Management.EventQuery'/> object representing a WMI event query, which determines the events for which the watcher will listen.</param>
/// <param name='options'>An <see cref='System.Management.EventWatcherOptions'/> object representing additional options used to watch for events. </param>
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);
}
/// <summary>
/// <para>Ensures that outstanding calls are cleared. This is the destructor for the object.</para>
/// </summary>
~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
//
/// <summary>
/// <para> Occurs when a new event arrives.</para>
/// </summary>
public event EventArrivedEventHandler EventArrived;
/// <summary>
/// <para> Occurs when a subscription is canceled.</para>
/// </summary>
public event StoppedEventHandler Stopped;
//
//Public Properties
//
/// <summary>
/// <para>Gets or sets the scope in which to watch for events (namespace or scope).</para>
/// </summary>
/// <value>
/// <para> The scope in which to watch for events (namespace or scope).</para>
/// </value>
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");
}
}
/// <summary>
/// <para>Gets or sets the criteria to apply to events.</para>
/// </summary>
/// <value>
/// <para> The criteria to apply to the events, which is equal to the event query.</para>
/// </value>
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");
}
}
/// <summary>
/// <para>Gets or sets the options used to watch for events.</para>
/// </summary>
/// <value>
/// <para>The options used to watch for events.</para>
/// </value>
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");
}
}
/// <summary>
/// <para>Waits for the next event that matches the specified query to arrive, and
/// then returns it.</para>
/// </summary>
/// <returns>
/// <para>A <see cref='System.Management.ManagementBaseObject'/> representing the
/// newly arrived event.</para>
/// </returns>
/// <remarks>
/// <para>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.</para>
/// </remarks>
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, WmiNetUtilsHelper.GetErrorInfo_f());
}
}
return obj;
}
//********************************************
//Start
//********************************************
/// <summary>
/// <para>Subscribes to events with the given query and delivers
/// them, asynchronously, through the <see cref='System.Management.ManagementEventWatcher.EventArrived'/> event.</para>
/// </summary>
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, WmiNetUtilsHelper.GetErrorInfo_f());
}
// 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, WmiNetUtilsHelper.GetErrorInfo_f());
}
}
finally
{
securityHandler.Reset();
}
}
//********************************************
//Stop
//********************************************
/// <summary>
/// <para>Cancels the subscription whether it is synchronous or asynchronous.</para>
/// </summary>
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, WmiNetUtilsHelper.GetErrorInfo_f());
}
}
}
}
}
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
{
}
}
}
}
}
}
}
|