Tuesday, October 14, 2008

Binary & XML Serialization

Serialization is a method for implementing object persistence. It is the process of saving an object onto a storage medium or to transmit it across a network connection link in binary form. Process of serializing an object is also called deflating or marshalling an object. Similarly, deserializing data is referred to as inflating or unmarshalling.

Binary and XML serialization is very popular in the enterprise environment. The BinaryFormatter class is used for binary serialization (Namespace:  System.Runtime.Serialization.Formatters.Binary). Data can be written to a file and it can be retrieved easily. Code snippet for serialization & deserialization is given below.

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

private void Serialize(object data, string fileName)
{
    try
    {
        // Create the stream for writing data
        using (FileStream stream = new FileStream(fileName, FileMode.Create))
        {
            BinaryFormatter formatter = new BinaryFormatter();

            // Serialize the object
            formatter.Serialize(stream, data);

            // Cleanup
            stream.Close();
        }
    }
    catch
    {
        throw;
    }
}

The function accepts an object and it will be serialized to a file. One point is; to serialize a class, it should be marked with the Serializable attribute, as shown:

[Serializable()]
public class Employee
{
    // Code...
}

To deserialize the data, below given function can be used.

private object Deserialize(string fileName)
{
    object data = null;

    try
    {
        // Open the stream for reading serialized data
        using (FileStream stream = new FileStream(fileName,
                                                FileMode.Open,
                                                FileAccess.Read))
        {
            BinaryFormatter formatter = new BinaryFormatter();

            // Deserialize object
            data = formatter.Deserialize(stream);
        }
    }
    catch
    {
        throw;
    }

    return data;
}

The returned object can be converted to the original data (type-case operation).

In case of XML serialization, objects are serialized and deserialized into and from XML documents. There are several advantages for XML serialization - storing user preferences, maintaining security information across pages and applications, XML manipulation without using DOM, passing objects between application or domains,passing objects through firewall as XML string etc.

For XML serialization, we can create classes annotated with attributes or by using the XML Schema Definition Tool (Xsd.exe) to generate classes based on an existing XSD document.

Method 1 - Classes annotated with attributes

In this method, class members are annotated with certain attributes for serialization. Attributes like XmlRootAttribute, XmlAttributeAttribute, XmlElementAttribute etc. are applied at required levels. These attributes are available in the System.Xml.Serialization namespace. An XmlSerializer object is used for serialization & deserialization. Check the below link for an example:

http://giri.tp.googlepages.com/XMLSerializationDemo.zip

Method 2 - Classes generated using Xsd.exe (from existing XSD documents)

The xsd command (available from the Visual Studio Command Prompt) can be used for the generation of classes from XSD files. For example, save the below given XSD with name books.xsd.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified">
<xsd:element name="bookstore" type="bookstoreType"/>
<xsd:complexType name="bookstoreType">
  <xsd:sequence maxOccurs="unbounded">
   <xsd:element name="book"  type="bookType"/>
  </xsd:sequence>
</xsd:complexType>
<xsd:complexType name="bookType">
  <xsd:sequence>
   <xsd:element name="title" type="xsd:string"/>
   <xsd:element name="author" type="authorName"/>
   <xsd:element name="price"  type="xsd:decimal"/>
  </xsd:sequence>
  <xsd:attribute name="genre" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="authorName">
  <xsd:sequence>
   <xsd:element name="first-name"  type="xsd:string"/>
   <xsd:element name="last-name" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>
</xsd:schema>

Now, from the Visual Studio command line, execute the following command to generate the C# class for this XSD file.

xsd books.xsd /c /l:cs

This will create a file books.cs in the current directory and it can be added directly to the C# project. The generated file will have all the necessary attributes for XML serialization.

Code snippets for converting an object to XML string and to reconstruct the original object from the XML is given below. This method is very useful in situations like passing objects across a network, through Firewall.

First, we need to define the class which is to be serialized/deserialized. Example:

public class Employee
{
    private string empID;

    public string EmpID
    {
        get { return empID; }
        get { empID = value; }
    }

    private string empName;

    public string EmpName
    {
        get { return empName; }
        set { empName = value; }
    }
}

Now, we can use the below functions for serializing/deserializing an Employee object.

using System.IO;
using System.Xml;
using System.Xml.Serialization;

public string Serialize(object data)
{
    string xml = string.Empty;

    try
    {
        using (MemoryStream stream = new MemoryStream())
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Employee));

            UTF8Encoding encoding = new UTF8Encoding(false);

            // Text writer for writing XML data
            using (XmlTextWriter writer = new XmlTextWriter(stream, encoding))
            {
                // Serialize data
                serializer.Serialize(writer, data);

                using (MemoryStream tmpStream = (MemoryStream)writer.BaseStream)
                { 
                    // Get XML string from memory
                    xml = encoding.GetString(tmpStream.ToArray());
                }
            }
        }
    }
    catch
    {
        throw;
    }

    return xml;
}

