English 中文(简体)
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("-	" + relatedPartition["Name"]);

                    using (ManagementObjectCollection relatedLogicalDisks = relatedPartition.GetRelated("Win32_LogicalDisk"))
                    {
                        foreach (ManagementBaseObject relatedLogicalDisk in
                        relatedLogicalDisks)
                        {
                            Trace.WriteLine("	-	" + 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.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签