質問

The current structure of my classes goes a little bit like this: PC.Processor.Architecture[0] = The Architecture of the first processor (assuming a multi-processor system).

How I ideally want it is more like this: PC.Processor[0].Architecture, because it's a bit more self explanatory this way.

Is there a fairly efficient way of doing this with what I have? Bear in mind there are like, over 9000 properties in each of the classes Processor, Motherboard, Memory etc and WMI calls are not CPU-cheap to run.

Here are the important snippets for my classes

class PC
{
    public Processor Processor;
    public Motherboard Motherboard;

    // Constructor
    public PC()
    {
        Processor = new Processor();
        Motherboard = new Motherboard();
    }

    // Method to get all info sequentially
    public void GetAllInfo()
    {
        Processor.GetInfo();
        Motherboard.GetInfo();
    }
}

class Processor
{
    public string[] Architecture;
    public string[] Availability;
    public UInt16[] Cores;

    public void GetInfo()
    {
        // Get WMI Information from custom process
        // Returns as an array of ManagementObjects for each matched device (which is a bit like an array of dictionaries)
        ManagementObject[] WMIData = DataRetriever.GetWMIData("Win32_Processor");
        try
        {
            for (int i = 1; i < WMIData.Length; i++)
            {
                this.Architecture[i] = (string)WMIData[i]["Architecture"];
                this.Availability[i] = (string)WMIData[i]["Availability"];
                this.Cores[i] = (UInt16)WMIData[i]["NumberOfCores"];
            }
        }
        catch (NullReferenceException e)
        {
            // To be implemented
        }
    }
}

FURTHERMORE
There may be more than one WMI search query per class. For example, HardDrive needs to make use of both Win32_PhysicalMedia and ATAPI_SmartData (or whatever the class actually is.)

役に立ちましたか?

解決 3

Thanks to everyone who's responded. I've come up with a reasonably elegant solution that suits my needs.

PC Class Example:

public class PC
{
    public List<Processor> Processor;

    // Constructor
    public PC()
    {
        this.Processor = new List<Processor>();
    }

    // Method to get all info sequentially
    public void GetAllInfo()
    {
        // These temporary stores fetch WMI data as ManagementObjects
        // Most cases will only need one WMI class.
        ManagementObject[] WMIDataTemp1;
        ManagementObject[] WMIDataTemp2;

        WMIDataTemp1 = DataRetriever.GetWMIData("Win32_Processor");
        foreach (ManagementObject Object in WMIDataTemp1)
        {
            this.Processor.Add(new Processor(Object));
        }
    }

    public void RefreshAll()
    {
        // Delete the lists and start again
        // Another option would be to foreach through the list elements and initialise each object again.
        this.Processor.Clear();
        GetAllInfo();
    }

    public void RefreshVolatileData()
    {
        // Extra function that will do some cool stuff later.
    }
}

Processor Class Example:

public class Processor
{
    // Define properties
    public string Architecture = "N/A";
    public string Availability = "N/A";
    public UInt32 CacheL2 = 0;
    public UInt32 CacheL3 = 0;

    // Overloaded constructor method
    // The one with no arguments does nothing to initialise the class
    // The one with the ManagementObject argument will call GetInfo to arrange the held data into the properties above
    public Processor() { }
    public Processor(ManagementObject wmiProcessor)
    {
        this.GetInfo(wmiProcessor);
    }

    // The main information handler for the classes.
    // This splits out the data in the ManagementObject into the class's own properties
    public void GetInfo(ManagementObject wmiProcessor)
    {
        // If anything fails, the try loop will just end without making a fuss
        // Because of the default values, N/A will be displayed everywhere if something fails here.
        try
        {
            this.Architecture = (string)wmiProcessor["Architecture"];
            this.Availability = (string)wmiProcessor["Availability"];
            this.CacheL2 = (UInt32)wmiProcessor["L2CacheSize"];
            this.CacheL3 = (UInt32)wmiProcessor["L3CacheSize"];
        }
        catch (Exception e)
        {

        }
    }
}

Usage example:

public PC Computer = new PC();
Computer.GetAllInfo();
textbox1.Text = Computer.Processor[0].Architecture

In the event a device needs to query more than one WMI class, each additional class can be listed as an extra parameter in the device constructor and GetInfo() method.

他のヒント

You should probably be making these calls lazily, e.g.:

class PC
{
    public IEnumerable<Processor> Processors {get; private set;}

    // Constructor
    public PC()
    {
        Processors = new List<Processor>();
        for (var i = 0; i < GetProcessorCount(); i++)
            Processors.Add(new Processor(i));
    }
}

class Processor
{
    public int ProcessorIndex { get; private set; }