public object Deserialize(string xml)
{
    object data = null;

    try
    {
        UTF8Encoding encoding = new UTF8Encoding(false);

        // Initialize memory stream using byte array
        using (MemoryStream stream = new MemoryStream(encoding.GetBytes(xml)))
        {
            // Text writer for writing XML data
            using (XmlTextWriter writer = new XmlTextWriter(stream, encoding))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Employee));

                // Deserialize object
                data = serializer.Deserialize(stream);
            }
        }
    }
    catch
    {
        throw;
    }

    return data;
}

Getting User/Group details in Windows

In one of the applications that I worked on recently, I had to determine whether the given username (loaded from XML) is actually a local username. First I felt its bit tricky. But instinct told me to look into WMI and I got the solution quickly.

The idea is to use ManagementObjectSearcher objects for searching the required WMI class. For a list of local users, we need to search for Win32_UserAccount. Win32_Group represents groups defined in the local system.

First, we need to add a reference to the System.Management component. Code snippets given below shows how to get the user & group listing:

using System.Management;

public string[] GetUsers(string machineName)
{
    return GetItems("Win32_UserAccount", machineName);
}

public string[] GetGroups(string machineName)
{
    return GetItems("Win32_Group", machineName);
}

private string[] GetItems(string className, string machineName)
{
    string[] items = null;

    try
    {
        // Prepare the select query
        SelectQuery query = new SelectQuery(className,
            string.Format("Domain='{0}'", machineName));

        using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
        {
            StringBuilder builder = new StringBuilder();

            foreach (ManagementObject mgmtObject in searcher.Get())
            {
                if (builder.Length != 0)
                    builder.Append('\t');

                builder.Append(mgmtObject["Name"]);
            }

            // Get the array
            items = builder.ToString().Split('\t');
        }
    }
    catch
    {
        throw;
    }

    return items;
}

The actual processing is done by GetItems(). Machine name can be obtained by using the System.Environment.MachineName property.

Now we can extend this to get all users in a specific group. The Win32_GroupUser class can be used for obtaining this information. If the computer is in a workgroup, we can pass the computer name (Environment.MachineName) to domainName parameter.

public string[] GetUsersInGroup(string domainName, string groupName)
{
    string[] users = null;

    try
    {
        string queryString = string.Format("GroupComponent=\"Win32_Group.Domain=\'{0}\',Name=\'{1}'\"",
                                                        domainName,
                                                        groupName);

        // Prepare the select query
        SelectQuery query = new SelectQuery("Win32_GroupUser", queryString);

        using (ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher(query))
        {
            StringBuilder builder = new StringBuilder();

            foreach (ManagementObject mObject in objectSearcher.Get())
            {
                ManagementPath path = new ManagementPath(mObject["PartComponent"].ToString());
                if (path.ClassName == "Win32_UserAccount")
                {
                    // Split the path (2 parts)
                    string[] names = path.RelativePath.Split(',');

                    if (builder.Length != 0)
                        builder.Append('\t');

                    // Extract the 'Name' part
                    builder.Append(names[1].Substring(names[1].IndexOf("=") + 1).Replace('"', ' ').Trim());
                }
            }

            // Get the array
            users = builder.ToString().Split('\t');
        }
    }
    catch
    {
        throw;
    }

    return users;
}

If you face any issue - like getting incorrect data - have a look at the below link for troubleshooting tips.

http://support.microsoft.com/kb/940527

