Friday, February 23, 2007

[How to] retrieve System Management information with WMI Queries

Windows Management Instrumentation (WMI) is the base for management data and operations on Windows Operating system. And System.Management namespace is the WMI namespace in .NET framework. The ManagementObjectSearcher class is one of the first level class objects contained within this namespace. This class can be used to retrieve a collection of ManagementObject based on a specified Win32 query. For example, it can be used to enumerate all Disk Drives, Disk Partitions, Network Adapters, Network Connections, Processes etc.

To enumerate all the disk drives and their associated properties we first instantiate a new ManagementObjectSearcher object which takes as input a WMI query. The Get() method on this object returns a collection of management objects that match this query.  

string mosQuery = "SELECT * FROM Win32_DiskDrive";

System.Management.ManagementObjectSearcher query = new System.Management.ManagementObjectSearcher(mosQuery);
foreach (System.Management.ManagementObject mObj in query.Get())
{
    foreach (System.Management.PropertyData property in mObj.Properties)
    {
        System.Console.WriteLine(property.Name + "__::" + property.Value);        
    }
}   

Sample Output (Just one object in the Collection)

Availability__::
BytesPerSector__::512
Capabilities__::System.UInt16[]
CapabilityDescriptions__::
Caption__::FUJITSU MHV2060BH
CompressionMethod__::
ConfigManagerErrorCode__::0
ConfigManagerUserConfig__::False
CreationClassName__::Win32_DiskDrive
DefaultBlockSize__::
Description__::Disk drive
DeviceID__::\\.\PHYSICALDRIVE0
ErrorCleared__::
ErrorDescription__::
ErrorMethodology__::
Index__::0
InstallDate__::
InterfaceType__::IDE
LastErrorCode__::
Manufacturer__::(Standard disk drives)
MaxBlockSize__::
MaxMediaSize__::
MediaLoaded__::True
MediaType__::Fixed hard disk media
MinBlockSize__::
Model__::FUJITSU MHV2060BH
Name__::\\.\PHYSICALDRIVE0
NeedsCleaning__::
NumberOfMediaSupported__::
Partitions__::1
PNPDeviceID__::IDE\DISKFUJITSU_MHV2060BH_______________________0085002A\5&1F698B3F&0&0.0.0
PowerManagementCapabilities__::
PowerManagementSupported__::
SCSIBus__::0
SCSILogicalUnit__::0
SCSIPort__::0
SCSITargetId__::0
SectorsPerTrack__::63
Signature__::4026531840
Size__::60011642880
Status__::OK
StatusInfo__::
SystemCreationClassName__::Win32_ComputerSystem
SystemName__::UOPONLG8BFLB1
TotalCylinders__::7296
TotalHeads__::255
TotalSectors__::117210240
TotalTracks__::1860480
TracksPerCylinder__::255

 

Or, you can also use this query,"SELECT * FROM Win32_Service WHERE Started = False", to enumerate a list of services which are not started.

The Win32_DiskDrive/Win32_Service is a WMI object that is being queried here. You can replace the above query with one of those that are listed below. I've compiled a list of management queries by inspecting the objects using WMI object browser. You can play with them or implement the same in your applications as per your requirements...

