0

我有以下代码通过物理磁盘并显示 WMI 设置。

我注意到,如果 WMI 参数不存在,它不会显示任何内容(这很好),但这会干扰服务器上存在的后续 WMI 参数,并且它们现在不显示任何内容......即使它们应该......下面的例子。

所以,我的意思是代码通过这部分:

 lblcapability_desc.Text = "Capability Description: " + moDisk["CapabilityDescription"].ToString();
                    lblAvailability.Text = "Availability: " + moDisk["Availability"].ToString();
                    lblbytepersector.Text = "Bytes per Sector: " + moDisk["BytesPerSector"].ToString();
                    lbl_deviceid.Text = "Device ID: " + moDisk["systemname"].ToString();

它首先获取 SystemName,然后获取 Type,依此类推。

现在,如果在计算机上说“可用性”的 WMI 参数不存在,那么它将不会显示。那也行。但是,紧接在“可用性”之后有“每个扇区的字节数”参数和其他参数。这些参数也不显示任何内容(尽管我知道它们应该显示一些东西,因为它们确实存在于服务器上 - 通过 powershell 测试)。

好像“可用性”中缺少信息会干扰之后的参数并且不显示任何内容。

在解决这个问题时我能想到的唯一方法(这样即使可用性只显示后续参数显示他们需要的数据)是可能在每个 WMI 参数中添加一个 IF THEN - 不是很好的编码,但想不出解决此问题的一种方法。

所以一个例子是 IF [Availbility] 然后消息“Avialbility = 不管” ELSE “WMI 参数未找到”。这样,每个 WMI 参数都有自己的错误检查。我想不是很好的编码..

如果 WMI 参数不存在,则已捕获错误,然后向最终用户显示注释。

这是代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management;
using Microsoft.Win32;

namespace diskdrive_info
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //Get all the disk drives
            ManagementObjectSearcher mosDisk = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
            // Loop through each object (disk) retrieved by WMI
            foreach (ManagementObject moDisk in mosDisk.Get())
            {
                cmbHdd.Items.Add(moDisk["Model"].ToString());
            }
        }

        private void cmbHdd_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                ManagementObjectSearcher mosDisks = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE Model = '" + cmbHdd.SelectedItem + "'");
                foreach (ManagementObject moDisk in mosDisks.Get())
                {

                    lblSystemName.Text = "SystemName: " + moDisk["systemname"];
                    lblType.Text = "Type: " + moDisk["MediaType"].ToString();
                    lblModel.Text = "Model: " + moDisk["Model"].ToString();
                    lblCapacity.Text = "Capacity: " + moDisk["Size"].ToString() + " bytes (" + Math.Round(((((double)Convert.ToDouble(moDisk["Size"]) / 1024) / 1024) / 1024), 2) + " GB)";
                    lblPartitions.Text = "Partitions: " + moDisk["Partitions"].ToString();
                    lblSectors.Text = "Sectors: " + moDisk["SectorsPerTrack"].ToString();
                    lblSignature.Text = "Signature: " + moDisk["Signature"].ToString();
                    lblFirmware.Text = "Firmware: " +moDisk["FirmwareRevision"].ToString();
                    lblFirmware.Text = "Firmware: " + moDisk["FirmwareRevision"] == null ? "Not Available" : moDisk["FirmwareRevision"].ToString();
                    lblcapability_desc.Text = "Capability Description: " + moDisk["CapabilityDescription"].ToString();
                    lblAvailability.Text = "Availability: " + moDisk["Availability"].ToString();
                    lblbytepersector.Text = "Bytes per Sector: " + moDisk["BytesPerSector"].ToString();
                    lbl_deviceid.Text = "Device ID: " + moDisk["systemname"].ToString();
                }
            }
            catch (Exception exp)
            {
                lblError.Text = "Some properties were not shown due to WMI errors or member not available on this system";
            }

        }

        private void btn_clear_Click(object sender, EventArgs e)
        {
            //lblSystemName.Text = string.Empty;
            lblSystemName.Text = "SystemName: " + "";
            lblType.Text = "Type: " + "";
            lblModel.Text = "Model: " + "";
            lblCapacity.Text = "Capacity: " + "";
            lblPartitions.Text = "Partitions: " + "";
            lblSectors.Text = "Sectors: " + "";
            lblSignature.Text = "Signature: " + "";
            lblFirmware.Text = "Firmware: " + "";
            lblError.Text = String.Empty;
            cmbHdd.Items.Clear();
            cmbHdd.Text = "";
            ManagementObjectSearcher mosDisk = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
            foreach (ManagementObject moDisk in mosDisk.Get())
            {
                cmbHdd.Items.Add(moDisk["Model"].ToString());
            }
        }
    }
}
4

