Wednesday, October 31, 2007

WMI Queries on Remote Machines

Windows management instrumentation (WMI) can be used to access system management data across remote machines. You can use this to get status and configuration information on windows machines listening on the network. The classes found in the System.Management namespace helps developers to write code to access these information quickly.

The following example shows how to access LogicalMemoryConfiguration data on a remote machine.

using System;
using System.Net;
using System.Management;

namespace WMIonRemoteMachine
{
    class Program
    {
        static void Main(string[] args)
        {
            //Specify the Adminstrator's Username and Password
            ConnectionOptions co = new ConnectionOptions();
            co.Username = "Administrator";
            co.Password = "password#xyz";

            //Connect to the default namespace on Remote Machine
            ManagementScope scope = new ManagementScope(@"\\[REMOTE MACHINE]\root\cimv2", co);   
       
            SelectQuery query = new SelectQuery("SELECT * FROM Win32_LogicalMemoryConfiguration");

            ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

            foreach (ManagementObject mObj in searcher.Get())
            {
                foreach (System.Management.PropertyData property in mObj.Properties)
                    System.Console.WriteLine(property.Name.PadLeft(25,' ') + ": " + property.Value);
            }            
            Console.ReadLine();
        }
    }
}

OUTPUT 

  AvailableVirtualMemory: 1405028
                          Caption: Logical Memory Configuration
                     Description: Logical Memory Configuration
                             Name: LogicalMemoryConfiguration
                       SettingID: LogicalMemoryConfiguration
       TotalPageFileSpace: 2523064
     TotalPhysicalMemory: 1046512
        TotalVirtualMemory: 3569576

 

The username and password supplied in the above code should belong to an account that is a member of Administrator Group on the remote machine. If the ConnectionOptions is not set then the namespace residing on the Local System is accessed.

The default WMI namespace or schema "\root\cimv2" is queried to retrieve common system management information. WMI implements the Common Information Model (CIV) schema proposed by Distributed Management Task Force (DMTF).

Remote connections in WMI are affected by Firewall. It blocks all data requests from remote machines. If a connection fails, an exception of type System.Runtime.InteropServices.COMException is thrown in System.Management with an error message "The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)". So make sure that the firewall settings are configured to allow these connections.

Monday, September 24, 2007

Data Formatting issues in GridView

You have a Grid bound to a datasource. You do all the right stuff by putting the correct formatting expressions in place for the BoundColumn. But when the page is rendered the columns are not formatted. I'm sure everybody would have encountered this at some point or the other while rendering data in ASP.NET 2.0 GridView.

The fix is straight, set the HtmlEncode property of the Bound Columns to "false". 

<asp:BoundField DataField="Number" DataFormatString="{0:c}"  HtmlEncode="false" HeaderText="Number" />
or
<asp:BoundField DataField="Date" DataFormatString="{0:MM/dd/yyyy}" HtmlEncode="false" HeaderText="Date" /> 

By default this property is set to True for security reasons. When the page is rendered, the output HTML is encoded to prevent cross site scripting (XSS) attacks. So make sure to turn off HtmlEncoding on those columns that you want to display formatted data.

Tuesday, May 22, 2007

Viewing HTML source in AJAX-enabled web pages

Sometimes we want to view the HTML rendered by the web server for debugging purposes. We right-click on the browser (IE), select "View Source" and a neat little notepad opens up with the HTML. This is valid as long as the pages are rendered in a conventional way where the entire page is posted back for each subsequent requests.

In AJAX, where the pages are rendered asynchronously, "View Source" would only show you the HTML for the page that was originally rendered but does not show you any updates to that page modified through AJAX callbacks. 

Enter the following javascript in the address bar to view the outer HTML actually generated through AJAX Callbacks.

javascript:'<xmp>'+window.document.body.outerHTML+'</xmp>'

Sunday, March 18, 2007

.NET Articles

Here is a list of articles that I've published elsewhere. This post will be updated frequently... 

ASP.NET

GDI+

Tuesday, March 6, 2007

Simple Image-To-ASCII Converter using C#