//mosQuery = "SELECT * FROM Win32_Account";
//mosQuery = "SELECT * FROM Win32_BIOS";
//mosQuery = "SELECT * FROM Win32_BootConfiguration";
//mosQuery = "SELECT * FROM Win32_Bus";
//mosQuery = "SELECT * FROM Win32_CacheMemory";
//mosQuery = "SELECT * FROM Win32_CDROMDrive";
//mosQuery = "SELECT * FROM Win32_ComputerSystem";
//mosQuery = "SELECT * FROM Win32_DesktopMonitor";
//mosQuery = "SELECT * FROM Win32_DeviceMemoryAddress";
//mosQuery = "SELECT * FROM Win32_DiskDrive";
//mosQuery = "SELECT * FROM Win32_DiskPartition";
//mosQuery = "SELECT * FROM Win32_DMAChannel";
//mosQuery = "SELECT * FROM Win32_Environment";
//mosQuery = "SELECT * FROM Win32_Fan";
//mosQuery = "SELECT * FROM Win32_IDEController";
//mosQuery = "SELECT * FROM Win32_IRQResource";
//mosQuery = "SELECT * FROM Win32_Keyboard";
//mosQuery = "SELECT * FROM Win32_LoadOrderGroup";
//mosQuery = "SELECT * FROM Win32_LogicalDisk";
//mosQuery = "SELECT * FROM Win32_LogicalMemoryConfiguration";
//mosQuery = "SELECT * FROM Win32_LogicalProgramGroup";
//mosQuery = "SELECT * FROM Win32_MemoryArray";
//mosQuery = "SELECT * FROM Win32_MemoryDevice";
//mosQuery = "SELECT * FROM Win32_MotherBoardDevice";
//mosQuery = "SELECT * FROM Win32_NetworkAdapter";
//mosQuery = "SELECT * FROM Win32_NetworkConnections";
//mosQuery = "SELECT * FROM Win32_NTEventLogFile";
//mosQuery = "SELECT * FROM Win32_NTLogEvent";
//mosQuery = "SELECT * FROM Win32_OperatingSystem";
//mosQuery = "SELECT * FROM Win32_PCMCIAController";
//mosQuery = "SELECT * FROM Win32_PnPEntity";
//mosQuery = "SELECT * FROM Win32_PointingDevice";
//mosQuery = "SELECT * FROM Win32_PortableBattery";
//mosQuery = "SELECT * FROM Win32_PortResource";
//mosQuery = "SELECT * FROM Win32_POTSModem";
//mosQuery = "SELECT * FROM Win32_Printer";
//mosQuery = "SELECT * FROM Win32_Process";
//mosQuery = "SELECT * FROM Win32_Processor";
//mosQuery = "SELECT * FROM Win32_SCSIController";
//mosQuery = "SELECT * FROM Win32_SerialPort";
//mosQuery = "SELECT * FROM Win32_Service";
//mosQuery = "SELECT * FROM Win32_share";
//mosQuery = "SELECT * FROM Win32_SoundDevice";
//mosQuery = "SELECT * FROM Win32_SystemDriver";
//mosQuery = "SELECT * FROM Win32_SystemUsers";
//mosQuery = "SELECT * FROM Win32_TemperatureProbe";
//mosQuery = "SELECT * FROM Win32_TimeZone";
//mosQuery = "SELECT * FROM Win32_USBController";
//mosQuery = "SELECT * FROM Win32_USBHub";
//mosQuery = "SELECT * FROM Win32_UserAccount";
//mosQuery = "SELECT * FROM Win32_VideoController";        

Happy Coding !!!

Thursday, February 15, 2007

Creating splash screens with fading effects in Windows Forms

All those cool splash screens that you see during application-startup can be created using Windows Forms in .NET 2.0/1.1 easily. Read my previous post on creating visually attractive custom shaped forms in .NET. You can enhance these forms with fading effects that could be "cool" and appealing.

The trick is with the Form's Opacity property which governs its overall transparency levels. A double value of 1.00, which is its default, sets the Form to be Opaque. Whereas, any value less than that would make it transparent. In mathematical terms, a form is 60% opaque when its opacity value is 0.60. And it is completely invisible when the value is 0.00.

Fading Effect

The code below does the trick of fading-out. Insert this code in the InitializeComponent method or in a button-click event wherever you would want a "fading-effect" to be fired.

for (int i = 100; i >= 0; i -= 10)
{
    this.Opacity = (double)i / 100;
    Application.DoEvents();
    System.Threading.Thread.Sleep(100);
}

As you can see, between each levels of transparency, I'm specifying a delay of 100 milliseconds. This would give the real fading effect to a form. The Application.DoEvents() method forces a repaint without blocking.

Monday, February 12, 2007

Creating custom WinForms in .NET 2.0. Yes, Forms with more attractive shapes...

Have you ever wanted to create an application with a GUI that looked just like one of those skins in Windows Media Player 10 ? In .NET you don't have to write a single line of code to generate one such form. Earlier, creating a non-rectangular form was the toughest part which involved complex low-level coding by trapping various Form-handles and calling so many system API's.

I'll show you how to create a Form just like the one as shown in the figure.

Start by creating a transparent Bitmap image using MS Paint or other Image editors.

Add this image to the Form by setting its BackgroundImage property.

Set the BackgroundImageLayout to None.

Set the FormBorderStyle to None.

Set the TransparencyKey to White. This color will appear transparent on the Form.

Thats it, run your application to find a more attractive window. But hold on, its not over yet. This form has one serious limitation. You can't drag and move this form around because you've alread set the FormBorderStyle property to None and the borders & title bar are missing. The title bar functionalities need to be explicitly added to the code to handle all the basic functions like Close, Minimize, Maximize and Move.

Adding a Close Handler

Drag and drop a Button control from the ToolBox over the form. Resize and rename this as btnClose with its Text property 'X'. Set the control's BackColor property to the one that blends with your Form's color. Double click on it and add the following code to the Form's Close EventHandler.

private void btnClose_Click(object sender, EventArgs e)

{
    this.Close();         
}

Adding Drag/Move functionality

When the left-button of a mouse is clicked on the form, we first capture the thickness, in pixels, of the border for the window. We then create a point that is relative to the Form's Border and the mouse's current position. This Point becomes the new reference that is available globally.

private Point _OffsetPoint;

private void CustomForm_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)

