C# - Hardware status check, working but crashing - c#

Working but crashing is weird to say, but the hardware status gets printed in the console window but my application crash when i run this. I have it on the load of the form.
Error says: Object reference not set to an instance of an object
And here is the code:
// Hardware check
ManagementObjectSearcher deviceList =
new ManagementObjectSearcher("Select Name, Status from Win32_PnPEntity");
// Any results? There should be!
if (deviceList != null)
// Enumerate the devices
foreach (ManagementObject device in deviceList.Get())
{
// To make the example more simple,
string name = device.GetPropertyValue("Name").ToString();
string status = device.GetPropertyValue("Status").ToString();
// Uncomment these lines and use the "select * query" if you
// want a VERY verbose list
// foreach (PropertyData prop in device.Properties)
// Console.WriteLine( "\t" + prop.Name + ": " + prop.Value);
// More details on the valid properties:
//
Console.WriteLine("Device name: {0}", name);
Console.WriteLine("\tStatus: {0}", status);
// Part II, Evaluate the device status.
bool working = ((status == "OK") || (status == "Degraded")
|| (status == "Pred Fail"));
Console.WriteLine("\tWorking?: {0}", working);
}

So your problem when working with management objects is that all properties can return null and you need to account for this at all times as well as some other issues:
The searcher is not a list of devices nor will it ever contain one so you shouldn't call it deviceList.
You don't need to check the searcher for a value as all we're doing is initializing the class, it won't fail without an Exception.
You need to clean up after yourself by disposing of the management objects.
Since both Name and Status properties are strings anyway, you can cast them as such without calling .ToString() and use ?? string.Empty to replace null values with an empty string.
// Hardware check
using (ManagementObjectSearcher deviceSearcher = new ManagementObjectSearcher("Select Name, Status from Win32_PnPEntity"))
using (ManagementObjectCollection devices = deviceSearcher.Get())
{
// Enumerate the devices
foreach (ManagementObject device in devices)
{
// To make the example more simple,
string name = (string)device.GetPropertyValue("Name") ?? string.Empty;
string status = (string)device.GetPropertyValue("Status") ?? string.Empty;
// Uncomment these lines and use the "select * query" if you
// want a VERY verbose list
// foreach (PropertyData prop in device.Properties)
// Console.WriteLine("\t{0}: {1}", prop.Name, prop.Value);
// More details on the valid properties:
//
Console.WriteLine("Device name: {0}", name);
Console.WriteLine("\tStatus: {0}", status);
// Part II, Evaluate the device status.
bool working = status == "OK" || status == "Degraded" || status == "Pred Fail";
Console.WriteLine("\tWorking?: {0}", working);
}
}

Related

Mimic this very simple powershell command in C#