Remote Program Execution (using C# - WMI)

There are different methods for remote application execution. Using a client/server mechanism which sends commands to each other is a simple approach. Another one is by using .NET Remoting. One another method is by using the psexec utility, written by the great Dr. Mark Russinovich. (Personal comment: I admire his works and I believe that he is the ideal role model for all Windows programmers).

We will talk about yet another method here - using WMI. The Win32_Process class (WMI) can be used for executing processes in a remote machine. Code snippet is given below:

using System.Management;

private void RemoteExecute(string userName,
                                string password,
                                string path,
                                object[] commandLine)
{
    ConnectionOptions options = new ConnectionOptions();

    options.Impersonation = ImpersonationLevel.Impersonate;
    options.Authentication = AuthenticationLevel.Default;
    options.Username = userName;
    options.Password = password;
    options.Authority = null;
    options.EnablePrivileges = true;

    // Note: The ConnectionOptions object is not necessary
    // if we are connecting to local machine & the account has privileges
    ManagementScope scope = new ManagementScope(path, options);
    scope.Connect();

    // Create the process
    using (ManagementClass process = new ManagementClass("Win32_Process"))
    {
        process.Scope = scope;
        process.InvokeMethod("Create", commandLine);
    }
}

This code can be invoked as shown below:

object[] commandLine = { "cmd.exe", null, null, 0 };
RemoteExecute("username",
            "password",
            @"\\192.168.100.12\root\cimv2",
            commandLine);

Couple of important points to be noted: First, the launched application will not show any interface. It will be in hidden state. Also, it will not be possible to make it interactive.

Finally, WMI applications will not work if the remote machine is not configured properly. Check the below link for information about adding remote administration exception in Windows Firewall and other troubleshooting tips.

http://techblog-giri-csharp.blogspot.com/2008/10/using-wmi-with-c.html

Friday, October 10, 2008

Using WMI with C#

Windows Management Instrumentation (WMI) is the infrastructure for data management and operations on Windows-based operating systems. It is a set of extensions to the Windows Driver Model that provides an operating system interface through which instrumented components provide information and notification. WMI is Microsoft's implementation of the Web-Based Enterprise Management (WBEM) and Common Information Model (CIM) standards. It is pre-installed in Windows 2000 and newer OSs.

We can write WMI scripts or applications to automate administrative tasks on remote computers but WMI also supplies management data to other parts of the operating system and products. WMI allows scripting languages like VBScript or Windows PowerShell to manage Microsoft Windows personal computers and servers, both locally and remotely.

WMI has its on query language - Windows Query Language, or WQL. WQL allows developers to query WMI providers using a SQL-like syntax. If you know the provider classes and the fields available, then you can get the info very easily.  For instance, if you want to get a list of logical drives from a system you would use the following query:

SELECT * FROM Win32_LogicalDisk

You can, however, refine the search by using where clauses and getting specific "fields" in the query. The following query gets the amount of freespace, the size, and the name of all fixed disk drives:

SELECT FreeSpace,Size,Name FROM Win32_LogicalDisk WHERE DriveType=3

.NET provides very good support to WMI, through the System.Management namespace. It has a number of classes that can be used for accessing WMI services.

ConnectionOptions
ManagementScope
ObjectQuery
ManagementObjectSearcher
ManagementObjectCollection
ManagementObject

Code snippet for collecting drive details (using the previous query) is given below. First of all, we need to add a reference to the System.Management .NET component.

using System.Management;

private void DiskStatisticsUsingWMI()
{
    // Connection credentials for the remote computer
    ConnectionOptions conn = new ConnectionOptions();

    conn.Username = "username";
    conn.Password = "password";

    // The machine to be connected (the root namespace)
    ManagementScope ms = new ManagementScope(@"\\192.168.100.12\root\cimv2", conn);

   // Get Fixed disk stats
    ObjectQuery query = new ObjectQuery("SELECT FreeSpace,Size,Name FROM Win32_LogicalDisk WHERE DriveType=3");

    // Execute the query 
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(ms, query);

    // Get results
    ManagementObjectCollection objectCollection = searcher.Get();

    // Loop through found drives and write out info
    foreach (ManagementObject mgmtObject in objectCollection)
    {
        Console.WriteLine("Name : " + mgmtObject["Name"].ToString());
        Console.WriteLine("FreeSpace: " + mgmtObject["FreeSpace"].ToString());
        Console.WriteLine("Size: " + mgmtObject["Size"].ToString());
    }
}

An important point: if Windows firewall is enabled in the remote system, we need to define an exception for WMI to access that machine from outside. Otherwise, the firewall will block calls to the system. In the machine to be connected, we need to define a policy for allowing remote administration. To do this, launch gpedit.msc from the Start -> Run and move to Computer Configuration -> Administrative Templates -> Network -> Network Connections -> Windows Firewall. Expand Domain Profile is the computer is in a domain, otherwise expand Standard Profile (the machine is in a workgroup). Double click on Windows Firewall: Allow remote administration exception and select Enabled to enable the exception. Check the screenshot given below.

WMI_Policy

Now this machine is ready to accept WMI queries. This should allow the calling application to work. Check the below links, for more troubleshooting details.

http://msdn.microsoft.com/en-us/library/aa389286.aspx
http://support.microsoft.com/kb/875605

XML - Read/Write binary data (Base64)

XML is an industry standard for data transfer. Usually normal text is stored in XML, but it is possible to store any type of data in an XML file - songs, pictures, documents, executable files etc. Base64 encoding is used for doing this.

Functions listed below can be used for storing binary data in XML and retrieving it. In this example, the filename is also stored in the XML while saving, so that it can be used for retrieval (not mandatory; just application logic).

First, define a constant for the buffer size to be used.

private const int BUFFER_SIZE = 1024;

Code for writing binary data:

using System.IO;
using System.Xml;

private void WriteBase64Data(string sourceFile, string targetFile)
{
    try
    {
        byte[] data;

        using (FileStream fStream = new FileStream(sourceFile,
                                                                FileMode.Open,
                                                                FileAccess.Read))
        {
            // Read data and store it in the buffer
            using (BinaryReader reader = new BinaryReader(fStream))
            {
                data = reader.ReadBytes((int)fStream.Length);
                reader.Close();
            }
            fStream.Close();
        }

        using (MemoryStream memStream = new MemoryStream())
        {
            using (StreamReader reader = new StreamReader(memStream))
            {
                using (XmlTextWriter writer = new XmlTextWriter(memStream,
                                                                                System.Text.Encoding.UTF8))
                {
                    writer.WriteStartElement("BinaryData");

                    // Write the filename
                    writer.WriteStartElement("FileName");
                    writer.WriteValue(sourceFile.Substring(sourceFile.LastIndexOf('\\') + 1));
                    writer.WriteEndElement();

                    // Write actual data
                    writer.WriteStartElement("Data");
                    writer.WriteBase64(data, 0, data.Length);
                    writer.WriteEndElement();

                    writer.WriteEndElement();

                    writer.Flush();
                    memStream.Position = 0;

                    StringBuilder xmlData = new StringBuilder();
                    xmlData.Append(reader.ReadToEnd());

                    XmlDocument xDoc = new XmlDocument();
                    xDoc.LoadXml(xmlData.ToString());

                    // Save to disk
                    xDoc.Save(targetFile);

                    memStream.Close();
                }
            }
        }
    }
    catch
    {
        throw;
    }
}

Code for reading binary data from XML:

using System.IO;
using System.Xml;

private void ReadBase64Data(string sourceFile, string targetPath)
{
    try
    {
        XmlDocument xDoc = new XmlDocument();
        xDoc.Load(sourceFile);

        XmlNode fileName = xDoc.SelectSingleNode("BinaryData/FileName");
        XmlNode data = xDoc.SelectSingleNode("BinaryData/Data");

        // Set output directory
        string targetFile = targetPath;
        if (!targetFile.EndsWith(Path.DirectorySeparatorChar.ToString()))
            targetFile += Path.DirectorySeparatorChar;
        targetFile += fileName.InnerText;

        using (FileStream target = new FileStream(targetFile,
                                                                FileMode.Create,
                                                                FileAccess.ReadWrite,
                                                                FileShare.ReadWrite))
        {
            int bytesRead = 0;
            byte[] buffer = new byte[BUFFER_SIZE];

            using (BinaryWriter writer = new BinaryWriter(target))
            {
                StringReader sr = new StringReader(data.OuterXml);

                using (XmlTextReader reader = new XmlTextReader(sr))
                {
                    reader.MoveToContent();

                    // Read raw data and write to output stream
                    do
                    {
                        bytesRead = reader.ReadBase64(buffer, 0, BUFFER_SIZE);
                        writer.Write(buffer, 0, bytesRead);
                    }
                    while (bytesRead >= BUFFER_SIZE);
                }

                writer.Flush();
            }
        }
    }
    catch
    {
        throw;
    }
}

Output Redirection in C#

Sometimes, it will be necessary to redirect the output of a command to a variable or a control. This is very useful when we are working with DOS commands, which display data in the console that can be used by the application. For example, if you want to process the output of a command such as TASKLIST or some other third party command which lists useful information (which does not have any programming interface), this technique is useful.

What we need to do is to redirect the input, output and error streams to our stream variables and use them for putting data and reading results. Below code snippet demonstrates this concept:

using System.IO;
using System.Diagnostics;

private void RedirectOutput(string command,
                            out string outputMessage,
                            out string errorMessage)
{
    using (Process proc = new Process())
    {
        ProcessStartInfo psI = new ProcessStartInfo("CMD.EXE");

        psI.UseShellExecute = false;

        // Choose the streams to be redirected
        psI.RedirectStandardInput = true;
        psI.RedirectStandardOutput = true;
        psI.RedirectStandardError = true;

        // Do not create a new window
        psI.CreateNoWindow = true;

        proc.StartInfo = psI;
        proc.Start();

        // Create writer/reader objects for redirection
        using (StreamWriter writer = proc.StandardInput)
        {
            using (StreamReader reader = proc.StandardOutput)
            {
                using (StreamReader errorReader = proc.StandardError)
                {
                    writer.AutoFlush = true;

                    // Write the command
                    writer.WriteLine(command);
                    writer.Close();

                    // Get output & error messages
                    outputMessage = reader.ReadToEnd();
                    errorMessage = errorReader.ReadToEnd();
                }
            }
        }
    }
}

By following this method, it is very easy to develop applications with functionality similar to TELNET. The above code can be modified easily to make the session interactive.

IP Address Change Notification

Recently I worked on a project, where I had to do some processing (reconnecting logic) whenever the IP address of the machine is changed. First, I felt that its going to be decently complex. But after a bit of R & D work, I came to know that it is surprisingly easy to do this with .NET! (such is the power of .NET Framework classes).

All I had to do was to register an event listener for the NetworkAddressChanged event (System.Net namespace). I did it on the application startup.

NetworkChange.NetworkAddressChanged += new System.Net.NetworkInformation.NetworkAddressChangedEventHandler(this.AddressChangedCallback);

Whenever the IP address of the machine is changed, the AddressChangedCallback() will be called. And thats it!

private void AddressChangedCallback(object sender, EventArgs e)
{
    NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
    foreach (NetworkInterface n in adapters)
    {
        System.Diagnostics.Trace.WriteLine(n.Name + " is " + n.OperationalStatus); 
    }
}

Internet/Network connection status

There are many methods for checking whether the machine is connected to Internet. I use one simple method for doing it (cannot say it is 100% foolproof. But it serves the purpose very well). What I used to do is to ping a couple of standard web sites like Google, Yahoo etc. to determine whether we are connected. The downtime of these servers is so small that we can assume that if we are not able to connect to at least one of these servers, there is probably something wrong with the connection.

To start with, there is a simple method for detecting whether the machine is connected to the network. Just enumerate the IP addresses of the local machine and if the media is disconnected, it will not have any other address than the loopback address.

using System.Net;

public bool IsNetworkAvailable()
{
   bool available = false;

   try
   {
      IPHostEntry iphostentry = Dns.GetHostEntry(Environment.MachineName);
      foreach (IPAddress ipaddress in iphostentry.AddressList)
      {
         if (!ipaddress.Equals(IPAddress.Loopback))
         {
            available = true;
            break;
         }
      }
   }
   catch (Exception ex)
   {
      System.Diagnostics.Trace.WriteLine("IsNetworkAvailable(): " + ex.ToString());
   }

   return available;
}

To ping a remote machine, the following function can be used.

using System.Net;
using System.Net.NetworkInformation;

public bool Ping(string url)
{
   bool available = false;

   try
   {
      Ping pingSender = new Ping();
      PingOptions options = new PingOptions();

      // Use the default TTL value, which is 128,
      // but change the fragmentation behavior.
      options.DontFragment = true;

      // Create a buffer of 32 bytes of data to be transmitted.
      string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
      byte[] buffer = System.Text.Encoding.ASCII.GetBytes(data);

      PingReply reply = pingSender.Send(url);

      available = (reply.Status == System.Net.NetworkInformation.IPStatus.Success);
   }
   catch (Exception ex)
   {
      System.Diagnostics.Trace.WriteLine("Ping(): " + ex.ToString());
   }

   return available;
}

Finally, to check whether the URL is available, we can use the following function (Ping() given above cannot be used for all URLs. e.g. we cannot determine whether a page is available in the remote machine. That can be found out by using the code given below).

using System.IO;
using System.Net;

private bool IsURLAvailable(string url)
{
    bool available = false;

    try
    {
        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);

        HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

        using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
        {
            string html = sr.ReadToEnd();
            if (html.Trim() == string.Empty)
                throw new Exception("No data available");

            // Close the stream
            sr.Close();
        }

        resp.Close();
        available = true;
    }
    catch (Exception ex)
    {
        System.Diagnostics.Trace.WriteLine("IsURLAvailable(): " + ex.ToString());
    }

    return available;
}

