System.Management.ManagementEventWatcher - Recovering From Disconnection - c#

I am attempting to build an application that can monitor multiple remote machines through WMI. As a C# developer, I have chosen to utilize the System.Management namespace.
For performance and scalability reasons, I would much prefer to use an event-driven method of gathering information than a poll-based one. As such, I have been investigating the ManagementEventWatcher class.
For simple monitoring tasks, this class seems to be exactly what I want. I create the object, give it ManagementScope, EventQuery, and EventWatcherOptions parameters, subscribe to the EventArrived event, and call the Start method (simplified example below).
using SM = System.Management;
...
SM.ManagementEventWatcher _watcher;
SM.ConnectionOptions conxOptions;
SM.ManagementScope scope;
SM.WqlEventQuery eventQuery;
SM.EventWatcherOptions eventOptions;
SM.EventArrivedEventHandler handler;
string path = #"\\machine\root\cimv2";
conxOptions = new SM.ConnectionOptions ();
conxOptions.Username = user;
conxOptions.Password = password;
scope = new SM.ManagementScope (path, conxOptions);
scope.Connect ();
eventQuery = new SM.WqlEventQuery ("SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process'");
eventOptions = new SM.EventWatcherOptions ();
eventOptions.Context.Add ("QueryName", "Process Query");
_watcher = new SM.ManagementEventWatcher (scope, eventQuery, eventOptions);
handler = new SM.EventArrivedEventHandler (HandleWMIEvent);
_watcher.EventArrived += handler;
_watcher.Start ();
Console.WriteLine ("Press Any Key To Continue");
Console.ReadKey ();
_watcher.Stop ();
_watcher.EventArrived -= handler;
The problem I am running into is that it is difficult to detect when the connection to the remote machine has been broken through various means (machine restart, downed router, unplugged network cable, etc.).
The ManagementEventWatcher class does not appear to provide any means of determining that the connection has been lost, as the Stopped event will not fire when this occurs. The ManagementScope object attached to the ManagementEventWatcher still shows IsConnected as true, despite the broken link.
Does anyone have any ideas on how to check the connection status?
The only thing I can think to do at this point is to use the ManagementScope object to periodically perform a WMI query against the machine and make sure that still works, though that can only check the local->remote connection and not the corresponding remote->local connection. I suppose I could look up another WMI query I could use to verify the connection (assuming the query works), but that seems like more work than I should have to do.

There are two kinds of event consumers in WMI - temporary and permanent. What you might be looking for is a permanent event subscription. Here is a brief blurb about that on MSDN
A permanent consumer is a COM object that can receive a WMI event at all times. A permanent event consumer uses a set of persistent objects and filters to capture a WMI event. Like a temporary event consumer, you set up a series of WMI objects and filters that capture a WMI event. When an event occurs that matches a filter, WMI loads the permanent event consumer and notifies it about the event. Because a permanent consumer is implemented in the WMI repository and is an executable file that is registered in WMI, the permanent event consumer operates and receives events after it is created and even after a reboot of the operating system as long as WMI is running. For more information, see Receiving Events at All Times.
This MSDN article should be enough to get you going http://msdn.microsoft.com/en-us/library/aa393014(VS.85).aspx.
However, in my situation in dealing with this problem, we chose to poll for the data as opposed to creating a permanent consumer. Another option is to monitor for certain events (such as a reboot) and then re-register your temporary event consumer.

Check out this post here. It covers how to detect when a removable disk is inserted using C#. SHould be inline with your WMI code that you supplied.

Subscribe to the NetworkAvailabilityChange event, this should let you know about the status of your current connection through the NetworkAvailabilityEventArgs.IsAvailable property. With a little extra work the NetworkAddressChange event will let you know about machines that move about, change addresses and etc on your network . . . The System.Net.NetworkInformation has good information . . . I' assuming you don't mind using something other than WMI to monitor this.

As far as I can tell, when something like that happens you should receive an exception of type ManagementException that contains wbemErrCallCancelled WMI error code (0x80041032).

Related

EventStore ClientAPI Subscription issue

