Getting MSFT_Partition from MSFT_Disk using Associators - c#

I am trying to get a list of MSFT_Partitions from a MSFT_Disk object and loop over them.
This is the code I have been using so far but it always outputs an System.Management.ManagementException: 'Invalid query ' exception.
This is the code I am using at the moment:
public static void GetDiskInfo() {
var rawDiskInfos = new ManagementObjectSearcher("root\\Microsoft\\Windows\\Storage", "SELECT * FROM MSFT_Disk");
foreach(var rawDiskInfo in rawDiskInfos.Get()) {
Console.WriteLine(rawDiskInfo["FriendlyName"]);
GetPartitionInfo(rawDiskInfo["ObjectId"]);
}
}
public static void GetPartitionInfo(object objectId) {
ManagementScope scope = new ManagementScope("\\\\.\\ROOT\\Microsoft\\Windows\\Storage");
var query = new ObjectQuery("ASSOCIATORS OF {MSFT_Disk.ObjectId=\"" + objectId + "\"} WHERE AssocClass = MSFT_DiskToPartition");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach(var partiton in queryCollection) {
Console.WriteLine(partiton["Guid"]);
}
}
I already tried a lot of solutions I found online and all of them resulted in the same exception.
Thank you very much for your help!

Most queries you'll find on the internet don't do it, but in the general case, you must escape raw strings you pass to WQL queries (in your case objectId contains special characters), using the backslash character, with a method like this:
public static string EscapeWql(string text)
{
if (text == null)
return null;
var sb = new StringBuilder(text.Length);
foreach (var c in text)
{
if (c == '\\' || c == '\'' || c == '"')
{
sb.Append('\\');
}
sb.Append(c);
}
return sb.ToString();
}
So your method should now look like this:
public static void GetPartitionInfo(object objectId)
{
var scope = new ManagementScope(#"root\Microsoft\Windows\Storage");
var query = new ObjectQuery("ASSOCIATORS OF {MSFT_Disk.ObjectId=\"" + EscapeWql((string)objectId) + "\"} WHERE AssocClass = MSFT_DiskToPartition");
using (var searcher = new ManagementObjectSearcher(scope, query))
{
using (var queryCollection = searcher.Get())
{
foreach (var partition in queryCollection)
{
Console.WriteLine(partition["Guid"]);
}
}
}
}
PS: don't forget using statements on IDisposable classes.

Related

Wpf WMI Win32_Service Query

In my WPF application I need to get a list of Windows Services(~200) with a specific properties.
var result = new List<ServicesModel>();
ConnectionOptions connOptions = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
EnablePrivileges = true,
};
ManagementScope manScope = new ManagementScope($#"\\{System.Environment.MachineName}\ROOT\CIMV2",
connOptions);
manScope.Connect();
SelectQuery query = new SelectQuery("SELECT * FROM Win32_Service");
using (var searcher = new ManagementObjectSearcher(manScope, query))
foreach (var o in searcher.Get())
{
var obj = (ManagementObject) o;
ServicesModel service = new ServicesModel
{
Name = obj["DisplayName"] as string,
Path = obj["PathName"] as string,
Description = obj["Description"] as string,
Pid = Convert.ToInt32(obj["ProcessId"]),
Status = obj["State"] as string,
StartMode = obj["StartMode"] as string,
LogOnAs = obj["StartName"] as string
};
result.Add(service);
}
return result;
It takes approximately ~1 minute to execute this method and return data which is unacceptable.
Looks like searcher.Get() takes all that time...
What I can do to improve execute/return time/performance?
Thanks
Based on the comments I came up with following, hope it might help someone:
public List<string> GetWindowsServicesList()
{
var result = new List<string>();
foreach (ServiceController service in ServiceController.GetServices())
{
string serviceName = service.ServiceName;
result.Add(serviceName);
}
return result;
}
public ServicesModel GetServiceProperties(string serviceName)
{
ServicesModel service = null;
string path = $"Win32_Service.Name=\"{serviceName}\"";
using (ManagementObject mngObj = new ManagementObject(path))
{
service = new ServicesModel
{
Name = mngObj.GetPropertyValue("Name") as string,
Path = mngObj.GetPropertyValue("PathName") as string,
Description = mngObj.GetPropertyValue("Description") as string,
Pid = Convert.ToInt32(mngObj.GetPropertyValue("ProcessId")),
Status = mngObj.GetPropertyValue("State") as string,
StartMode = mngObj.GetPropertyValue("StartMode") as string,
LogOnAs = mngObj.GetPropertyValue("StartName") as string
};
}
return service;
}
public List<ServicesModel> WindowsServicesCollection()
{
var result = new List<ServicesModel>();
try
{
var windowsServicesNames = GetWindowsServicesList();
for (int i = 0; i < windowsServicesNames.Count(); i++)
{
result.Add(GetServiceProperties(windowsServicesNames[i]));
}
}
catch (System.Management.ManagementException ex)
{
}
catch (System.TimeoutException ex)
{
}
return result;
}
Works much, much faster than previous solution although it seems like performance is not super consistent and it depends from some various factors that I did not identified...