1 回答 1

2

您遇到的问题

好像“可用性”中缺少信息会干扰之后的参数并且不显示任何内容。

部分情况是这样:“可用性”不存在并且抛出 NullReferenceException。在分配标签之前检查它是否为空(代码似乎正在使用 FirmwareRevision 执行此操作)。

lblAvailability.Text = "Availability: " + moDisk["Availability"] != null ? moDisk["Availability"].ToString() : string.Empty;

问题的第二部分是你包装了整个方法体,包括 try-catch 中的迭代语句。如果块中发生未处理的异常try,则执行切换到catch块。

额外的功劳:使用泛型和反射简化 WMI 报告

我已经看到很多实例,其中 WMI 信息直接从 WMI 查询对象显示在 UI 中,您在 UI 中看到类似这样的内容几千次:

lblName.Text = mgmtObjQuery["Name"]

虽然这确实给了你想要的结果,但有更有效的方法可以做到这一点,不会导致过早脱发。等到您的 PM 扩展要求以包括来自 root\cimv2 的另外 20 个类。

我想出了一个解决方案来使用泛型、强类型类和反射来获取 WMI 信息来完成繁重的工作,现在我的代码看起来更像这样:

lblName.Text = disk.Name;

首先,为 WMI 类创建一个模型。类名及其字段名都应对应于相同的 WMI 类名和字段。类和字段应该是公共的,并且数据类型也应该匹配 C# 数据类型。

转到Win32_DiskDrive 的 MSDN 参考,并生成一个如下所示的类:

/// <summary>
/// A Win32_DiskDrive class based on 
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa394132(v=vs.85).aspx
/// </summary>
public class Win32_DiskDrive 
{
    public UInt16 Availability;
    public UInt32 BytesPerSector;
    public UInt16[] Capabilities ;
    public string[] CapabilityDescriptions ;
    public string Caption;
    public string CompressionMethod;
    public UInt32 ConfigManagerErrorCode;
    public bool ConfigManagerUserConfig;
    public string CreationClassName;
    public UInt64 DefaultBlockSize;
    public string Description;
    public string DeviceID;
    public bool ErrorCleared;
    public string ErrorDescription;
    public string ErrorMethodology;
    public string FirmwareRevision;
    public UInt32 Index;
    public DateTime InstallDate;
    public string InterfaceType;
    public UInt32 LastErrorCode;
    public string Manufacturer;
    public UInt64 MaxBlockSize;
    public UInt64 MaxMediaSize;
    public bool MediaLoaded;
    public string MediaType;
    public UInt64 MinBlockSize;
    public string Model;
    public string Name;
    public bool NeedsCleaning;
    public UInt32 NumberOfMediaSupported;
    public UInt32 Partitions;
    public string PNPDeviceID;
    public UInt16[] PowerManagementCapabilities ;
    public bool PowerManagementSupported;
    public UInt32 SCSIBus;
    public UInt16 SCSILogicalUnit;
    public UInt16 SCSIPort;
    public UInt16 SCSITargetId;
    public UInt32 SectorsPerTrack;
    public string SerialNumber;
    public UInt32 Signature;
    public UInt64 Size;
    public string Status;
    public UInt16 StatusInfo;
    public string SystemCreationClassName;
    public string SystemName;
    public UInt64 TotalCylinders;
    public UInt32 TotalHeads;
    public UInt64 TotalSectors;
    public UInt64 TotalTracks;
    public UInt32 TracksPerCylinder;