Trying to mimic the command Get-CimInstance CIM_ManagedSystemElement in C#
string NamespacePath = "\\\\.\\Root\\CIMv2";
string ClassName = "CIM_ManagedSystemElement";
//Create ManagementClass
ManagementClass oClass = new ManagementClass(NamespacePath + ":" + ClassName);
//Get all instances of the class and enumerate them
foreach (ManagementObject oObject in oClass.GetInstances())
{
//access a property of the Management object
Console.WriteLine("Caption : {0}", oObject["Caption"]);
}
Sadly, that didnt work as expected, would like to get some help
Thanks
You do this like this (you have to add System.Management namespace)
Because CIM_ManagedSystemElement is at the default WMI namespace( which is Root\CIMV2) you don't have to specify it at ManagementObjectSearcher.
Also, be sure that you have the minimum supported client- Windows Vista
string query = #"SELECT * FROM CIM_ManagedSystemElement";
var moSearch = new ManagementObjectSearcher(query);
var moCollection = moSearch.Get();
foreach (ManagementObject mo in moCollection)
{
Console.WriteLine("Caption = " + mo["Caption"]);
}
Furthermore i suggest you use an ORM to remove boilerplate code like ORMi or Kexla
I also couldn't get your code to work, but in the meantime if you need a workaround you can use the PowerShell API from within C# using this simple program I wrote based on some online documentation. It will give you an output you're looking for. You should have access to all the properties in OutputCollection_DataAdded so if you need more than Caption you can grab it here. Also, at the end of the execution there is a foreach() loop that will contain the entire output collection if you need to do something with that. The execution is extremely slow so I had to make it async to work.
static void Main(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Get-CimInstance");
ps.AddParameter("-ClassName", "CIM_ManagedSystemElement");
var outputCollection = new PSDataCollection<PSObject>();
outputCollection.DataAdded += OutputCollection_DataAdded;
// invoke execution on the pipeline (collecting output)
var async = ps.BeginInvoke<PSObject, PSObject>(null, outputCollection);
// do something else until execution has completed.
// this could be sleep/wait, or perhaps some other work
while (async.IsCompleted == false)
{
Console.WriteLine("Waiting for pipeline to finish...");
Thread.Sleep(1000);
// might want to place a timeout here...
}
Console.WriteLine("Execution has stopped. The pipeline state: " + ps.InvocationStateInfo.State);
// loop through each output object item
foreach (PSObject outputItem in ps.EndInvoke(async))
{
// if null object was dumped to the pipeline during the script then a null
// object may be present here. check for null to prevent potential NRE.
if (outputItem != null)
{
//TODO: do something with the output item
// outputItem.BaseOBject
}
}
Console.Read();
}
}
private static void OutputCollection_DataAdded(object sender, DataAddedEventArgs e)
{
if (sender is PSDataCollection<PSObject>)
{
var output = (PSDataCollection<PSObject>)sender;
// Handle the output item here
var caption = output.Last().Properties["Caption"];
if (caption != null)
{
Console.WriteLine($"Caption: {caption.Value}");
}
}
}

How can I find out if my microphone is muted

As the title says I'd like to check if my microphone is muted. Or even better get an event if it gets muted/unmuted.
I tried to get some information form the internet. Turns out I simply don't get it. I found out about the "new" Audio Core API which gave me some ideas, sadly I did not find any c# code so I cant figure out how to use any of this.
With the following code I could get some information for my microphone, however nothing changes if I mute it.
ManagementObjectSearcher objSearcher = new ManagementObjectSearcher(
"SELECT * FROM Win32_SoundDevice");
ManagementObjectCollection objCollection = objSearcher.Get();
ManagementObject rodeMic = null;
foreach (ManagementObject obj in objCollection)
{
foreach (PropertyData property in obj.Properties)
{
if (property.Name == "DeviceID" && (string) property.Value == "USB\\VID_...")
{
rodeMic = obj;
}
}
}
if (rodeMic != null)
{
foreach (var property in rodeMic.Properties)
{
Console.Out.WriteLine(String.Format("{0}:{1}", property.Name, property.Value));
}
}

Is there a way to check if a printing process was successful?