How to get Battery Health Using C#

I'm trying to get two values, DesignCapacity and FullChargeCacity, but they are not returning a value.
I'm using the code below:
public static bool CheckBattery()
{
bool _testResult = false;
string computer = ".";
try
{
ManagementScope scp = new ManagementScope(#"\\" + computer + #"\root\cimv2");
scp.Connect();
SelectQuery sql = new SelectQuery("CIM_Battery");
ManagementObjectSearcher mos = new ManagementObjectSearcher(scp,sql);
ManagementObjectCollection moc = mos.Get();
List<string> resultList = new List<string>();
foreach (ManagementObject o in moc)
{
foreach (PropertyData property in o.Properties)
{
resultList.Add( property.Name + ":" +property.Value);
}
File.WriteAllLines("bat.txt", resultList.ToArray());
//UInt16 availability = (UInt16)o["Availability"];
//resultList.Add("Availability," + availability.ToString());
//UInt16 batterystatus = (UInt16)o["BatteryStatus"];
//resultList.Add("BatteryStatus," + batterystatus.ToString());
//string status = (string)o["Status"];
//resultList.Add("Status," + status);
//UInt32 Desigcap = (UInt32)o["DesignCapacity"];
}
}
catch (Exception ex)
{
string errr = ex.Message;
}
return _testResult;
}
How Can I avoid to get null values in this properties? In fact iterating the objects im getting this information:
Availability:2
BatteryRechargeTime:
BatteryStatus:2
Caption:Internal Battery
Chemistry:2
ConfigManagerErrorCode:?
ConfigManagerUserConfig:?
CreationClassName:Win32_Battery
Description:Internal Battery
DesignCapacity:?
DesignVoltage:17119
DeviceID:3338SANYO AL12A32
ErrorCleared:?
ErrorDescription:?
EstimatedChargeRemaining:100
EstimatedRunTime:71582788
ExpectedBatteryLife:
ExpectedLife:?
FullChargeCapacity:?
InstallDate:?
LastErrorCode:
MaxRechargeTime:
Name:AL12A32
PNPDeviceID:
PowerManagementCapabilities:System.UInt16[]
PowerManagementSupported:False
SmartBatteryVersion:
Status:OK
StatusInfo:?
SystemCreationClassName:Win32_ComputerSystem
SystemName:MX02L180
TimeOnBattery:?
TimeToFullCharge:?
To get those particular values you need to do separate queries against different classes.
DesignCapacity can be queried from BatteryStaticData
FullChargeCacity can be queried from BatteryFullChargedCapacity
You'll need to use a different scope in the code also for the query. These classes are found in root/WMI instead of root/cimv2
string scope = "root/WMI";
string query = "SELECT DesignedCapacity FROM BatteryStaticData";
using (ManagementObjectSearcher batteriesQuery = new ManagementObjectSearcher(scope, query))
{
using (ManagementObjectCollection batteries = batteriesQuery.Get())
{
foreach (ManagementObject battery in batteries)
{
if (battery != null)
{
foreach (var property in battery.Properties)
{
Console.Log("Property name: " + property.Name + " Property value: " + property.Value);
}
}
}
}
}

C# + WMI + LPT help!

I'm making an application that needs to list all the LPT ports in a machine with their IO addresses. (ie it's output : LPT1 [starting ; ending] ....)
Using WMI you can get this info.. the name/number from Win32_ParallelPort and the addresses from Win32_PortResource.
The problem is that i don't know how to associate the portname with it's addresses.
You have to query three times and loop over the results to get the matching records from ParallelPort, PnPAllocatedResource and PortResource. The following code does exactly that:
var parallelPort = new ManagementObjectSearcher("Select * From Win32_ParallelPort");
//Dump(parallelPort.Get());
foreach (var rec in parallelPort.Get())
{
var wql = "Select * From Win32_PnPAllocatedResource";
var pnp = new ManagementObjectSearcher(wql);
var searchTerm = rec.Properties["PNPDeviceId"].Value.ToString();
// compensate for escaping
searchTerm = searchTerm.Replace(#"\", #"\\");
foreach (var pnpRec in pnp.Get())
{
var objRef = pnpRec.Properties["dependent"].Value.ToString();
var antref = pnpRec.Properties["antecedent"].Value.ToString();
if (objRef.Contains(searchTerm))
{
var wqlPort = "Select * From Win32_PortResource";
var port = new ManagementObjectSearcher(wqlPort);
foreach (var portRec in port.Get())
{
if (portRec.ToString() == antref)
{
Console.WriteLine( "{0} [{1};{2}]",
rec.Properties["Name"].Value,
portRec.Properties["StartingAddress"].Value,
portRec.Properties["EndingAddress"].Value );
}
}
}
}
}

how to refresh management object

i had written a code to display the description(Name) of connected USB devices.once i removed a device,then i need to refresh the ManagementObject and have to display the connected device description.
Here is my Code,
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("SELECT * FROM Win32_USBControllerDevice");
foreach (ManagementObject mo in searcher.Get())
{
string str1 = mo["CurrentRefreshRate"].ToString();
Console.WriteLine(str1);
string dependent = mo["Dependent"].ToString();
string deviceId = dependent.Split('=')[1];
deviceId = deviceId.Replace('\"', '\'');
ManagementObjectSearcher searcher2 =
new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity Where DeviceID = " + deviceId);
foreach (ManagementObject mo2 in searcher2.Get())
{
HardwareDetails Detail = new HardwareDetails();
Detail.Description = mo2["Description"].ToString();
Detail.DeviceId = mo2["DeviceId"].ToString();
string[] str = Detail.DeviceId.Split('\\');
string Id = str[1];
if (Id.Contains('&'))
{
string[] separate = Id.Split('&');
Detail.Vid = separate[0].Contains('_') ? separate[0].Split('_')[1] : separate[0].Split('D')[1];
Detail.Pid = separate[1].Contains('_') ? separate[1].Split('_')[1] : separate[1].Split('D')[1];
//Detail.Pid = pid1[1];
}
else
{
Detail.Vid = "";
Detail.Pid = "";
}
if (list.Count > 0)
{
foreach (HardwareDetails h in list)
{
if (!(h.Description == Detail.Description))
{
list.Add(Detail);
break;
}
}
}
else
list.Add(Detail);
}
}
// remove duplicates, sort alphabetically and convert to array
HardwareDetails[] usbDevices = list.ToArray();
return usbDevices;
Did you try this?
WqlEventQuery query = new WqlEventQuery(
"SELECT * FROM Win32_DeviceChangeEvent");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
watcher.EventArrived +=
new EventArrivedEventHandler(HandleEvent);
// Start listening for events
watcher.Start();
.........
// Stop listening for events
watcher.Stop();
And in the HandleEvent add or remove device from the list
Hope this helps!

WMI Poor Performance

I wrote a code in C# that maps logical drives to their physical disks, using WMI (System.Management).
The code working perfectly, but slow like hell.
In my machine (Windows 7 x64, Dual-Core with 3 GB RAM) it runs as least 1 second.
1 second is too slow for me, even 0.1 is more than enough to accomplish.
I more than sore that this functionallity can be done in less than 0.1 second.
Is there any Win32API functions that can help?
Any other suggestions?
this is my code so far:
List<Dictionary<string, string>> results = new List<Dictionary<string, string>>();
using (ManagementClass diskDriveClass = new ManagementClass(#"Win32_Diskdrive"))
{
using (ManagementObjectCollection diskDrives = diskDriveClass.GetInstances())
{
foreach (ManagementObject diskDrive in diskDrives)
{
string deviceId = (string)diskDrive["DeviceId"];
Dictionary<string, string> logicalDisksResults = new Dictionary<string, string>();
Trace.WriteLine(deviceId);
using (ManagementObjectCollection relatedPartitions = diskDrive.GetRelated("Win32_DiskPartition"))
{
foreach (ManagementObject relatedPartition in relatedPartitions)
{
Trace.WriteLine("-\t" + relatedPartition["Name"]);
using (ManagementObjectCollection relatedLogicalDisks = relatedPartition.GetRelated("Win32_LogicalDisk"))
{
foreach (ManagementBaseObject relatedLogicalDisk in
relatedLogicalDisks)
{
Trace.WriteLine("\t-\t" + relatedLogicalDisk["Name"] + " " + relatedLogicalDisk["FileSystem"]);
logicalDisksResults.Add((string)relatedLogicalDisk["Name"], (string)relatedLogicalDisk["FileSystem"]);
}
}
}
}
results.Add(logicalDisksResults);
}
}
}
Well here is some code which at least on my system runs (from an objective point of view) quicker and gives the same results. As the list of drives is barely likely to change on a second by second basis I am not sure why you really care so much, but anyway, see if this makes you happier. You can speed it slightly by removing the code getting Win32_DiskDrive at the start, good luck making it run in 0.1s though :)
Dictionary<string, Dictionary<string, string>> results = new Dictionary<string,Dictionary<string,string>>();
ManagementClass diskPartMap = null;
ManagementObjectCollection diskPartIns = null;
ManagementClass partLogicalMap = null;
ManagementObjectCollection partLogicalIns = null;
try
{
using (ManagementClass diskDriveClass = new ManagementClass("Win32_Diskdrive"))
{
using (ManagementObjectCollection diskDrives = diskDriveClass.GetInstances())
{
foreach (ManagementObject diskDrive in diskDrives)
{
results.Add((string)diskDrive["DeviceId"], new Dictionary<string, string>());
}
}
}
Dictionary<string, ManagementObject> partToDisk = new Dictionary<string, ManagementObject>();
Dictionary<string, ManagementObject> partToLogical = new Dictionary<string, ManagementObject>();
diskPartMap = new ManagementClass("Win32_DiskDriveToDiskPartition");
diskPartIns = diskPartMap.GetInstances();
foreach (ManagementObject diskDrive in diskPartIns)
{
ManagementObject o = new ManagementObject((string)diskDrive["Antecedent"]);
partToDisk.Add((string)diskDrive["Dependent"], o);
}
partLogicalMap = new ManagementClass("Win32_LogicalDiskToPartition");
partLogicalIns = partLogicalMap.GetInstances();
foreach (ManagementObject diskDrive in partLogicalIns)
{
ManagementObject o = new ManagementObject((string)diskDrive["Dependent"]);
string s = (string)diskDrive["Antecedent"];
partToLogical.Add(s, o);
}
foreach (KeyValuePair<string, ManagementObject> pair in partToDisk)
{
string deviceId = (string)pair.Value["DeviceId"];
Dictionary<string, string> dict = null;
if (!results.ContainsKey(deviceId))
{
dict = new Dictionary<string, string>();
results[deviceId] = dict;
}
else
{
dict = results[deviceId];
}
if (partToLogical.ContainsKey(pair.Key))
{
ManagementObject o = partToLogical[pair.Key];
dict.Add((string)o["Name"], (string)o["FileSystem"]);
}
}
}
finally
{
if (diskPartIns != null)
{
diskPartIns.Dispose();
diskPartIns = null;
}
if (diskPartMap != null)
{
diskPartMap.Dispose();
diskPartMap = null;
}
if (partLogicalIns != null)
{
partLogicalIns.Dispose();
partLogicalIns = null;
}
if (partLogicalMap != null)
{
partLogicalMap.Dispose();
partLogicalMap = null;
}
}
This is my final code, x23 faster than the first version, based on tyranid idea to use Win32_LogicalDiskToPartition.
private static Regex _logicalDiskNameRegex = new Regex("(?<=\")[^\"]*(?=\")");
private static Regex _partitionDiskIndexRegex = new Regex("(?<=\"Disk #)\\d+");
public static Dictionary<string, string>[] GetPhisicalHardDiskToDriveLettersMap()
{
DriveInfo[] driveInfoArr = DriveInfo.GetDrives();
DriveInfo lastDriveInfo = null;
Dictionary<string, DriveInfo> driveInfos = new Dictionary<string, DriveInfo>(driveInfoArr.Length);
foreach (DriveInfo driveInfo in driveInfoArr)
{
if (driveInfo.DriveType == DriveType.Fixed)
{
driveInfos.Add(driveInfo.Name.Substring(0, 2), driveInfo);
lastDriveInfo = driveInfo;
}
}
if (driveInfos.Count == 1 && lastDriveInfo != null)
{
return new Dictionary<string, string>[]
{
new Dictionary<string, string>()
{
{lastDriveInfo.Name.Substring(0, 2), lastDriveInfo.DriveFormat}
}
};
}
Dictionary<string, Dictionary<string, string>> results = new Dictionary<string, Dictionary<string, string>>();
using (ManagementClass partLogicalMap = new ManagementClass("Win32_LogicalDiskToPartition"))
{
using (ManagementObjectCollection partLogicalIns = partLogicalMap.GetInstances())
{
foreach (ManagementObject diskDrive in partLogicalIns)
{
bool lazySuccess = false;
string driveName = null;
string driveFileSystem = null;
string physicalHardDisk = null;
string logicalDiskPath = (string)diskDrive["Dependent"];
string partitionPath = (string)diskDrive["Antecedent"];
Trace.WriteLine(logicalDiskPath);
Trace.WriteLine(partitionPath);
Match logicalDiskNameMatch = _logicalDiskNameRegex.Match(logicalDiskPath);
if (logicalDiskNameMatch.Success)
{
Match partitionDiskIndexMatch = _partitionDiskIndexRegex.Match(partitionPath);
if (partitionDiskIndexMatch.Success)
{
try
{
driveName = logicalDiskNameMatch.Value;
physicalHardDisk = partitionDiskIndexMatch.Value;
driveFileSystem = driveInfos[driveName].DriveFormat;
lazySuccess = true;
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
}
}
if (!lazySuccess)
{
// old good code but less performance, to be on the safe side if lazy method fails.
ManagementObject logicalDiskObject = new ManagementObject(logicalDiskPath);
ManagementObject partitionObject = new ManagementObject(partitionPath);
driveName = (string)logicalDiskObject["Name"];
driveFileSystem = (string)logicalDiskObject["FileSystem"];
physicalHardDisk = partitionObject["DiskIndex"].ToString();
}
Dictionary<string, string> hardDiskDrives;
if (!results.TryGetValue(physicalHardDisk, out hardDiskDrives))
{
hardDiskDrives = new Dictionary<string, string>();
results.Add(physicalHardDisk, hardDiskDrives);
}
hardDiskDrives.Add(driveName, driveFileSystem);
}
}
}
return ToArray(results.Values);
}
I have found that the best route is to get the full data from each of the 4 classes and then do your joining with LINQ to minimize the impact on the WMI service (as it is slow under load).
As first you might think that sounds horrible, but test it out to see what I am talking about.
see this article (with code sample) about GetLogicalDrives, GetLogicalDriveStrings, GetDriveType, and GetVolumeInformation
To find physical drives, you can use FindFirstVolumeand FindNextVolume (I get "\.\Device{uiid}". Combined with GetVolumePathNamesForVolumeNameW to get the associated drive letter. Then you can get the info you want with above mentioned APIs.
If you need partition/disk numbers see DeviceIoControl to get that info
I thought you needed what is in results in your code.

Categories