I am facing a subscription issue with EventStore.ClientAPI. I have a projection manager that configured to subscribe all and the service started and i am getting subscriptions for events such as $metadata, $UserCreated, $statsCollected etc. but no event from the stream i created. I am pretty new here, please guide me where i am going wrong.
void InitiateProjection(IProjection projection)
{
var checkpoint = GetCurrentPosition(projection.GetType());
_eventStoreConnection.SubscribeToAllFrom(
checkpoint,
CatchUpSubscriptionSettings.Default,
EventAppeared(projection),
LiveProcessingStarted(projection));
}
Event store - product created event
What about if you specifically connect to ProductCreatedDomainEvent+d78435fc43fd-a7bf-56c01d7efa25?
As an observation, the stream name might start to give you problems (unless you changed the system proejctions to search for + instead or - already?)
If you have changed to +, could you try conencting to the $ce-ProductCreatedDomainEvent and see what you get.

How do I trigger a subscription error to test recovery on exchange web service

My application uses the EWS API with a Streaming Subscription and everything is working fine and thats a problem for me as i havn't been able to exercise my recovery code for the OnSubscriptionError event.
Here is the code i use to subscribe for streaming notifications
private void SetStreamingNotifications(List<FolderId> folder_ids)
{
streaming_subscriptions_connection = new StreamingSubscriptionConnection(exchange_service, 30);
streaming_subscriptions_connection.OnDisconnect += OnDisconnect;
streaming_subscriptions_connection.OnSubscriptionError += OnSubscriptionError;
streaming_subscriptions_connection.OnNotificationEvent += OnNotificationEvent;
foreach (var folder_id in folder_ids)
{
StreamingSubscription sub = exchange_service.SubscribeToStreamingNotifications(
new[] { folder_id },
EventType.Created,
EventType.Modified,
EventType.Deleted,
EventType.Moved,
EventType.Copied
);
streaming_subscriptions_connection.AddSubscription(sub);
}
streaming_subscriptions_connection.Open();
}
private void OnSubscriptionError(object sender, SubscriptionErrorEventArgs args)
{
/* What exceptions can i expect to find in "args.Exception" */
/* Can the streaming subscription be recovered or do i need to create a new one? */
}
So my question is how can i trigger a subscription error so i can ensure my code can recover where possible and log / alert when not possible?
EDIT
Following a comment from #kat0r i feel i should add:
I'm currently testing against Exchange 2013 and also intend to test against Exchange 2010 SP1.
I logged a call with Microsoft to find out if it was possible. The short answer is no you can't trigger the OnSubscriptionError event.
Here are the email responses from MS:
In answer to your question, I don’t believe that there is a way you can trigger the OnSubscriptionError event. The correct action to take if you do encounter this event is to attempt to recreate the subscription that encountered the error. I will see if I can find out any further information about this, but the event is generated rarely and only when an unexpected error is encountered on the Exchange server (which is why it probably isn’t possible to trigger it).
It occurred to me that the EWS Managed API has been open-sourced, and is now available on Github: https://github.com/officedev/ews-managed-api
Based on this, we can see exactly what causes the OnSubscriptionError event to be raised – and as far as I can see, this only occurs in the IssueSubscriptionFailures and IssueGeneralFailure methods, both of which can be found in StreamingSubscriptionConnection.cs. Any error that is not ServiceError.ErrorMissedNotificationEvents and is tied to a subscription will result in this event being raised (and the subscription being removed). The error is read from the Response Xml. Of course, this doesn’t really answer your question of how to trigger the event, as that involves causing Exchange to generate such an error (and I’m afraid there is no information on this). It may be possible to inject some Xml (indicating an error) into a response in a test environment – in theory, you may be able to use Fiddler to do this (as it allows you to manipulate requests/responses).
A few things you could do is
Throttling will restrict the maximum number of subscriptions you can create so if you just keep creating new subscription you should get a throttling response from the server once you exceed 20.
The other thing is if you take the SubscriptionId and use a different process to unsubscribe you other code should get a Subscription not found.
You also want to test underlying network issue eg break the connection , dns if you have dev environment see what happens when you bounce the Exchange Server etc.
Cheers
Glen

Is it good practice to put try-catch in a loop until all statements in the try block is executed without any exceptions?