The above function can be used as shown below. Check multiple urls and if at least one is available, we can assume that we are connected to Internet.

if (IsURLAvailable("http://www.google.com") ||
    IsURLAvailable("http://www.yahoo.com") ||
    IsURLAvailable("http://www.hotmail.com"))
{
    // Connected
}

Thursday, October 9, 2008

Restrict C# applications to single instance

I have seen many methods for doing this. But each one has its own pros & cons. So I thought of combining multiple approaches to implement a new one. In this approach, we will be using a mutex to make sure that only one instance of the application is running. Also, if the application is already running, we will try to bring it to the foreground.

We will be using few API calls for implementing this functionality. To start with, we will create a class Win32API for holding the API declarations.

using System.Runtime.InteropServices;

public class Win32API
{
  public enum WindowState : int
  {
     SW_NORMAL = 1,
     SW_MAXIMIZE = 3,
     SW_MINIMIZE = 6,
     SW_RESTORE = 9
  }

  [DllImport("user32.dll")]
  public static extern bool ShowWindow(IntPtr intPtWnd, int intCmdShow);

  [DllImport("user32.dll")]
  public static extern bool SetForegroundWindow(IntPtr hWnd);

  [DllImport("user32.dll")]
  public static extern IntPtr GetLastActivePopup(IntPtr intPtWnd);

