Reputation: 4552
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);
}
}
}
Upvotes: 3
Views: 3447
Reputation: 2323
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.
Upvotes: 1
Reputation: 4552
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);
}
Upvotes: 1
Reputation: 13318
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; } }
Upvotes: 1
Reputation:
see this article (with code sample) about GetLogicalDrives, GetLogicalDriveStrings, GetDriveType, and GetVolumeInformation
To find physical drives, you can use FindFirstVolume
and 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.
Upvotes: 0