I was trying to develop a multicast receiver program and socket initialization was done as shown below:
public void initializeThread()
{
statuscheckthread = new Thread(SetSocketOptions);
statuscheckthread.IsBackground = true;
}
private void Form1_Load(object sender, EventArgs e)
{
rxsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
iep = new IPEndPoint(IPAddress.Any, 9191);
rxsock.Bind(iep);
ep = (EndPoint)iep;
initializeThread();
statuscheckthread.Start();
}
public void SetSocketOptions()
{
initializeThread(); //re-initializes thread thus making it not alive
while (true)
{
if (NetworkInterface.GetIsNetworkAvailable())
{
bool sockOptnSet = false;
while (!sockOptnSet)
{
try
{
rxsock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("224.50.50.50")));
rxsock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 64);
sockOptnSet = true;
}
catch
{
//Catch exception here
}
}
}
break; // Break out from loop once socket options are set
}
}
When my PC is not connected to a network, SetSocketOption method was throwing exception and even after network is connected,
I was unable to receive data because socket options are not set.
To avoid this I used a thread which runs in the background checking
for network availability and once network is available, it sets the socket options.
It works properly in some PC's but in some others, NetworkInterface.GetIsNetworkAvailable()
returned true before network got connected
(while network was being identified) .
So, to make sure Socket options are set, I used a bool variable sockOptnSet
which is set as
true if all the statements in the try block is executed as shown inside the method public void SetSocketOptions()
This program works fine in all PC's I tried, but I am doubtful about how much I can rely on this to work.
My questions are:
1) Is this good practice?
2) If not, what are the possible errors or problems it may cause? And how can I implement it in a better way?
Is this a good practice?
No, not a good practice. The vast majority of exceptions, including your first one, fall in the category of vexing exceptions. Software is supposed to work, worked well when you tested it, but doesn't on the user's machine. Something went wrong but you do not know what and there isn't anything meaningful that you can do about it. Trying to keep your program going is not useful, it cannot do the job it is supposed to do. In your case, there's no hope that the socket is ever going to receive data when there is no network. And, as you found out, trying to work around the problem just begets more problems. That's normal.
If this is bad practice, how can I implement it in a better way?
You need help from a human. The user is going to have to setup the machine to provide a working network connection. This requires a user interface, you must have a way to tell a human what he needs to do to solve your problem. You can make that as intricate or as simple as you desire. Just an error message, a verbatim copy of the Exception.Message can be enough. Writing an event handler for the AppDomain.CurrentDomain.UnhandledException event is a very good (and required) strategy. Microsoft spent an enormous amount of effort to make exception messages as clear and helpful as possible, even localizing them for you in the user's native language, you want to take advantage of that. Even if the exception message is mystifying, a quick Google query on the message text returns hundreds of hits. With this event handler in place, you don't have to do anything special. Your program automatically terminates and your user knows what to do about it.
You can certainly make it more intricate, you discovered that SetSocketOption() is liable to fail right after the network becomes available but works when you wait long enough. So this is actually an error condition that you can work around, just by waiting long enough. Whether you should write the code to handle this is something that you have to decide for yourself. It is something you write when you have enough experience with the way your program behaves, you never write it up front. Usually as a result from feedback from the users of your program.
Some good advice in the comments, lets' expand on it.
Firstly, I would put all this socket code in to its' own class, outside of the form. This makes it its' own entity and semantically easier to understand. This class could have a property Initialised, which is initially set to false. The first thing you do in your form is call an Initialise method on this class which attempts to set socket options and catches the relevant exceptions if the network is not available. If it is available, we set our Initialised property to true.
If not available, we set a single timeout (see System.Threading.Timer) that calls this same function (potentially with a retry count) after 'x' seconds. Once again we'll find ourselves back in this Initialise function, perhaps with a retry count mentioned at the beginning. Once again, if it is available, we're good - if not, set the timer again. Eventually, after 'x' retries if we're not initialised we can throw an exception or set some other failure property to indicate that we can't proceed.
Your Form class can periodically check (or hook in to an event) to determine whether the socket is now ready for communication. In case of failure you can gracefully quit out, or because our class is nice and abstracted, attempt to start the whole process again.