    private String _architecture;
    public string Architecture { 
        get { 
            // Return architecture if it's already been retrieved, 
            // otherwise retrieve it, store it, and return it.
            return _architecture ?? (_architecture == getArchitecture(ProcessorIndex));
        }
    }
    public Processor(int processorIndex) {
        ProcessorIndex = processorIndex;
    }
}

This way you get the semantics you want, e.g.

Console.Out.WriteLine(myPCInstance.Processors[0].Architecture);

and at the same time you are only retrieving that information when it is actually asked for.

why not make a Processor class just have single properties Architecture, Cores etc and take a ManagementObject instance in its constructor? you can then pull the required data from the management object in the constructor of the Processor and just create many Processors in your PC object.

class PC
{
    //I'd encapsulate these in a property rather than a public field
    public Processor[] Processors;
    public Motherboard Motherboard;

    // Constructor
    public PC()
    {

        Motherboard = new Motherboard();
    }

    // Method to get all info sequentially
    public void GetAllInfo()
    {
        ManagementObject[] WMIData = DataRetriever.GetWMIData("Win32_Processor");
        Processors = new Processor[WMIData.Length-1];
        for (int i = 1; i < WMIData.Length; i++)
            {
            Processors[i-1] = new Processor(WMIData[i-1]); //assuming 0 based                
            }

        Motherboard.GetInfo();
    }
}

class Processor
{
    public string Architecture;
    public string Availability;
    public UInt16 Cores;

    public Processor(ManagementObject WMIData)
    {
        this.Architecture = (string)WMIData["Architecture"];
        this.Availability = (string)WMIData["Availability"];
        this.Cores = (UInt16)WMIData["NumberOfCores"];
    }
}

if you are worried about performance then you should hide your public fields behind properties and then make the calls lazily as you need them. Obviously this is a trade off between loading the data when you need it (which might involve delays at the time of access) or preloading all of it, (which might mean a delay at the start but will be quick when you need it). The desired result is down to whether you always needs all the data and how it is going to be used.

you could store the WMIData object in the Processor class and just read the values when you access the properties. It depends on where the slow bits are:

class Processor
{ 
    private ManagementObject WMIData;
    // obviously you might want to cache this value once it has been retrieved once
    public string Architecture{get{return (string)WMIData["Architecture"];}}
    public string Availability {get{return (string)WMIData["Availability"];}}
    public UInt16 Cores{get{return (UInt16)WMIData["NumberOfCores"]}}

    public Processor(ManagementObject WMIData)
    {
        this.WMIData = WMIData;
    }
}

EDIT

if you need more than 1 WMI query, then either pass the results of each WMI call to the object so it can get the data from them (sounds like it'll be slow) or give it just enough data so it can make those calls when it needs to:

class HardDrive
{
   private int index;
   private ManagmentObject physicalMediaInfo;
   private ManagementObject smartDataInfo;

   // choose one of these constructors.  this one lets you delay all the WMI calls till you need to do them 
   public HardDrive(int index)
   {
       this.index=index;
   }

   //this one means you have to make the calls in advance
   public HardDrive(ManagmentObject physicalMediaInfo,ManagementObject smartDataInfo)
   {
       this.physicalMediaInfo=physicalMediaInfo;
       this.smartDataInfo=smartDataInfo;
   }

   private ManagementObject PhysicalMediaInfo
   {
       get
       { 
          if(physicalMediaInfo==null)
          {
              ManagementObject[] WMIData = DataRetriever.GetWMIData("Win32_PhysicalMedia");
              physicalMediaInfo=WMIData[index];
          } 
          return physicalMediaInfo;         
       }
   }

   private ManagementObject SmartDataInfo
   {
       get
       { 
          if(smartDataInfo==null)
          {
              ManagementObject[] WMIData = DataRetriever.GetWMIData("ATAPI_SmartData");
              smartDataInfo=WMIData[index];
          } 
          return smartDataInfo;         
       }
   }

   //property for getting the details of the hard disk
   //uses the private property to ensure that the management object for the  is only loaded when its needed
   public int Sectors{get{return (int)PhysicalMediaInfo["Sectors"]};};

   //Same for the smart data.  
   public int SomeSmartData{get{return (int)SmartDataInfo["SomeSmartData"]};};

}

this approach allows you to only do each api call as a property from it is needed, and ensures that it is only done once regardless of how many properties from it are used. you could combine the constructors and allow the management objects passed in to be null, then you could provide the index which would be used to look up and ManagementObject instances which were not passed in with the constructor, but leave you free to pass in some ManagementObjects which could be preloaded if they were available or cheap...

EDIT 2

As for dealing with a refresh, assuming that refresh needs to ensure that next time data is accessed the latest info from the APi is requested you should be able to simply do this:

 public void Refresh()
 {
     this.physicalMediaInfo=null;
     this.smartDataInfo=null;
 }

This will then mean that next time Sectors is called the "Sectors" value will be re-queried from the WMIData as the existing WMIObject will be replaced.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top