  [DllImport("user32.dll")]
  public static extern bool IsWindowEnabled(IntPtr intPtWnd);

  [DllImport("user32.dll")]
  public static extern bool IsIconic(IntPtr intPtWnd);
}

We will use a GUID to identify the running application. The Main() is written as follows.

using System.Diagnostics;

static void Main()
{
    bool createdNow = true;

    /*
      * Create a Mutex object. A GUID is used as the name, to make sure that it is unique.
      */
    using (Mutex mutex = new Mutex(true, "{D21C4614-5252-4ea5-B38C-8C9A85035434}", out createdNow))
    {
        if (createdNow)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmMainWnd());
        }
        else
        {
            if (!ActivatePreviousInstance())
            {
                // If the attempt to bring the window to foreground fails,
                // just display a message.
                MessageBox.Show("Another instance of this application is already running",
                          "Information",
                          MessageBoxButtons.OK,
                          MessageBoxIcon.Information);
            }
        }
    }
}

private static bool ActivatePreviousInstance()
{
    bool success = false;

    try
    {
        using (Process current = Process.GetCurrentProcess())
        {
            foreach (Process proc in Process.GetProcessesByName(current.ProcessName))
            {
                // Get the process, that is not the current one (the previous instance)
                if (proc.Id != current.Id)
                {
                    IntPtr ptrWnd = proc.MainWindowHandle;

                    // Get handle to popup (child) window, if there is any
                    IntPtr ptrPopupWnd = Win32API.GetLastActivePopup(ptrWnd);

                    // If child window is found, it is the one to be brought to foreground
                    if (ptrPopupWnd != null && Win32API.IsWindowEnabled(ptrPopupWnd))
                    {
                        ptrWnd = ptrPopupWnd;
                    }

                    // Bring the window to foreground
                    if (!Win32API.SetForegroundWindow(proc.MainWindowHandle))
                        throw new Exception("SetForegroundWindow() failed");

                    // If the window is minimized, restore it on screen
                    if (Win32API.IsIconic(ptrWnd))
                    {
                        Win32API.ShowWindow(ptrWnd, (int)Win32API.WindowState.SW_RESTORE);
                    }

                    success = true;
                    break;
                }
            }
        }
    }
    catch (Exception ex)
    {
        Trace.WriteLine("Error [Program::ActivatePreviousInstance]: " + ex.ToString());
    }

    return success;
}