If you're a .NET lover and moreover worked with GDI+ then you would definitely want to mess around with images. I've written a short article, Generating ASCII Art from an Image using C#, and published it on c-sharpcorner with complete source code. This is a quick and simple Image-To-ASCII generator with zoom-in/zoom-out feature that converts JPEGs and Bitmaps of any resolution to cool and fascinating ASCII arts. Have fun playing with the code!

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.

Tuesday, January 30, 2007

Split and Merge files in C#

In this feed I'll show you how to Split a file into user-specified chunks and eventually merge them all together. You will find this very helpful if you have very large text files, greater than a GB,  that cannot be viewed in your "lousy Notepad". These large text files could be one of the crucial log files from your enterprise applications that may accrue data, if left un-attended, over time.

The code example I have shown below is generalized to split any file irrespective of their format.

private void btnSplit_Click(object sender, EventArgs e)
{
    string inputFile = txtInputFile.Text; // Substitute this with your Input File 
    FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
    int numberOfFiles = Convert.ToInt32(txtChunks.Text);
    int sizeOfEachFile = (int)Math.Ceiling((double)fs.Length / numberOfFiles);

    for (int i = 1; i <= numberOfFiles; i++)
    {
        string baseFileName = Path.GetFileNameWithoutExtension(inputFile);
        string extension = Path.GetExtension(inputFile);
        FileStream outputFile = new FileStream(Path.GetDirectoryName(inputFile) + "\\" + baseFileName + "." + i.ToString().PadLeft(5, Convert.ToChar("0")) + extension + ".tmp", FileMode.Create, FileAccess.Write);
        int bytesRead = 0;
        byte[] buffer = new byte[sizeOfEachFile];

        if ((bytesRead = fs.Read(buffer, 0, sizeOfEachFile)) > 0)
        {
            outputFile.Write(buffer, 0, bytesRead);
        }
        outputFile.Close();
    }
    fs.Close();
}
private void btnMerge_Click(object sender, EventArgs e)
{
    string outPath = txtInputFolder.Text; // Substitute this with your Input Folder 
    string[] tmpFiles = Directory.GetFiles(outPath, "*.tmp");
    FileStream outputFile = null;
    string prevFileName = "";

    foreach (string tempFile in tmpFiles)
    {

        string fileName = Path.GetFileNameWithoutExtension(tempFile);
        string baseFileName = fileName.Substring(0, fileName.IndexOf(Convert.ToChar(".")));
        string extension = Path.GetExtension(fileName);

        if (!prevFileName.Equals(baseFileName))
        {
            if (outputFile != null)
            {
                outputFile.Flush();
                outputFile.Close();
            }
            outputFile = new FileStream(outPath + baseFileName + extension, FileMode.OpenOrCreate, FileAccess.Write);
        }
        
        int bytesRead = 0;
        byte[] buffer = new byte[1024];
        FileStream inputTempFile = new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.Read);

        while ((bytesRead = inputTempFile.Read(buffer, 0, 1024)) > 0)
            outputFile.Write(buffer, 0, bytesRead);

        inputTempFile.Close();
        File.Delete(tempFile);
        prevFileName = baseFileName;
    }
    outputFile.Close();
}
 
The split method is straightforward, you set the count of number of files to be split, and the size of each file is allocated equally. Each file is named after its parent, numbered and tailed with an extension of ".tmp". If you're splitting a Text file with no intention of merging them at a later time, you can replace the ".tmp" extension with ".txt".

The Merge method above, is in fact a "Merge All" method. It merges all the files with extensions ".tmp" in the specified directory and re-creates the parent file back. That's the reason why I'm retaining the original fileName and their extensions.

The Directory.GetFiles() method returns an array of all the file paths from the directory in ascending order. If you have fileNames like Testfile1.txt, Testfile2.txt, .... testfile100.txt then their order in the string array would be Testfile1.txt, Testfile10.txt, Testfile100.txt, Testfile2.txt, Testfile20.txt, Testfile3.txt,.... as the numbers are just strings. The Merge would fail eventually because of this merge-order. This can be addressed if you LeftPad the number with 0's, preferably a padding of 5 characters, while splitting. The fileNames now would be something like these, Testfile00001.txt, Testfile00002.txt...Testfile00100.txt.

All the "tmp" files are deleted from the folder after they are merged.