Process Start Event Using WMI - Not All Process Starts Being Detected

I am using the following C# code in a Windows Service (which runs as NT_AUTHORITY\SYSTEM) to create an event handler for receiving process creation events (using WMI and WQL):
string queryString = "SELECT * FROM Win32_ProcessStartTrace";
ManagementEventWatcher watcher = new ManagementEventWatcher(new WqlEventQuery(queryString));
watcher.EventArrived += new EventArrivedEventHandler(ProcessStartEvent);
watcher.Start();
In ProcessStartEvent:
int processId = int.Parse(e.NewEvent.Properties["ProcessId"].Value.ToString());
Process proc = Process.GetProcessById(processId);
Out("Received process: " + proc.ProcessName);
The problem I'm having is that (for some strange reason) not every process start is captured and reported by the program. If I start about 6 processes simultaneously, one may not show up in the output.
I've tried to do some research on capturing process creation events using WMI, but there is limited information available. I've seen that it is also possible to capture process starts using something similar to:
SELECT TargetInstance
FROM __InstanceCreationEvent
WITHIN 2
WHERE TargetInstance ISA 'Win32_Process'
(As seen in this Stack Overflow answer)
Are there any major differences between using __InstanceCreationEvent and Win32_ProcessStartTrace? Could this be the cause of my problems?
Is there an explanation as to why I'm not receiving events for all process starts? Is there something more obvious that I'm doing wrong here?
Both methods are valid but works in differents ways.
When you uses the __InstanceCreationEvent WMI class you are using a intrinsic event which means which you are monitoring changes in the standard WMI data model (this works like a trigger in a table).
When you uses the Win32_ProcessStartTrace you are using a Extrinsic event that means you are using a specialized event class made for a specific task in this case monitor the process creation.
Now back to your issue, the best way to avoid the "lost" of some events is creating a permanent event consumer.
I've found when you get an event that a process has started - pass that event into a seperate thread with for instance boost thread you can pass the process ID to a new thread.
This means the WMI COM doesn't get in a tangle and stop itself working.
see http://sourceforge.net/p/processhistory/code/HEAD/tree/trunk/PHLogger/ and look for a revision with COM_WMI_Consumer/
for some working C++ code.

What is the easiest way using .net to check if events have been logged in the eventlog?

