Reputation: 15561
What is the C# equivalent to this PowerShell command?
PS C:\WINDOWS\system32> gwmi win32_DiskDrive | %{gwmi -query "ASSOCIATORS OF {$($_.__RELPATH)} where resultclass = Win32_PnpEntity" | %{gwmi -query "ASSOCIATORS OF {$($_.__RELPATH)}"}} | fl __CLASS,__RELPATH
The result of the above command is:
...
__CLASS : Win32_SystemDriver
__RELPATH : Win32_SystemDriver.Name="disk"
__CLASS : Win32_ComputerSystem
__RELPATH : Win32_ComputerSystem.Name="JMR-ENG-SARAH"
__CLASS : Win32_IDEController
__RELPATH : Win32_IDEController.DeviceID="PCI\\VEN_8086&DEV_8C82&SUBSYS_79171462&REV_00\\3&11583659&0&FA"
__CLASS : CIM_DataFile
__RELPATH : CIM_DataFile.Name="c:\\windows\\system32\\drivers\\disk.sys"
__CLASS : Win32_DiskDrive
__RELPATH : Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE1"
__CLASS : Win32_SCSIController
__RELPATH : Win32_SCSIController.DeviceID="PCI\\VEN_144D&DEV_A804&SUBSYS_A801144D&REV_00\\6&381D8F6A&0&00080008"
__CLASS : Win32_SystemDriver
__RELPATH : Win32_SystemDriver.Name="disk"
__CLASS : Win32_ComputerSystem
__RELPATH : Win32_ComputerSystem.Name="JMR-ENG-SARAH"
__CLASS : CIM_DataFile
__RELPATH : CIM_DataFile.Name="c:\\windows\\system32\\drivers\\disk.sys"
__CLASS : Win32_DiskDrive
__RELPATH : Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE2"
...
I guess what I do not understand is how the PowerShell command operates. I do know part of the translation.
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
foreach (ManagementObject wmi_HD in searcher.Get())
{
String driveDeviceId = wmi_HD["DeviceID"].ToString();
}
The above code is the gwmi win32_DiskDrive
part. What property I need to extract is open to discussion. I know that there is a laundry list of returned properties.
I have another code fragment, where I get the drive letters associated with physical disks, which uses the ASSOCIATORS OF
gwmi query. As such, the answer should be similar to:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
String query2 = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + driveDeviceId + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
foreach (ManagementObject partition in new ManagementObjectSearcher(query2).Get())
{
foreach (ManagementObject disk in new ManagementObjectSearcher(
"ASSOCIATORS OF {Win32_DiskPartition.DeviceID='"
+ partition["DeviceID"]
+ "'} WHERE AssocClass = Win32_LogicalDiskToPartition").Get())
{
String diskMount = disk["Name"].ToString();
}
}
The |
is simply an embedded for-loop, where the inner wmi query would use the result of the other loop element. The query that I would like is a bit more complex with a double set of |
and uses a double set of {$($_.__RELPATH)}
.
How would I write the C# code for the desired PowerShell script at the top?
Upvotes: 3
Views: 1136
Reputation: 15561
Note 1: I struggled for quite some time and did research and finally understood things a bit better. I was able to construct the answer to my question on my own. I am placing the answer here along with an explanation in hopes to educate others. At least for me, WMI is quite difficult to understand, especially with the intricacies.
My big breakthrough came with believe it or not this Microsoft web page along with help from ScryptingGuy and this SO article on escaping dollar signs in a WMI query filter.
I also realized that the filter at the end applies to queries. The source code equivalent would be to pull only that property, such as:
String propDiskRelpaths = wmiDisks["__RELPATH"].ToString();
I found out that braces are required by WMI and indicate a value. The dollar sign merely indicates the result from the outer query. The connotation ($_.__RELPATH)
is a fancy way of saying the __RELPATH property of the previous query.
The query first enumerates all disks on the system. The second query then enumerates all PNP Entities for the disk (there is only one), while the last query gets all path associations (driver hierarchy) for the disk. Each query drills down one level.
In case anyone is interested, here is the breakdown in PowerScript, which then led me to the code at the bottom. I only list one disk drive here, but I have 6 drives on my system at the moment: 0 through 5. Physical drive 3 shows below.
PS C:\WINDOWS\system32> gwmi win32_DiskDrive | fl __CLASS,__RELPATH
....
__CLASS : Win32_DiskDrive
__RELPATH : Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE3"
Here is the second command, which yields the PNP Entities for the disk. In this case, I chose my C: drive, drive 0, rather than my M.2 card, drive 3, to list here as an example. The real code is in a for-loop, so I would get all drives.
PS C:\WINDOWS\system32> gwmi -query 'ASSOCIATORS OF {Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE0"} where resultclass = Win32_PnpEntity' | fl __CLASS,__RELPATH
__CLASS : Win32_PnPEntity
__RELPATH : Win32_PnPEntity.DeviceID="SCSI\\DISK&VEN_NVME&PROD_SAMSUNG_SSD_960\\7&335357E&0&000000"
The last command and the output is:
PS C:\WINDOWS\system32> gwmi -query 'ASSOCIATORS OF {Win32_PnPEntity.DeviceID="SCSI\\DISK&VEN_NVME&PROD_SAMSUNG_SSD_960\\7&335357E&0&000000"}' | fl __CLASS,__RELPATH
__CLASS : Win32_SCSIController
__RELPATH : Win32_SCSIController.DeviceID="PCI\\VEN_144D&DEV_A804&SUBSYS_A801144D&REV_00\\6&381D8F6A&0&00080008"
__CLASS : Win32_SystemDriver
__RELPATH : Win32_SystemDriver.Name="disk"
__CLASS : Win32_ComputerSystem
__RELPATH : Win32_ComputerSystem.Name="JMR-ENG-SARAH"
__CLASS : CIM_DataFile
__RELPATH : CIM_DataFile.Name="c:\\windows\\system32\\drivers\\disk.sys"
__CLASS : Win32_DiskDrive
__RELPATH : Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE0"
As you can see, my M.2 card gets listed as a SCSI device rather than as an NVMe PCIe card that it really is. I did research and that is as expected, because the Microsoft StorNVMe.sys
driver implements NVMe as IOCTL_SCSI_*
translated to IOCTL_NVME_*
commands at the device driver level. That was true in Windows Vista and evidently true still with Windows 10.
PS C:\WINDOWS\system32> gwmi -query 'ASSOCIATORS OF {Win32_PnPEntity.DeviceID="SCSI\\DISK&VEN_NVME&PROD_SAMSUNG_SSD_960\\7&335357E&0&000000"}' | fl __CLASS,__RELPATH
__CLASS : Win32_SCSIController
__RELPATH : Win32_SCSIController.DeviceID="PCI\\VEN_144D&DEV_A804&SUBSYS_A801144D&REV_00\\6&381D8F6A&0&00080008"
__CLASS : Win32_SystemDriver
__RELPATH : Win32_SystemDriver.Name="disk"
__CLASS : Win32_ComputerSystem
__RELPATH : Win32_ComputerSystem.Name="JMR-ENG-SARAH"
__CLASS : CIM_DataFile
__RELPATH : CIM_DataFile.Name="c:\\windows\\system32\\drivers\\disk.sys"
__CLASS : Win32_DiskDrive
__RELPATH : Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE0"
Here is the final code that I wanted. I should add that had for IDE based drives, I can then enumerate the IDE class and get supported modes for that disk (IDE, AHCI, RAID, etc.).
Hopefully, this explanation is clear. Let me know, if it needs anymore explanation. Oh, the code below assumes VS2017 or above.
using System;
using System.Collections.Generic;
using System.Management;
using SiloStor.Tools;
internal static void EnumerateClassPaths()
{
try
{
// Enumerate all disk drives.
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
foreach (ManagementObject wmiDisks in oSearcher.Get())
{
// Get the properties needed.
String propDiskRelpaths = wmiDisks["__RELPATH"].ToString();
//
String wmiQuery1 = "ASSOCIATORS OF {" + propDiskRelpaths + "} WHERE ResultClass = Win32_PnpEntity";
foreach (ManagementObject wmiPnp in new ManagementObjectSearcher(wmiQuery1).Get())
{
// Get the properties needed.
String propPnpRelpaths = wmiPnp["__RELPATH"].ToString();
//
String wmiQuery2 = "ASSOCIATORS OF {" + propPnpRelpaths + "}";
foreach (ManagementObject wmiDrivers in new ManagementObjectSearcher(wmiQuery2).Get())
{
String driverClass = wmiDrivers["__CLASS"].ToString();
String driverRelpath = wmiDrivers["__RELPATH"].ToString();
Console.WriteLine($"__CLASS : {driverClass}");
Console.WriteLine($"__RELPATH : {driverRelpath}");
Console.WriteLine("");
}
}
}
}
catch (Exception ex)
{
// Log the error.
Errors.LogError(ex);
}
}
UPDATE:
Yesterday (August 9, 2017), I found a link to Microsoft WMI Code Creator
v1.0. I am not being paid by Microsoft nor do I think this tool a "Must Have" for WMI work, but it is actually quite nice and not advertised at all.
The WMI Code Creator
, for which gives source code to the App, generates C# and VB.Net source code for WMI queries. Sadly, the WMI queries are limited to basic commands, but still, very helpful. The other useful thing is that the tool shows classes that a user can query, also very helpful. Sadly, there are no ASSOCIATORS OF
or concatenated queries, but does have many other useful features. Check it out and hope that it helps.
Upvotes: 6