    public Win32_DiskDrive()
    {

    }
}

这种通用方法将完成繁重的工作。此方法使用反射将字段名称映射到 WMI 查询结果。空值检查和日期时间转换都在一个地方处理:

/// <summary>
/// Generic method that uses reflection for wiring up a local class to the corresponding win32_class and properties.
/// </summary>
/// <typeparam name="T">A class who's name and fields correspond to those of a WMI class.</typeparam>
/// <returns>A collection of WMI data.</returns>
public static IEnumerable<T> WmiSnapshot<T>()
{
    // The name of T must match that of the WMI class
    var searcher = new ManagementObjectSearcher(new SelectQuery(Activator.CreateInstance<T>().GetType().Name));

    foreach (ManagementObject managementObject in searcher.Get())
    {
        // Creates an instance of T
        var listItem = Activator.CreateInstance<T>();
        // an array of PUBLIC FIELDS of T
        var fields = listItem.GetType().GetFields();
        // matches a value from the WMI query to a field name
        foreach (FieldInfo field in fields)
        {
            if (managementObject[field.Name] != null)
            {
                field.SetValue(listItem,
                    field.FieldType == typeof(DateTime)
                        ? ManagementDateTimeConverter.ToDateTime(managementObject[field.Name].ToString())
                        : Convert.ChangeType(managementObject[field.Name], field.FieldType));
            }
        }
        yield return listItem;
    }
}

我将 WMI 类和 WMI 辅助方法保存在它们自己的类中,以便从 UI 调用。UI 代码更易于阅读且更易于调试。

这是一个将快照的结果输出到控制台的快速示例:

var disks = WmiHelper.WmiSnapshot<Win32_DiskDrive>().ToList(); 

StringBuilder sb = new StringBuilder(); 

foreach (Win32_DiskDrive disk in disks)
{
    sb.AppendFormat("SystemName: {0}\r\n", disk.SystemName);
    sb.AppendFormat("Type: {0}\r\n",  disk.MediaType);
    sb.AppendFormat("Model: {0}\r\n",  disk.Model);
    sb.AppendFormat("Capacity: {0}\r\n", disk.Size );
    sb.AppendFormat("Partitions: {0}\r\n", disk.Partitions);
    sb.AppendFormat("Sectors: {0}\r\n",  disk.SectorsPerTrack);
    sb.AppendFormat("Signature: {0}\r\n", disk.Signature);
    sb.AppendFormat("Firmware: {0}\r\n",  string.IsNullOrEmpty(disk.FirmwareRevision) ? "Not Available" : disk.FirmwareRevision);
    sb.AppendFormat("Capability Description: {0}\r\n", string.Join("\r\n", disk.CapabilityDescriptions));
    sb.AppendFormat("Availability: {0}\r\n", disk.Availability);
    sb.AppendFormat("Bytes per Sector: {0}\r\n", disk.BytesPerSector);
    sb.AppendFormat("Device ID: {0}\r\n", disk.DeviceID);
    sb.AppendLine();
}

Console.WriteLine(sb.ToString());

输出看起来像这样:

系统名称:UVVXWVXXWV
类型:固定硬盘介质
型号:ST9500420AS
容量:500105249280
分区:4
部门:63
签名:2210653369
固件:0006
能力描述:随机访问
支持写作
智能通知
可用性:0
每个扇区的字节数:512
设备 ID:\\.\PHYSICALDRIVE0

现在对Win32_ComputerSystem然后Win32_Product重复相同的操作!

让我知道这是否能让你朝着正确的方向前进。

于 2012-09-24T21:06:09.930 回答