HTTP GET/POST to the same URL

Recently I worked on a project, in which the user will send a POST or GET request to the same URL. We need to follow few steps to implement this functionality.

The IHttpHandler (System.Web) interface comes to picture in this case. This interface is used to define the contract that ASP.NET implements to synchronously process HTTP Web requests using custom HTTP handlers. This interface has two functions - ProcessRequest() and IsReusable() - which needs to be implemented. The ProcessRequest() receives an HttpContext object and this can be used for further processing.

Sample code for an IHttpHandler implementation is given below. First, a web application is created with name XyzDataCollector. Class DataCollector is added to this project.

public class DataCollector : IHttpHandler
{
   public void ProcessRequest(System.Web.HttpContext context)
    {
        HttpResponse objResponse = context.Response;

        switch (context.Request.HttpMethod)
        {
            case "GET":
                objResponse.Write(context.Request.QueryString["assignmentID"]);
                break;

            case "POST":
                context.Response.Write(context.Request["assignmentID"]);
                break;

            default:
                break;
        }
    }

    public bool IsReusable
    {
        get
        {
            return true;
        }
    }
}

The switch statement in the ProcessRequest() is where the processing takes place. We can distinguish between the GET & POST requests here, as shown. Code is written here for handling these requests (in this example, it just returns the passed value).

Now, the most important step. We need to add the HTTP handler information to the Web.config file (add one if it does not exist in the current project). Under the system.web section, we need to add the httpHandlers details.

<httpHandlers>
   <add verb="*" path="GetAssignment.Assign" type="XyzDataCollector.DataCollector"/>
</httpHandlers>

Few points to be noted here. First, the very '*' indicates that it can accept all requests such as GET, POST etc. Instead of '*', it is possible to specify a combination of these values. The path attribute represents the URL path that will be used to access this and the type identifies the class that will process the request. In this case, I am using a different extension - .Assign - instead of standard type such as .aspx. So, it is necessary to define an extension mapping in IIS, for handling this type of request. For this, open IIS and drill down to the XyzDataCollector virtual directory. Right-click on the directory and select Properties. Then click the Configuration button to show the file types and their mappings. Create a new application mapping by clicking the Add button. If a standard extension such as .aspx is used (which is already defined), these steps are not necessary.