{
    if (e.Button == MouseButtons.Left)
    {
        int formBorderWidth = SystemInformation.FrameBorderSize.Width;
        int formBorderHeight = SystemInformation.FrameBorderSize.Height;
        _OffsetPoint = new Point(-(e.X + formBorderWidth), -(e.Y + formBorderHeight));      
    }
}

The SystemInformation Class can be used to get information about the current system environment like Windows display element sizes, OS settings and other Hardware installed on the machine. The FrameBorderSize property gives the thickness of the border for the window.

When a window is dragged, the new position of the mouse determines the Form's new location. This is achieved by the Point Class' Offset Method. Setting the form's Location property with this new point will translate the form to this point.

private void CustomForm_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {             
        Point mousePos = Control.MousePosition;
        mousePos.Offset(_OffsetPoint.X, _OffsetPoint.Y);
        Location = mousePos;
    }
}

This is just slice of what you can do with Visual Studio.NET and the .NET framework. Have fun with it !!

Friday, February 9, 2007

Search and Replace Texts in all Files and Subfolders using C#

Recently I needed to replace a string in whole bunch of HTML template files that I had been working on. I found a couple of text editors which attempted to do this only after loading the entire lot of files in the memory. When the number of files were in hundreds they failed miserably. I also tried to load the files into a project in Visual Studio 2005 IDE and use its Find & Replace in Current Project feature. This failed to find texts with line breaks eventually forcing me to drop this idea. After spending an hour of intense googling to find the right tool, I decided to make my own Search & Replace application in C# and I did make it in 15mins. This application can search and replace texts in all files and subfolders filtered by their extensions. 

Instead of using the string.Replace() method provided by the string object, I wrote a custom method which finds and replace strings  in a loop. This gives me more flexibility to keep track of the number of replacements as well as the files that were actually affected. The Directory.GetFiles() method returns the complete paths of all files (after applying the filter) in the specified folder and all its subfolders.

Here is The code....

private void btnReplace_Click(object sender, EventArgs e)
{
    //Enter some text that you want to search and replace
    string find = txtFind.Text; 
    int replaced = 0;

    //Get all the files from the root directory filtered by a filter text.
    string[] fileList = Directory.GetFiles(@"C:\Documents and Settings\tganesan\Desktop\FindnReplace", "*.txt", SearchOption.AllDirectories);

    //Loop through each file, call the ReplaceText() method
    //and replace the file if something was replaced.
    foreach (string file in fileList)
    {
       StreamReader sr = new StreamReader(file);
       string content = sr.ReadToEnd();
       sr.Close();
       
       if(ReplaceText(ref content, txtFind.Text, txtReplace.Text, ref replaced))
       {
           StreamWriter sw = new StreamWriter(file);
           sw.Write(content);
           sw.Flush();
           sw.Close();
           //TODO: Add the files to a collection that were affected.
       }               
    }
    MessageBox.Show("Total replacements = " + replaced);
}


/// <summary>
/// This method loops through the File content 
/// and replaces the text if found.
/// </summary>
/// <param name="content"></param>
/// <param name="oldValue"></param>
/// <param name="newValue"></param>
/// <param name="replaced"></param>
/// <returns></returns>
private bool ReplaceText(ref string content,string oldValue, string newValue, ref int replaced)
{
    Boolean isReplaced = false;
    int startIndex = 0;         

    while (startIndex != -1)
    {
        startIndex = content.IndexOf(oldValue,startIndex);
        if (startIndex != -1)
        {
            content = content.Remove(startIndex, oldValue.Length);
            content = content.Insert(startIndex,newValue);                    
            replaced += 1;
            isReplaced = true;
        }
    }
    return isReplaced;
}
Don't forget to take a backup of files before running this code.

Monday, February 5, 2007

System.Data.EvaluateException: Cannot perform '=' operation on System.Int16 and System.String

This exception was thrown while filtering out rows from a DataTable with an incorrect filter expression. See the sample code shown below...

DataTable dt = GetDataTable();        
DataRow[] drs = dt.Select("ID='" +param + "'");
private DataTable GetDataTable()
{
    DataTable dt = new DataTable();
    dt.Columns.Add(new DataColumn("ID", typeof(Int16)));
    DataRow dr;
    for (int i = 0; i <= 100; i++)
    {
        dr = dt.NewRow();
        dr["ID"] = i;
        dt.Rows.Add(dr);            
    }
    return dt;
}

The GetDataTable() method just returns a sample DataTable with one column of Type Int16. The param in the filter expression is of string type. The expression works fine as long as the param string is a number, like 0, 1, 2... since ID is an Int16 column. The expression can be framed either as "ID = '2'" or "ID = 2". When param is an empty string or null, the above exception is thrown as these types of strings cannot be converted into a type that is equivalent to the comparing column (Int16).

So the next time when you use a filter expression to filter out rows from a DataTable ensure that you use the right Data Types.