I have an application where I need to print a ticket. Each ticket must be unique. The application is windows forms and written entirely in c#. For our application we're using Samsung ML- 2525 laser monochromatic printers.
The flow is basically the following, the operator picks a product/ticket (which is unique) and then it presses a button that does 2 things:
Connects to a database and updates the product as used
Prints the ticket (this is done using System.Drawing and GDI+)
For some reason, every once in a while, the image that needs to be printed is not sent to the printer. It's a rare case, but it happens.
I tried to connect to the printer using Win32_Printer ( http://msdn.microsoft.com/en-us/library/Aa394363 ) but I can't get to get the current printer's state (online, offline, low toner, paper jam, etc). I can only check if the printer exists and that the paper size is installed correctly. I tried code similar to the following but it didn't work
private string MonitorPrintJobWmi()
{
var jobMessage = String.Empty;
var scope = new ManagementScope(ManagementPath.DefaultPath);
scope.Connect();
var selectQuery = new SelectQuery { QueryString = #"select * from Win32_PrintJob" };
var objSearcher = new ManagementObjectSearcher(scope, selectQuery);
var objCollection = objSearcher.Get();
foreach (var job in objCollection)
{
if (job != null)
{
jobMessage += String.Format("{0} \r\n", job["Name"].ToString());
jobMessage += String.Format("{0} \r\n", job["JobId"].ToString());
_jobId = Convert.ToInt32(job["JobId"]);
jobMessage += String.Format("{0} \r\n", job["JobStatus"].ToString());
jobMessage += String.Format("{0} \r\n", job["Status"].ToString());
}
}
return jobMessage;
}
I tried to get an API for the printer but I couldn't get a hold of it. By the way, the printer's software do indicate different errors in the windows toolbar.
My question is if anyone can lead me in the right direction as to how to connect to a printer and check if printing was successful.
Also, it would be helpful if someone know of some other specific printer in which I may accomplish this ie, changing hardware.
Thanks,
To get a list of print queues on the local machine, try PrintServer's GetPrintQueues method.
Once you have an instance of the PrintQueue object associated with the relevant printer, you can use it to access the printer's status (IsOffline, IsPaperOut, etc.). Also, you can use it to get a list of the jobs in the given queue (GetPrintJobInfoCollection) which then will allow you to get job-specific status information (IsInError, IsCompleted, IsBlocked, etc.).
Hope this helps!
After try to print your PrintDocument (System.Drawing.Printing), try to check status of printjobs.
First step: Initialize your printDocument.
Second step: Get your printer Name From System.Drawing.Printing.PrinterSettings.InstalledPrinters.Cast<string>();
And copy it into your printerDocument.PrinterSettings.PrinterName
Third step: Try to print and dispose.
printerDocument.Print();
printerDocument.Dispose();
Last step: Run the check in a Task (do NOT block UI thread).
Task.Run(()=>{
if (!IsPrinterOk(printerDocument.PrinterSettings.PrinterName,checkTimeInMillisec))
{
// failed printing, do something...
}
});
Here is the implementation:
private bool IsPrinterOk(string name,int checkTimeInMillisec)
{
System.Collections.IList value = null;
do
{
//checkTimeInMillisec should be between 2000 and 5000
System.Threading.Thread.Sleep(checkTimeInMillisec);
// or use Timer with Threading.Monitor instead of thread sleep
using (System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_PrintJob WHERE Name like '%" + name + "%'"))
{
value = null;
if (searcher.Get().Count == 0) // Number of pending document.
return true; // return because we haven't got any pending document.
else
{
foreach (System.Management.ManagementObject printer in searcher.Get())
{
value = printer.Properties.Cast<System.Management.PropertyData>().Where(p => p.Name.Equals("Status")).Select(p => p.Value).ToList();
break;
}
}
}
}
while (value.Contains("Printing") || value.Contains("UNKNOWN") || value.Contains("OK"));
return value.Contains("Error") ? false : true;
}
Good luck.

how can I turn off 'allow hybrid sleep' in advanced power setting? by c#

how can I turn off 'allow hybrid sleep' in advanced power setting? by c#
by manually: power options -> change Plan Settings -> change advanced power setting->
Sleep-> 'Allow hybrid sleep' -> plugged in: OFF
If you are targeting Windows 7/2008 Server then you can use WMI and the Win32_PowerSetting class. Below is code that does that. Make sure to add an assembly reference and using directive to System.Management.
private bool SetAllowHybridSleep(bool enabled)
{
//Machine to work on, "." for local
string RemotePC = ".";
//Set the namespace to the power namespace, used throughout the function
ManagementScope ms = new ManagementScope(#"\\" + RemotePC + #"\root\cimv2\power");
//Will hold each of our queries
ObjectQuery oq = null;
//Will hold the values of our power plan and the specific setting that we want to change
Guid PowerPlanInstanceId = Guid.Empty;
string PowerSettingInstanceId = null;
//Look for the specific setting that we want
oq = new ObjectQuery(string.Format("SELECT * FROM Win32_PowerSetting WHERE ElementName = 'Allow hybrid sleep'"));
using (ManagementObjectSearcher mos = new ManagementObjectSearcher(ms, oq))
{
ManagementObjectCollection results = mos.Get();
foreach (ManagementObject obj in results)
{
foreach (PropertyData p in obj.Properties)
{
if (p.Name == "InstanceID")
{
//This will give us a string with a GUID specific to our setting
PowerSettingInstanceId = p.Value.ToString();
break;
}
}
}
}
//Sanity check
if (string.IsNullOrEmpty(PowerSettingInstanceId))
{
Console.WriteLine("System does not support hybrid sleep");
return false;
}
//Look for the active power scheme
oq = new ObjectQuery("SELECT * FROM Win32_PowerPlan WHERE IsActive=True");
using (ManagementObjectSearcher mos = new ManagementObjectSearcher(ms, oq))
{
ManagementObjectCollection results = mos.Get();
foreach (ManagementObject obj in results)
{
foreach (PropertyData p in obj.Properties)
{
if (p.Name == "InstanceID")
{
//The instance contains a string with a GUID inside of it, use the code below to get the GUID by itself
if (!Guid.TryParse(System.Text.RegularExpressions.Regex.Match(p.Value.ToString(), #"\{[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12}\}").Value, out PowerPlanInstanceId))
{
Console.WriteLine("Could not find active power plan");
return false;
}
break;
}
}
}
}
//Now we need to update the actual power setting in the active plan
//Get all power schemes for the target setting
oq = new ObjectQuery(string.Format("ASSOCIATORS OF {{Win32_PowerSetting.InstanceID=\"{0}\"}} WHERE ResultClass = Win32_PowerSettingDataIndex", PowerSettingInstanceId.Replace(#"\", #"\\")));
using (ManagementObjectSearcher mos = new ManagementObjectSearcher(ms, oq))
{
ManagementObjectCollection results = mos.Get();
foreach (ManagementObject obj in results)
{
foreach (PropertyData p in obj.Properties)
{
//See if the current scheme is the current setting. This will happen twice, once for AC and once for DC
if (p.Name == "InstanceID" && p.Value.ToString().Contains(PowerPlanInstanceId.ToString()))
{
//Change the value of the current setting
obj.SetPropertyValue("SettingIndexValue", (enabled ? "1" : "0"));
obj.Put();
break;
}
}
}
}
return true;
}
Using procmon, I managed to work out that the following registry key is responsible for it on my machine.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\238C9FA8-0AAD-41ED-83F4-97BE242C8F20\94ac6d29-73ce-41a6-809f-6363ba21b47e
You'll probably have to do some research on your machine to see how it works for you.
You can also call on the powercfg utility to do this. Each power setting is identified by three things:
GUID of power profile
GUID of profile subgroup
GUID of setting
You can use powercfg -QUERY to produce a full list of values.
Once you've got the GUID of the profile you want to edit, the GUID of the subgroup (in this case the Sleep subgroup) and the GUID of the setting (Allow Hybrid Sleep) you can use either powercfg -SETACVALUEINDEX for plugged in or powercfg -SETDCVALUEINDEX for on battery to set the value.
In my case (Win7 Ultimate x64) you can turn it off using:
powercfg -SETACVALUEINDEX 381b4222-f694-41f0-9685-ff5bb260df2e 238c9fa8-0aad-41ed-83f4-97be242c8f20 94ac6d29-73ce-41a6-809f-6363ba21b47e 1
This translates to the AcSettingIndex value in:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\238C9FA8-0AAD-41ED-83F4-97BE242C8F20\94AC6D29-73CE-41A6-809F-6363BA21B47E\DefaultPowerSchemeValues\381b4222-f694-41f0-9685-ff5bb260df2e

C# bool not set correctly

I have some C# code that's attempting to do an LDAP search using a supplied computer name to determine whether or not the computer account is disabled. Most of this code was taken from this SO question. The code in the example link works great and correctly displays true if I go disable an account in AD and false on computers that are active. The issue is that I can't exactly use the code in the manner it was originally presented, it must be used in the manner I have pasted it below. The problem with the below code is that it's always returning false, it doesn't seem to matter what computer name you pass to it. I also realize that the foreach loop is probably unneeded since I'm only trying to find one computer.
using System;
using System.DirectoryServices;
namespace DynamicNamespace
{
public class DynamicClass
{
public System.Boolean DynamicMethod(System.Boolean IsDisabled, System.String ComputerName)
{
//the string should be your a DC(domain controller)
const string ldap = "LDAP://server-name";
//DirectoryEntry is used for manipulating objects (users, computers)
DirectoryEntry entry = new DirectoryEntry(ldap);
//DirectorySearcher responds to a filter method for LDAP searches
//http://www.tek-tips.com/faqs.cfm?fid=5667 has a decent query guide
DirectorySearcher dSearch = new DirectorySearcher(entry);
//SAM Account Name was showing a $ sign at one point, using * for wildcard
dSearch.Filter = String.Format("samAccountName={0}*", ComputerName);
dSearch.PropertiesToLoad.Add("samAccountName");
dSearch.PropertiesToLoad.Add("userAccountControl");
SearchResultCollection results = dSearch.FindAll();
foreach (SearchResult result in results)
{
int userAccountControl = Convert.ToInt32(result.Properties["userAccountControl"][0]);
string samAccountName = Convert.ToString(result.Properties["samAccountName"][0]);
bool disabled = ((userAccountControl & 2) > 0);
if (disabled == false)
{
IsDisabled = false;
}
else
{
IsDisabled = true;
}
}
return IsDisabled;
}
}
}
You are probably receiving more than one SearchResult and since you're using a loop IsDisabled will be assigned multiple times.
According to the link in your comments, you're doing a partial match:
PARTIAL MATCH......................(attribute={partial value}*)
If the supplied computer name is exact, why not use:
EQUALITY...........................(attribute=value)
Then you can remove the loop:
dSearch.Filter = String.Format("(samAccountName={0})", ComputerName);
dSearch.PropertiesToLoad.Add("samAccountName");
dSearch.PropertiesToLoad.Add("userAccountControl");
SearchResult result = dSearch.FindOne();
bool disabled = (result != null) && ((userAccountControl & 2) > 0);
You should step through the debugger to confirm this, but its possible that if you are passing false in as the first argument when you call this function and the search is not getting any results, then your function will return the same false value that you passed in to begin with through IsDisabled.
There is nothing wrong with your code, the only problem is that you don't have a distinction between if the account does not exist and if it exists but is disabled.
You can do the following to detect if the account does not exists, it does not matter that you are doing a for loop because like you said it only does it once but if you like change it to below... (You have to change it to cater for the fact that it can return more than 1 results as well because you've got a * in your search filter)
SearchResultCollection results = dSearch.FindAll();
if (results.Count == 0)
throw new Exception("Account not found.");
else if (results.Count == 1)
{
SearchResult result = results[0];
int userAccountControl = Convert.ToInt32(result.Properties["userAccountControl"][0]);
string samAccountName = Convert.ToString(result.Properties["samAccountName"][0]);
bool disabled = ((userAccountControl & 2) > 0);
if (disabled == false)
{ IsDisabled = false; }
else { IsDisabled = true; }
}
else
throw new Exception("More than 1 result found, please filter");
try
{
bool res = dc.DynamicMethod(false, "Username");
}
catch (Exception ex)
{
if (ex.Message == "Account not found.")
{
//Do Something
}
else
throw ex;
}
Obviously you can replace throwing an exception with something more appropriate...

Categories

Resources