I'm building what could be called the DAL for a new app. Unfortunately, network connectivity to the database is a real problem.
I'd like to be able to temporarily block network access within the scope of my test so that I can ensure my DAL behaves as expected under those circumstances.
UPDATE: There are many manual ways to disable the network, but it sure would be nice if I could enable/disable within the test itself.
For the time being, I'm just "disabling" the network by setting a bogus static IP as follows:
using System.Management;
class NetworkController
{
public static void Disable()
{
SetIP("192.168.0.4", "255.255.255.0");
}
public static void Enable()
{
SetDHCP();
}
private static void SetIP(string ip_address, string subnet_mask)
{
ManagementClass objMC = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection objMOC = objMC.GetInstances();
foreach (ManagementObject objMO in objMOC) {
if ((bool)objMO("IPEnabled")) {
try {
ManagementBaseObject setIP = default(ManagementBaseObject);
ManagementBaseObject newIP = objMO.GetMethodParameters("EnableStatic");
newIP("IPAddress") = new string[] { ip_address };
newIP("SubnetMask") = new string[] { subnet_mask };
setIP = objMO.InvokeMethod("EnableStatic", newIP, null);
}
catch (Exception generatedExceptionName) {
throw;
}
}
}
}
private static void SetDHCP()
{
ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject mo in moc) {
// Make sure this is a IP enabled device. Not something like memory card or VM Ware
if ((bool)mo("IPEnabled")) {
ManagementBaseObject newDNS = mo.GetMethodParameters("SetDNSServerSearchOrder");
newDNS("DNSServerSearchOrder") = null;
ManagementBaseObject enableDHCP = mo.InvokeMethod("EnableDHCP", null, null);
ManagementBaseObject setDNS = mo.InvokeMethod("SetDNSServerSearchOrder", newDNS, null);
}
}
}
}
Write a wrapper to the network class connectivity class you're using (e.g. WebClient) with an on-off switch :)
Either that, or block your application in the firewall.
Try blocking the connection with a firewall midway through the session maybe?
I like the wrapper idea as well, but thats kind of abstracting the problem and you prolly might not get exact real world behavior. Also, inserting the wrapper layer and then removing it may be more trouble than its worth.
Edit: Run a script that turns the Network adapter on/off randomly or at set intervals?
If you are trying a complete network outage for your application unplugging the network cable will work. Sometimes you might have a data access layer with multiple data sources (on different machines) in which case you can simulate an exception in your tests with a Mock Framework like Rhino Mocks. Here is some pseudo-code that you may have in your test
void TestUserDBFailure()
{
// ***** THIS IS PSEUDO-CODE *******
//setting up the stage - retrieval of the user info create an exception
Expect.Call(_userRepository.GetUser(null))
.IgnoreArguments()
.Return(new Exception());
// Call that uses the getuser function, see how it reacts
User selectedUser = _dataLoader.GetUserData("testuser", "password");
}
Probably not helpful for simulating "real" network issues, but you could just point your DB connection string to a non-existent machine while within the scope of your test.
Depends on what particular network problem you wish to simulate. For most folks, it's as simple as "server unreachable", in which case you'd just try to connect to a non existent server. Be careful, though, because you want something that is routable but does not answer. Trying to connect to dkjdsjk.com will fail immediately (DNS lookup), but trying to connect to www.google.com:1433 will (probably) time out due to a firewall - which is how your app will behave when your DB server is down.
Try Toxiproxi, it can simulate network outage.
They have REST API and even .NET Client API to change the network simulation programatically (from your test code)
https://github.com/shopify/toxiproxy
Look for a WAN simulator that will allow you to restrict bandwidth (and cut it off completely) I always find it interesting to see how the user experience changes when my apps are run in a bandwidth restricted environment. Look here for some information.
There is a tool you can use for simulating High Latency and Low Bandwidth in Testing of Database Applications as explained in this blog entry.
Just found an alternative that allows to directly close TCP connections:
http://lamahashim.blogspot.ch/2010/03/disabling-network-using-c.html
It is based on Windows IP Helper API (uses DllImport):
http://msdn.microsoft.com/en-us/library/windows/desktop/aa366073(v=vs.85).aspx
Use mock objects to create configurable, destructible versions of the real thing--in this case, the database.
Related
I have multiple applications running on a single machine. One is doing work and writing logs to disk about what it has done (I'll call this WorkerApp) and another that summarizes support information about the status of WorkerApp along with some more details (I'll call this Dashboard).
From the Dashboard i want to instruct the WorkerApp to take an action (say, "Ping remote services") and I would like the WorkerApp to send the Dashboard the "pong" response when it gets it.
I have seen examples of using the SendMessage, but this seems to be pretty archaic (isn't there anything more standard now in 2016 for between process communications?).
I have very little experience with Akka.Net but the Remoting feature of it seem like a good approach, although setting this up seems a little overkill for what I would like to do.
What is the easiest way currently to go about communicating between two processes in .Net? And is there some examples of this, working on a localmachine?
I put together an Akka.Net example for this. This is what it looks like.
DashBoard (sends messages)
using System;
using Akka.Actor;
using Akka.Configuration;
namespace DashBoard
{
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
}
remote {
helios.tcp {
transport-class = ""Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote""
applied-adapters = []
transport-protocol = tcp
port = 0
hostname = localhost
}
}
}
");
using (var system = ActorSystem.Create("Dashboard", config))
{
var server = system.ActorSelection("akka.tcp://WorkerApp#localhost:8081/user/WorkerAppActor");
while (true)
{
var input = Console.ReadLine();
server.Tell(input);
}
}
}
}
}
WorkerApp (receives messages)
using System;
using Akka.Actor;
using Akka.Configuration;
namespace WorkerApp
{
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
}
remote {
helios.tcp {
transport-class = ""Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote""
applied-adapters = []
transport-protocol = tcp
port = 8081
hostname = localhost
}
}
}
");
using (var system = ActorSystem.Create("WorkerApp", config))
{
system.ActorOf<WorkerAppActor>("WorkerAppActor");
Console.ReadLine();
}
}
}
class WorkerAppActor : TypedActor, IHandle<string>
{
public void Handle(string message)
{
Console.WriteLine($"{DateTime.Now}: {message}");
}
}
}
Have a look at .Net remoting. It's a bit more modern than SendMessage, but not a whole lot. It's pretty easy to use though. I think the official way to do this these days is probably using WCF, but I'm pretty sure it's just the same under the hood.
.Net remoting supports various channels (Http, TCP), but in your case I'd suggest IPC remoting. It sits on top of named pipes.
Probably easiest if you google (.Net remoting), but the general idea is to define a class in your "server" app derived from MarshalByRefObject. Once you've done that, register it with the remoting infrastructure using RemotingConfiguration.RegisterWellKnownServiceType.
Your client app can then create an instance of the class using Activator.CreateObject and then you're good to go.
One thing to be aware of: it looks like you'll want a callback mechanism - so your Dashboard isn't blocked waiting for your WorkerApp. This is supported in .Net remoting, but you'll need to create two channels - one for the outgoing calls (from Dashboard to WorkerApp) and then another for the incoming callbacks.
Another suggestion: your worker class (the one derived from MarshalByRefObject) will be easier to deal with if exposes an interface as well. Put that interface in a DLL available to both apps and life will be easier.
One suggestion may be to use a database or other persistent storage to create a type of 'Queue' which the dashboard can insert tasks into. This could also store statuses about the tasks which can be updated by the worker process. Although this could be considered 'overkill' it brings a vartiety of benefits such as auditing, historical reporting and server crash/power-off redundancy. It would also probably make scaling up the application in the future easier.
I've seen several similar questions on Google, but nothing exactly matches what I'm trying to do. I'm making a lag-reducing program (for a game) that basically lowers the user's MTU when a certain process is open, and restores it when the process is closed. However, MTU is a network-adapter specific setting, and some users have multiple connected network adapters. To this end, I thought it'd be nice to have the program also detect which adapter is being used by the game, and only change the MTU on that adapter.
The game will only use one adapter at a time.
I can't hardcode in end-server-IP addresses because they change fairly frequently. It seems to be there must be a way to determine which adapter the other process is using without knowing the end IP address, but I can't seem to find it.
EDIT:
Thanks to Cicada and Remco, I've solved the problem.
I used the ManagedIPHelper class that Remco linked to (ManagedIpHelper) and Cicada's comments led me to this article (Identifying active network interface)
Combining those with some (Nasty, horribly unoptimized) LINQ, I got this code snippet, which takes the process name and returns the Network Interface it's using, or null if it can't find one.
private NetworkInterface getAdapterUsedByProcess(string pName)
{
Process[] candidates = Process.GetProcessesByName(pName);
if (candidates.Length == 0)
throw new Exception("Cannot find any running processes with the name " + pName + ".exe");
IPAddress localAddr = null;
using (Process p = candidates[0])
{
TcpTable table = ManagedIpHelper.GetExtendedTcpTable(true);
foreach (TcpRow r in table)
if (r.ProcessId == p.Id)
{
localAddr = r.LocalEndPoint.Address;
break;
}
}
if (localAddr == null)
throw new Exception("No routing information for " + pName + ".exe found.");
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties ipProps = nic.GetIPProperties();
if (ipProps.UnicastAddresses.Any(new Func<UnicastIPAddressInformation, bool>((u) => { return u.Address.ToString() == localAddr.ToString(); })))
return nic;
}
return null;
}
Testing confirms this works perfectly! Many thanks, guys!
Side notes to anyone using this snippet:
You'll need the ManagedIpHelper classes.
Your app may need to request elevation, depending on the situation.
Multiple running processes (think Chrome) will return an undefined result. If you're going to use this code with a multpile-process-candiate situation, I highly recommend you change using (Process p = candidates[0]) to a more specific filter, ie based on PID.
You may also want to impliment new exception types, so you can, for example, catch "No routing info" more cleanly, the reason being that this error is often fixed by simply waiting a bit (to let the target process open a connection) and then retrying.
in addition to Cicada, this must help you:
It is a C# wrapper around some c/c++ code, which gets you the list of all open connections with associated PID ( Process Id ).
http://www.timvw.be/2007/09/09/build-your-own-netstatexe-with-c/
I do believe this is the only way to go, determine the process(id) based on executable path/name and try to find the current connection of that process.
I currently have a single application that needs to be started from a windows service that i am coding in .net 3.5. This application is currently running as the user who ran the service, in my case the SYSTEM user. If running as the SYSTEM user it does not show the application to the users desktop. Thoughts? advice?
//constructor
private Process ETCHNotify = new Process();
//StartService()
ETCHNotify.StartInfo.FileName = baseDir + "\\EtchNotify.exe";
ETCHNotify.StartInfo.UseShellExecute = false;
//BackgroundWorkerThread_DoWork()
if (!systemData.GetUserName().Equals(""))
{
// start ETCHNotify
try {
ETCHNotify.Start();
}
catch (Exception ex)
{
systemData.Run("ERR: Notify can't start: " + ex.Message);
}
}
I only execute the try/catch if the function i have written GetUserName() (which determines the username of the user running explorer.exe) is not null
again to reiterate: desired functionality is that this starts ETCHNotify in a state that allows it to interact with the currently logged in user as determined by GetUserName()
Collage of some post found around (this and this)
Note that as of Windows Vista, services are strictly forbidden from interacting directly with a user:
Important: Services cannot directly interact with a user as of Windows
Vista. Therefore, the techniques mentioned in the section titled Using
an Interactive Service should not be used in new code.
This "feature" is broken, and conventional wisdom dictates that you shouldn't have been relying on it anyway. Services are not meant to provide a UI or allow any type of direct user interaction. Microsoft has been cautioning that this feature be avoided since the early days of Windows NT because of the possible security risks.
There are some possible workarounds, however, if you absolutely must have this functionality. But I strongly urge you to consider its necessity carefully and explore alternative designs for your service.
Use WTSEnumerateSessions to find the right desktop, then CreateProcessAsUser to start the application on that desktop (you pass it the handle of the desktop as part of the STARTUPINFO structure) is correct.
However, I would strongly recommend against doing this. In some environments, such as Terminal Server hosts with many active users, determining which desktop is the 'active' one isn't easy, and may not even be possible.
A more conventional approach would be to put a shortcut to a small client app for your service in the global startup group. This app will then launch along with every user session, and can be used start other apps (if so desired) without any juggling of user credentials, sessions and/or desktops.
Ultimately in order to solve this i took the advice of #marco and the posts he mentioned. I have created the service to be entirely independent of the tray application that interacts with the user. I did however install the Tray application via registry 'start up' methods with the service. The Service installer will now install the application which interacts with the user as well... This was the safest and most complete method.
thanks for your help everyone.
I wasn't going to answer this since you already answered it, (and it's oh, what? going on 2.5 years OLD now!?) But there are ALWAYS those people who are searching for this same topic, and reading the answers...
In order to get my service to Interact with the Desktop, no matter WHAT desktop, nor, how MANY desktops, nor if the service was even running on the SAME COMPUTER as the desktop app!! None of that matters with what I got here... I won't bore you with the details, I'll just give you the meat and potatoes, and you and let me know if you want to see more...
Ok. First thing I did was create an Advertisement Service. This is a thread that the service runs, opens up a UDP socket to listen for broadcasts on the network. Then, using the same piece of code, I shared it with the client app, but it calls up Advertise.CLIENT, rather than Advertise.SERVER... The CLIENT opens the port I expect the service to be on, and broadcasts a message, "Hello... Is there anybody out there??", asking if they're there ANY servers listening, and if so, reply back to THIS IP address with your computer name, IP Address and port # where I can find the .NET remoting Services..." Then it waits a small amount of time-out time, gathers up the responses it gets, and if it's more than one, it presents the user with a dialog box and a list of services that responded... The Client then selects one, or, if only ONE responded, it will call Connect((TServerResponse) res); on that, to get connected up. At this point, the server is using Remoting Services with the WellKnownClientType, and WellKnownServerType to put itself out there...
I don't think you are too interested in my "Auto-Service locater", because a lot of people frown on UDP, even more so when your app start broadcasting on large networks. So, I'm assuming you'd be more interested in my RemotingHelper, that gets the client connected up to the server. It looks like this:
public static Object GetObject(Type type)
{
try {
if(_wellKnownTypes == null) {
InitTypeCache();
}
WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)_wellKnownTypes[type];
if(entr == null) {
throw new RemotingException("Type not found!");
}
return System.Activator.GetObject(entr.ObjectType, entr.ObjectUrl);
} catch(System.Net.Sockets.SocketException sex) {
DebugHelper.Debug.OutputDebugString("SocketException occured in RemotingHelper::GetObject(). Error: {0}.", sex.Message);
Disconnect();
if(Connect()) {
return GetObject(type);
}
}
return null;
}
private static void InitTypeCache()
{
if(m_AdvertiseServer == null) {
throw new RemotingException("AdvertisementServer cannot be null when connecting to a server.");
}
_wellKnownTypes = new Dictionary<Type, WellKnownClientTypeEntry>();
Dictionary<string, object> channelProperties = new Dictionary<string, object>();
channelProperties["port"] = 0;
channelProperties["name"] = m_AdvertiseServer.ChannelName;
Dictionary<string, object> binFormatterProperties = new Dictionary<string, object>();
binFormatterProperties["typeFilterLevel"] = "Full";
if(Environment.UserInteractive) {
BinaryServerFormatterSinkProvider binFormatterProvider = new BinaryServerFormatterSinkProvider(binFormatterProperties, null);
_serverChannel = new TcpServerChannel(channelProperties, binFormatterProvider);
// LEF: Only if we are coming form OUTSIDE the SERVICE do we want to register the channel, since the SERVICE already has this
// channel registered in this AppDomain.
ChannelServices.RegisterChannel(_serverChannel, false);
}
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatServiceStatus)));
RegisterType(typeof(IPawnStatServiceStatus),m_AdvertiseServer.RunningStatusURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatService)));
RegisterType(typeof(IPawnStatService), m_AdvertiseServer.RunningServerURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IServiceConfiguration)));
RegisterType(typeof(IServiceConfiguration), m_AdvertiseServer.RunningConfigURL.ToString());
}
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration, RemotingConfiguration=true)]
public static void RegisterType(Type type, string serviceUrl)
{
WellKnownClientTypeEntry clientType = new WellKnownClientTypeEntry(type, serviceUrl);
if(clientType != RemotingConfiguration.IsWellKnownClientType(type)) {
RemotingConfiguration.RegisterWellKnownClientType(clientType);
}
_wellKnownTypes[type] = clientType;
}
public static bool Connect()
{
// Init the Advertisement Service, and Locate any listening services out there...
m_AdvertiseServer.InitClient();
if(m_AdvertiseServer.LocateServices(iTimeout)) {
if(!Connected) {
bConnected = true;
}
} else {
bConnected = false;
}
return Connected;
}
public static void Disconnect()
{
if(_wellKnownTypes != null) {
_wellKnownTypes.Clear();
}
_wellKnownTypes = null;
if(_serverChannel != null) {
if(Environment.UserInteractive) {
// LEF: Don't unregister the channel, because we are running from the service, and we don't want to unregister the channel...
ChannelServices.UnregisterChannel(_serverChannel);
// LEF: If we are coming from the SERVICE, we do *NOT* want to unregister the channel, since it is already registered!
_serverChannel = null;
}
}
bConnected = false;
}
}
So, THAT is meat of my remoting code, and allowed me to write a client that didn't have to be aware of where the services was installed, or how many services were running on the network. This allowed me to communicate with it over the network, or on the local machine. And it wasn't a problem to have two or more people running the app, however, yours might. Now, I have some complicated callback code in mine, where I register events to go across the remoting channel, so I have to have code that checks to see if the client is even still connected before I send the notification to the client that something happened. Plus, if you are running for more than one user, you might not want to use Singleton objects. It was fine for me, because the server OWNS the objects, and they are whatever the server SAYS they are. So, my STATS object, for example, is a Singleton. No reason to create an instance of it for EVERY connection, when everyone is going to see the same data, right?
I can provide more chunks of code if necessary. This is, of course, one TINY bit of the overall picture of what makes this work... Not to mention the subscription providers, and all that.
For the sake of completeness, I'm including the code chunk to keep your service connected for the life of the process.
public override object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if(lease.CurrentState == LeaseState.Initial) {
lease.InitialLeaseTime = TimeSpan.FromHours(24);
lease.SponsorshipTimeout = TimeSpan.FromSeconds(30);
lease.RenewOnCallTime = TimeSpan.FromHours(1);
}
return lease;
}
#region ISponsor Members
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
public TimeSpan Renewal(ILease lease)
{
return TimeSpan.FromHours(12);
}
#endregion
If you include the ISponsor interface as part of your server object, you can implement the above code.
Hope SOME of this is useful.
When you register your service, you can tell it to allow interactions with the desktop. You can read this oldie link http://www.codeproject.com/KB/install/cswindowsservicedesktop.aspx
Also, don't forget that you can have multiple users logged in at the same time.
Apparently on Windows Vista and newer interacting with the desktop has been made more difficult. Read this for a potential solution: http://www.codeproject.com/KB/cs/ServiceDesktopInteraction.aspx
I've found multiple online tutorials for establishing WMI connections to remote machines using c#. These tutorials describe a process like the following:
ConnectionOptions cOpts = new ConnectionOptions();
ManagementObjectCollection moCollection;
ManagementObjectSearcher moSearcher;
ManagementScope mScope;
ObjectQuery oQuery;
mScope = new ManagementScope(String.Format("\\\\{0}\\{1}", host.hostname, "ROOT\\CIMV2"), cOpts);
oQuery = new ObjectQuery("Select * from Win32_OperatingSystem");
moSearcher = new ManagementObjectSearcher(mScope, oQuery);
moCollection = moSearcher.Get();
The happy path cases - connecting to a local host, or connecting to a remote host with proper credentials - work fine. I'm working on a project where we need to support the case when the currently logged in account does not have access to the remote host we're attempting to connect to. That is, we need to catch this case, bring the bad credentials to the attention of the user, and prompt them to supply credentials again.
When I specify credentials in my ConnectionOptions object that do not have context on the remote machine, my call to moSearcher.Get() hangs (seemingly) indefinitely. Similarly, a call to the Connect() function in ManagementScope hangs in the same manner.
We have similar logic in place to perform the equivalent WMI commands in c++, and I can report that those return almost immediately if improper credentials are supplied. An appropriate "access is denied" message is returned. The hosts I'm using for test purposes right now are the same ones we use when testing our existing c++ logic, so I have no reason to believe that WMI is incorrectly configured in our environment.
I've searched for timeout issues surrounding WMI connections in c#. I've explored the Timeout property of ConnectionOptions and moSearcher.Options. I've also looked at the ReturnImmediately property of the EnumerationOptions object that can be associated with ManagementObjectSearcher instance. These options did not have the desired effect for me.
I suppose I could perform these WMI commands in a separate thread, and surround the thread with monitoring code that kills it if it hasn't returned in a reasonable amount of time. That seems like a fair amount of work that would be pushed to all consumers of the c# WMI routines, and I'm hoping there's an easier way. Plus, I'm not sure that killing an outstanding thread this way properly cleans up the WMI connection.
Pinging the remote host doesn't do me any good, because knowing the host is up and running does not tell me if the credentials I have are appropriate (and if the c# WMI calls will hang). Is there another way to validate the credentials against a remote host?
It's always possible that there's an obvious flag or API I'm missing, because I would think others have run into this problem. Any information/assistance would be appreciated. Thanks for reading this lengthy post.
I don't know what all your special functions are, but here's a little routine to help you troubleshoot that should be able to wrap your routine in a thread and give it 5 seconds to execute:
void Fake() {
bool ok = false;
ConnectionOptions cOpts = new ConnectionOptions();
ManagementObjectCollection moCollection;
ManagementObjectSearcher moSearcher;
ManagementScope mScope;
ObjectQuery oQuery;
if (cOpts != null) {
mScope = new ManagementScope(String.Format("\\\\{0}\\{1}", host.hostname, "ROOT\\CIMV2"), cOpts);
if (mScope != null) {
oQuery = new ObjectQuery("Select * from Win32_OperatingSystem");
if (oQuery != null) {
moSearcher = new ManagementObjectSearcher(mScope, oQuery);
if (moSearcher != null) {
ManualResetEvent mre = new ManualResetEvent(false);
Thread thread1 = new Thread(() => {
moCollection = moSearcher.Get();
mre.Set();
};
thread1.Start();
ok = mre.WaitOne(5000); // wait 5 seconds
} else {
Console.WriteLine("ManagementObjectSearcher failed");
}
} else {
Console.WriteLine("ObjectQuery failed");
}
} else {
Console.WriteLine("ManagementScope failed");
}
} else {
Console.WriteLine("ConnectionOptions failed");
}
}
Hope that helps or gives you some ideas.
I took jp's suggestion to surround the WMI API calls in a separate thread that could be killed if they exceeded a timeout. When testing, the separate thread threw an exception of type System.UnauthorizedAccessException. I removed the threading logic and added a catch statement to handle this exception type. Sure enough, the exception is caught almost immediately following the call to ManagementObjectSearcher.Get().
try
{
moCollection = moSearcher.Get();
}
catch (System.UnauthorizedAccessException)
{
return Program.ERROR_FUNCTION_FAILED;
}
catch (System.Runtime.InteropServices.COMException)
{
MessageBox.Show("Error, caught COMException.");
return Program.ERROR_FUNCTION_FAILED;
}
(Note that the System.Runtime.InteropServices.COMException catch statement already existed in my code)
I don't know why this exception isn't thrown (or at least is not brought to the user's attention through the VS 2010 IDE) when executed as part of the parent thread. In any event, this is exactly what I was looking for, and is consistent with the behavior of the WMI connection routines in c++.
I'm using WMI for monitoring all our servers through a small C# service, which creates a bunch of ManagementScopes (one per server it should monitor) and periodically polls for CPU load etc.
However every once in a while it starts throwing COMExceptions, with the message "The RPC server is unavailable". Now that's fair enough if it was true, however I can manully connect to the server just fine, and if I create a new ManagementScope to the same server, I can reconnect without problems!
There's a problem with this approach though: It leaks memory :-(
ManagementScope has no Close, Dispose or similar cleanup function, and leaks memory when just garbage collected. This is, according to all my google searches, a problem with the underlying WMI components, and as such not a .Net issue.
So I figure my best approach is to solve the COMException issue, and just staying with the original ManagementScope - however if I manually call Connect on the scope after the COMException, it does return true (as in "Yes I've got a connection), but at first attempt at getting data from it, it throws another COMException.
I've tried quite a few things, but I simply cannot figure out why this happens :-(
The code is quite large, therefore I haven't pasted it here (and it's split into a lot of classes)
But basically I create a scope, and then call the following methods:
public ManagementObject GetSingleObject(string query)
{
using (var searcher = CreateSearcher(query))
{
try
{
using (var collection = searcher.Get())
{
return collection
.Cast<ManagementObject>()
.FirstOrDefault();
}
}
catch
{
return null;
}
}
}
private ManagementObjectSearcher CreateSearcher(string query)
{
return new ManagementObjectSearcher(_scope, new ObjectQuery(query), _options);
}
If you need more code, let me know :-)