ApplicationConfig

AddISAPIExtension

Once this step is completed, IIS will know how to handle the new extension. One point; we need to select the executable as aspnet_isapi.dll (of the current .NET Framework).

Now, to send a GET or POST request to this URL (from a thick client or thin client application), we can use the following functions.

public void DoGET()
{
    string url = "http://localhost/XyzDataCollector/GetAssignment.Assign?assignmentID=10000";

    // Create the web request  
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;

    // Get response  
    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
    {
        // Get the response stream  
        StreamReader reader = new StreamReader(response.GetResponseStream());

        // Get the response  
        string response = reader.ReadToEnd();

        //
        // Add code for processing the response
        //
    }
}

public void DoPOST()
{
    string url = "http://localhost/XyzDataCollector/GetAssignment.Assign";

    Uri address = new Uri(url);

   // Create the web request  
    HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;

    // Set type to POST  
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";

    StringBuilder data = new StringBuilder();
    data.Append("assignmentID=" + HttpUtility.UrlEncode("10000"));

    // Create a byte array of the data we want to send  
    byte[] byteData = UTF8Encoding.UTF8.GetBytes(data.ToString());

    // Set the content length in the request headers  
    request.ContentLength = byteData.Length;

    // Write data  
    using (Stream postStream = request.GetRequestStream())
    {
        postStream.Write(byteData, 0, byteData.Length);
    }

    // Get response  
    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
    {
        // Get the response stream  
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
            string response = reader.ReadToEnd();

            //
            // Add code for processing the response
            //
        }
    }  
}

Executing C# application as a different user

This is conceptually similar to impersonation. But one difference is, in case of impersonation, there is much more control - we can execute different parts of an application work with different privileges. What we are discussing here is applicable only at the application level. The application will be executed as a different user. This is similar to executing an application by selecting "Run as..." from the Explorer context menu.

Code snippet is given below. First, add the following lines to the class.

using System.Diagnostics;
using System.Security;

Function for executing the application as different user:

private void RunAs(string userName,
                        string password,
                        string domain,
                        string application)
{
    try
    {
        using (Process proc = new Process())
        {
            proc.StartInfo.FileName = application;

            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.WindowStyle = ProcessWindowStyle.Normal;

            proc.StartInfo.UserName = userName;
            proc.StartInfo.Password = GetPassword(password);
            proc.StartInfo.Domain = domain;

            proc.Start();

            proc.WaitForExit();

            proc.Close();                   
        }
    }
    catch
    {
        throw;
    }
}

Helper function - GetPassword() - used for getting the secured password from plaintext password:

private  SecureString GetPassword(string password)
{
    Char[] input = password.ToCharArray();
    SecureString securePassword = new SecureString();

    for (int i = 0; i < input.Length; i++)
    {
        securePassword.AppendChar(input[i]);
    }
    securePassword.MakeReadOnly();

    return securePassword;
}

If the computer is in a workgroup (instead of domain), pass empty string to the domain parameter of RunAs().

Impersonation in C#

Impersonation (in programming context) refers to a technique that executes code under another user context than the user who originally started an application. In this case, the user context is temporarily changed once or multiple times during the lifetime of an application.

The reason for doing this is to perform tasks that the current user context of an application is not allowed to do. For example, if the application is running in a restricted  account and if it needs administrative privileges to perform certain actions, impersonations is the right technique.  Of course it is possible to grant the user more privileges, but usually this is a bad idea (due to security constraints) or impossible (e.g. no administrative access to a machine to set privileges for the user).

The System.Security.Principal namespace has classes that can be used for impersonation. The Impersonate() method of WindowsIdentity class returns a WindowsImpersonationContext and this can be used for executing code as a different user. The LogonUser() API function is used for initializing the access token and this token is used for the creation of WindowsIdentity object. After executing code with different privileges, we can return to the current login using Undo() method of the WindowsImpersonationContext instance.

Code snippet for impersonation is given below. First, add the following references to the class:

using System.Security.Principal;
using System.ComponentModel;

Next, declare the Win32 functions and constants (in the same class or preferably, in a separate class - say Win32API)

using System.Runtime.InteropServices;

public class Win32API
{
    // Constant declarations
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    // The LogonUser() API
    [DllImport("advapi32.dll", SetLastError=true)]
    public static extern bool LogonUser(string lpszUsername,
                                         string lpszDomain,
                                         string lpszPassword,
                                         int dwLogonType,
                                         int dwLogonProvider,
                                         out IntPtr phToken);
}