What is the easiest way to check if events have been logged in the eventlog during a period of time?
I want to perform a series of automated test steps and then check if any errors were logged to the Application Event Log, ignoring a few sources that I'm not interested in. I can use System.Diagnostics.EventLog and then look at the Entries collection, but it doesn't seem very useable for this scenario. For instance Entries.Count can get smaller over time if the event log is removing old entries. I'd prefer some way to either query the log or monitor it for changes during a period of time. e.g.
DateTime start = DateTime.Now;
// do some stuff...
foreach(EventLogEntry entry in CleverSolution.EventLogEntriesSince(start, "Application"))
{
// Now I can do stuff with entry, or ignore if its Source is one
// that I don't care about.
// ...
}
Just to be a good Wiki citizen and strive for completion, there are other ways. I didn't suggest it earlier because it is complete overkill for something that is only going to be run in-house as part of a test suite, and you said right in the title you wanted something easy.
But if you need to see events as they occur in shipping code, read on. Believe it or not there are three different Windows APIs for this thing at this point.
NotifyChangeEventLog()
The original API for this sort of thing is called NotifyChangeEventLog() and it was supported starting in Windows 2000. Essentially you use the WIN32 event log APIs to open the event log, then you call this API with the handle you were given by the other API and an event handle. Windows will signal your event when there are new event log entries to look at.
I never used this API myself, because most of my interest was in remote event log access and this API explicitly does not support remote logs. However, the rest of the API set this belongs to does let you sequentially read remote logs if you have the right permissions.
Windows Management Instrumentation
A second way is to use the Windows Management Instrumentation API, and this does support both local and remote logs. This is a COM/DCOM based API that has existed for several years in Windows, and the .NET Framework has a nice implementation of it in the System.Management namespace. Essentially what you do is create an EventQuery that looks for the appearance of new WMI objects of type (meaning within the WMI type system) of Win32_NTLogEvent. The appearance of these will indicate new event log entries, and they will present pretty much in real time. The attributes on these objects contain all the details of the log entry. There's an article from MSDN magazine that talks about playing around with this stuff in Visual Studio.
Again, this would be total overkill for a test application, it would require far more code than your existing solution. But years ago I wrote a subsystem for a network management application that used the DCOM flavor of this API to gather the event logs off of all the servers on a network so we could alert on particular ones. It was pretty slick and darn near real time. If you implement this in C++ with DCOM, be prepared to deal with Multithreaded Apartments and a lot of hairy logic to detect if/when your connection to the remote server goes up or down.
Windows Vista Event Log
Windows Vista (and Server 2008) have a whole new API suite relating to event logging and tracing. The new event log is documented here. It looks like there is an API called EvtSubscribe that allows you to subscribe to events. I have not used this API so I can't comment on its pros and cons.
That having been said, here's an answer that actually should be pretty straightforward even for your test application and is .NET Framework specific.
You need to open the EventLog before you start your test, and subscribe an event handler to the EventLog.EntryWritten event. This is the way that .NET exposes the NotifyChangeEventLog() Win32 API.
Move your current logic from GetEventLogEntriesSince() into the event handler, but instead of adding the events to a list for return, store them in a list you can retrieve from somewhere at the end of the run. You can retrieve the contents of the log entry from the EntryWrittenEventArgs argument which is passed, via its Entry property.
The System.Diagnostics.EventLog class really is the right way to do this.
Your main objection seems to be that the log can remove old entries in some cases. But you say this is in a software testing scenario. Can't you arrange to configure your test systems such that the logs are large enough to contain all entries and the removal of old entries won't occur during your tests?
Well the solution I've come up with does use System.Diagnostics.EventLog and simply iterating over all events to filter for the ones I want. I guess this is straightforward, I just thought there would have been a more efficient interface for this. Any suggestions or improvements very welcome!
I've created a method to return event log entries since a certain time:
/// <summary>
/// Steps through each of the entries in the specified event log and returns any that were written
/// after the given point in time.
/// </summary>
/// <param name="logName">The event log to inspect, eg "Application"</param>
/// <param name="writtenSince">The point in time to return entries from</param>
/// <param name="type">The type of entry to return, or null for all entry types</param>
/// <returns>A list of all entries of interest, which may be empty if there were none in the event log.</returns>
public List<EventLogEntry> GetEventLogEntriesSince(string logName, DateTime writtenSince, EventLogEntryType type)
{
List<EventLogEntry> results = new List<EventLogEntry>();
EventLog eventLog = new System.Diagnostics.EventLog(logName);
foreach (EventLogEntry entry in eventLog.Entries)
{
if (entry.TimeWritten > writtenSince && (type==null || entry.EntryType == type))
results.Add(entry);
}
return results;
}
In my test class I store a timestamp:
private DateTime whenLastEventLogEntryWritten;
and during test setup I set the timestamp to when the last event log entry was written:
EventLog eventLog = new EventLog("Application");
whenLastEventLogEntryWritten = eventLog.Entries.Count > 0 ?
eventLog.Entries[eventLog.Entries.Count - 1] : DateTime.Now;
At the end of my test I check that there were no event log errors:
Assert.IsEmpty(GetEventLogEntriesSince("Application",
whenLastEventLogEntryWritten,
EventLogEntryType.Error),
"Application Event Log errors occurred during test execution.");
I use this Powershell to scan the eventlog for relevant entries within the last 7 days:
$d=Get-Date
$recent=[System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($d.AddDays(-7))
get-wmiobject -computer HOSTNAME -class Win32_NTLogEvent `
-filter "logfile = 'Application' and (sourcename = 'SOURCENAME' or sourcename like 'OTHERSOURCENAME%') and (type = 'error' or type = 'warning') AND (TimeGenerated >='$recent')" |
sort-object #{ expression = {$_.TimeWritten} } -descending |
select SourceName, Message |
format-table #{Expression = { $_.SourceName};Width = 20;Label="SourceName"}, Message
If you use C# (tagged, but not mentioned in the question), the magic lies in the get-wmiobject query.

Categories

Resources