Now the function for impersonation:

private static void RunAsDifferentUser(string userName, string password, string domain)
{
    IntPtr accessToken = IntPtr.Zero;

    try
    {
        if (LogonUser(userName,
                        domain,
                        password,
                        Win32API.LOGON32_LOGON_INTERACTIVE,
                        Win32API.LOGON32_PROVIDER_DEFAULT,
                        out accessToken))
        {
            // Use the token to create an instance of WindwosIdentity class
            using (WindowsIdentity identity = new WindowsIdentity(accessToken))
            {
                // Impersonate now
                using (WindowsImpersonationContext context = identity.Impersonate())
                {
                    //
                    // Add code - Perform the actual operation here
                    //

                    // Return to the original Windows login
                    context.Undo();
                }
            }
        }
        else
            throw new Win32Exception(Marshal.GetLastWin32Error());
    }
    catch
    {
        throw;
    }
    finally
    {
        if (accessToken != IntPtr.Zero)
        {
            CloseHandle(accessToken);
        }
    }
}

If the computer is in a workgroup (instead of domain), pass a "." to the domain parameter of the above function.

Using MSMQ with .NET (C#)

Microsoft Message Queuing (MSMQ) technology enables applications to communicate across heterogeneous networks and systems that may be temporarily offline. MSMQ provides guaranteed message delivery, efficient routing, security, and priority-based messaging. Whenever there's need for two or more applications (processes) to send messages to each other without having to immediately know results, MSMQ can be used. It's free and comes with Windows, but is not installed by default. Even though MSMQ is primarily used for asynchronous processing, it can also be used in synchronous messaging scenarios.

MSMQ is an excellent choice for implementing B2B architecture. A very popular way of implementation is; one component will be putting XML messages to the queue, and the another component will be processing these messages by fetching them, one by one. Such a disconnected architecture is very common in enterprise applications.

.NET framework has very good support form MSMQ. There are a number of classes in the System.Messaging namespace. The MessageQueue class has methods for basic queue management - connecting to queue, create/delete queues etc. The Message class encapsulates the actual message - the data to send or receive into a message.

Check the below link for a simple introduction to using MSMQ with C#.

http://www.csharphelp.com/archives3/archive581.html

One more point; once MSMQ installed, it can be managed using the Computer Management console (Message Queuing under Services and Applications).

MSMQ

Web Application vs Web Site project model (ASP.NET 2.0)

This is a very common question asked by ASP.NET developers: what project model should be used for ASP.NET applications? Web Site project (which was introduced with VS 2005) or Web Application project (which is delivered as add-in for VS 2005 and built-in with VS 2005 SP1)?

Every project model has it's own advantages and disadvantages. To understand more, lets have a look at the features of these two project models.

Web Application project model

  1. Provides the same Web project semantics as Visual Studio .NET 2003 Web projects
  2. It has a project file (structure based on project files)
  3. All code in the project is compiled into a single assembly (the build model)
  4. It supports both IIS and the built-in ASP.NET Development Server
  5. It supports all the features of Visual Studio 2005 (refactoring, generics, etc.) and of ASP.NET 2.0 (master pages, membership and login, site navigation, themes, etc)
  6. Use of FrontPage Server Extensions (FPSE) is no longer a requirement

Web Site project model

  1. No project file (it is based on file system)
  2. It uses a new compilation model. Details can be found from this link.
  3. Uses dynamic compilation. Allows working on pages without building entire site on each page view
  4. It supports both IIS and the built-in ASP.NET Development Server
  5. Each page has its own assembly
  6. It follows a different code model (single page).

Check the below links for more information about the single-page code model and code-behind model:

http://www.odetocode.com/Articles/406.aspx
http://msdn.microsoft.com/hi-in/magazine/cc163675(en-us).aspx

Some other useful articles on the Web Site vs Web Application topic...

http://reddnet.net/code/aspnet-web-site-vs-web-application/
http://reddnet.net/code/asp-net-web-site-vs-web-application-project-part-2/
http://www.codersbarn.com/post/2008/06/ASPNET-Web-Site-versus-Web-Application-Project.aspx

Follow the below link, for installing support for Visual Studio 2005 Web Application projects.

http://msdn.microsoft.com/en-us/asp.net/aa336618.aspx

To summarize; both models have pros and cons. For smaller projects (example; demo purpose) the Web Site model is good, because of the simplicity. If the site that is to be developed is a big one which requires pre-build & post-build steps, has multiple project etc. the Web Application project model should be used. Also, Web Application model is the right choice for converting web sites developed using VS 2003 to VS 2005.