Subscribe

RSS Feed (xml)

Use a Drag-and-Drop Operation

A drag-and-drop operation allows the user to transfer information from one place to another by clicking an item and "dragging" it to another location. A drag-and-drop operation consists of the following three basic steps:

  1. The user clicks a control, holds the mouse button down, and begins dragging. If the control supports the drag-and-drop feature, it sets aside some information.

  2. The user drags the mouse over another control. If this control accepts the dragged type of content, the mouse cursor changes to the special drag-and-drop icon (arrow and page). Otherwise, the mouse cursor becomes a circle with a line drawn through it.

  3. When the user releases the mouse button, the data is sent to the control, which can then process it appropriately.

To support drag and drop, you must handle the DragEnter, DragDrop, and (typically) the MouseDown events. This example uses two text boxes. Here's the code needed to attach the event handlers we'll use:

this.TextBox2.MouseDown += new MouseEventHandler(this.TextBox_MouseDown);
this.TextBox2.DragDrop += new DragEventHandler(this.TextBox_DragDrop);
this.TextBox2.DragEnter += new DragEventHandler(this.TextBox_DragEnter);

this.TextBox1.MouseDown += new MouseEventHandler(this.TextBox_MouseDown);
this.TextBox1.DragDrop += new DragEventHandler(this.TextBox_DragDrop);
this.TextBox1.DragEnter += new DragEventHandler(this.TextBox_DragEnter);

To start a drag-and-drop operation, you call the source control's DoDragDrop method. At this point you submit the data and specify the type of operations that will be supported (copying, moving, and so on). The following recipe example initiates a drag-and-drop operation when the user clicks a text box:

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

    TextBox txt = (TextBox)sender;
    txt.SelectAll();
    txt.DoDragDrop(txt.Text, DragDropEffects.Copy);
}

Controls that can receive dragged data must have the AllowDrop property set to true. These controls will receive a DragEnter event when the mouse drags the data over them. At this point, you can examine the data that's being dragged, decide whether the control can accept the drop, and set the DragEventArgs.Effect property accordingly, as shown here in this code:

private void TextBox_DragEnter(object sender, 
  System.Windows.Forms.DragEventArgs e) {

    if (e.Data.GetDataPresent(DataFormats.Text)) {
        e.Effect = DragDropEffects.Copy;
    }
    else {
        e.Effect = DragDropEffects.None;
    }
}

The final step is to respond to the DragDrop event, which occurs when the user releases the mouse button.

private void TextBox_DragDrop(object sender, 
  System.Windows.Forms.DragEventArgs e) {

    TextBox txt = (TextBox)sender;
    txt.Text = (string)e.Data.GetData(DataFormats.Text);
}

Using the code presented so far, you can create a simple drag-and-drop test application (see below figure) that allows text to be dragged from one text box to another. You can also drag text from another application and drop it into either text box.

textbox.JPG

Technorati :

Validate an Input Control

There are a number of ways that you can perform validation in a Windows- based application. One approach is to respond to control validation events and prevent users from changing focus from one control to another if an error exists. A less invasive approach is to simply flag the offending control in some way so that the user can review all the errors at once. You can use this approach in .NET with the ErrorProvider control.

The ErrorProvider is a special provider control that displays error icons next to invalid controls. You show the error icon next to a control by using the ErrorProvider.SetError method and specifying the appropriate control and a string error message. The ErrorProvider will then show a warning icon automatically to the right of the control. When the user hovers the mouse above the warning icon, the detailed message appears. Below figure shows how the ErrorProvider will indicate an input error for a TextBox control.

user-input.JPG

You only need to add one ErrorProvider control to your form, and you can use it to display an error icon next to any control. To add the ErrorProvider, drag it into the component tray or create it manually in code. The following form code checks the content of the text box every time a key is pressed. The code validates this text box using a regular expression with checks to see if the value corresponds to a valid e-mail address. If validation fails, the ErrorProvider is used to display an error message. If the text is valid, any existing error message is cleared from the ErrorProvider. Finally, the Click event handler for the OK button steps through all the controls on the form and verifies that none of them have errors before allowing the application to continue.

using System;
using System.Windows.Forms;
using System.Text.RegularExpressions;

public class ErrorProviderValidation : System.Windows.Forms.Form {

    // (Designer code omitted.)

    private void txtEmail_TextChanged(object sender, System.EventArgs e) {
    
        Regex regex;
        regex = new Regex(@"\S+@\S+\.\S+");

        Control ctrl = (Control)sender;
        if (regex.IsMatch(ctrl.Text)) {
            errProvider.SetError(ctrl, "");
        }
        else {
            errProvider.SetError(ctrl, "This is not a valid e-mail address.");
        }
    }

    private void cmdOK_Click(object sender, System.EventArgs e) {
    
        string errorText = "";
        bool invalidInput = false;

        foreach (Control ctrl in this.Controls) {
        
            if (errProvider.GetError(ctrl) != "")
            {
                errorText += "   * " + errProvider.GetError(ctrl) + "\n";
                invalidInput = true;
            }
        }
            
        if (invalidInput) {
        
            MessageBox.Show(
              "The form contains the following unresolved errors:\n\n" +
              errorText, "Invalid Input", MessageBoxButtons.OK,
              MessageBoxIcon.Warning);
        }
        else {
            this.Close();
        }
    }
}

Technorati :

Create an Animated System Tray Icon

The .NET Framework makes it easy to show a system tray icon with the NotifyIcon control. You simply need to add this control to a form, supply an icon by setting the Icon property, and, optionally, you can add a linked context menu through the ContextMenu property. The NotifyIcon control automatically displays its context menu when it's right-clicked, unlike most other controls.

You can animate a system tray icon by simply swapping the icon periodically. For example, the following program uses eight icons, each of which shows a moon graphic in a different stage of fullness. By moving from one image to another, the illusion of animation is created.

using System;
using System.Windows.Forms;
using System.Drawing;

public class AnimatedSystemTrayIcon : System.Windows.Forms.Form {

    // (Designer code omitted.)

    Icon[] images;
    int offset = 0;

    private void Form1_Load(object sender, System.EventArgs e) {
    
        // Load the basic set of eight icons.
        images = new Icon[8];
        images[0] = new Icon("moon01.ico");
        images[1] = new Icon("moon02.ico");
        images[2] = new Icon("moon03.ico");
        images[3] = new Icon("moon04.ico");
        images[4] = new Icon("moon05.ico");
        images[5] = new Icon("moon06.ico");
        images[6] = new Icon("moon07.ico");
        images[7] = new Icon("moon08.ico");
    }

    private void timer_Elapsed(object sender, 
      System.Timers.ElapsedEventArgs e) {
    
        // Change the icon.
        // This event handler fires once every second (1000 ms).
        notifyIcon.Icon = images[offset];
        offset++;
        if (offset > 7) offset = 0;
    }
}

Technorati :

Make a Borderless Form Movable

Borderless forms omit a title bar, which makes it impossible for a user to move them. You can compensate for this shortcoming by adding a control to the form that serves the same purpose. For example, below figure shows a form that includes a label to support dragging. The user can click this label, and then drag the form to a new location on the screen while holding down the mouse button. As the user moves the mouse, the form is automatically moved correspondingly, as though it's "attached" to the mouse pointer.

movable.JPG

To implement this solution, you need take the following steps:

  1. Create a form-level Boolean variable that tracks whether or not the form is currently being dragged.

  2. When the label is clicked, the code sets the flag to indicate that the form is in drag mode. At the same time, the current mouse position is recorded. You add this logic to the event handler for the Label.MouseDown event.

  3. When the user moves the mouse over the label, the form is moved correspondingly so that the position of the mouse over the label is unchanged. You add this logic to the event handler for the Label.MouseMove event.

  4. When the user releases the mouse button, the dragging mode is switched off. You add this logic to the event handler for the Label.MouseUp event.

Here's the complete form code:

using System;
using System.Windows.Forms;
using System.Drawing;

public class DragForm : System.Windows.Forms.Form {

    // (Designer code omitted.)

    // Tracks whether the form is in drag mode. If it is, mouse movements
    // over the label will be translated into form movements.
    private bool dragging;

    // Stores the offset where the label is clicked.
    private Point pointClicked;

    private void lblDrag_MouseDown(object sender,
      System.Windows.Forms.MouseEventArgs e) {
    
        if (e.Button == MouseButtons.Left) {
        
            dragging = true;
            pointClicked = new Point(e.X, e.Y);
        }
        else {
        
            dragging = false;
        }
    }

    private void lblDrag_MouseMove(object sender,
      System.Windows.Forms.MouseEventArgs e) {
    
        if (dragging) {
        
            Point pointMoveTo;

            // Find the current mouse position in screen coordinates.
            pointMoveTo = this.PointToScreen(new Point(e.X, e.Y));

            // Compensate for the position the control was clicked.
            pointMoveTo.Offset(-pointClicked.X, -pointClicked.Y);

            // Move the form.
            this.Location = pointMoveTo;
        }   
    }

    private void lblDrag_MouseUp(object sender,
      System.Windows.Forms.MouseEventArgs e) {
    
        dragging = false;
    }

    private void cmdClose_Click(object sender, System.EventArgs e) {
    
        this.Close();
    }
}

Technorati :

Immovable Forms Creation

You can create a borderless form by setting the FormBorderStyle property to None. Borderless forms can't be moved. However, they also lack any kind of border-if you want the customary blue border, you'll need to add it yourself either with manual drawing code or by using a background image.

There's one other approach to creating an immovable form that provides a basic control-style border. First, set the ControlBox, MinimizeBox, and MaximizeBox properties of the form to false. Then set the Text property to an empty string. The form will have a raised gray border or black line (depending on the FormBorderStyle option you use), similar to a button. Below figure shows both types of immovable forms.

immovable.JPG

Technorati :

Making Multilingual Form in C#

The .NET Framework includes built-in support for localization through its use of resource files. The basic idea is to store information that's locale-specific (for example, button text) in a resource file. You can then create multiple resource files for multiple different cultures and compile them into satellite assemblies. When you run the application, .NET will automatically use the correct satellite assembly based on the locale settings of the current computer.

You can read to and write from resource files manually. However, Visual Studio .NET also includes extensive design-time support for localized forms. It works like this:

  1. First set the Localizable property of the form to true using the Properties window.

  2. Set the Language property of the form to the locale for which you would like to enter information. (See Figure1) Then configure the localizable properties of all the controls on the form. Instead of storing your changes in the designer-generated code for the form, Visual Studio .NET will actually create a new resource file to hold your data.

  3. Repeat step 2 for each language that you want to support. Each time, a new resource file will be generated. If you change the Language property to a locale you have already configured, your previous settings will reappear, and you'll be able to modify them.

    figure1.JPG
You can now compile and test your application on differently localized systems. Visual Studio .NET will create a separate directory and satellite assembly for each resource file in the project. You can select Project | Show All Files from the Visual Studio .NET menu to see how these files are arranged, as shown in Figure2.
figure2.JPG

As a testing shortcut, you can also force your application to adopt a specific culture by modifying the Thread.CurrentUICulture property of the application thread. However, you must modify this property before the form has loaded.

using System;
using System.Windows.Forms;
using System.Threading;
using System.Globalization;

public class MultiLingualForm : System.Windows.Forms.Form {

private System.Windows.Forms.Label label1;

// (Designer code omitted.)

static void Main() {

Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");
Application.Run(new MultiLingualForm());
}
}

Using Part of a Main Menu for a Context Menu

In many applications, a control's context-sensitive menu duplicates a portion of the main menu. However, .NET does not allow you to create a MenuItem instance that's contained in more than one menu at a time.

The solution is to make a duplicate copy of a portion of the menu using the CloneMenu method. The CloneMenu method not only copies the appropriate MenuItem items (and any contained submenus), it also registers each MenuItem object with the same event handlers. Thus, when a user clicks a cloned menu item in a context menu, the same event handler will be triggered as if the user clicked the duplicate menu item in the main menu.

For example, consider the test application shown in the following Figure. In this example, the context menu for the text box shows the same entries that are found in the File menu. Technically, these are duplicate copies of the appropriate MenuItem objects. However, if the user clicks on one of these entries, the same event handler is executed.

combo-box.JPG

Here's the form code you need to create this example. It duplicates the entries in the main menu when the form first loads. (Unfortunately, it's not possible to perform the same operation at design time.)

using System;
using System.Windows.Forms;
using System.Drawing;

public class ContextMenuCopy : System.Windows.Forms.Form {

    // (Designer code omitted.)

    private void ContextMenuCopy_Load(object sender, System.EventArgs e) {
    
        ContextMenu mnuContext = new ContextMenu();

        // Copy the menu items from the File menu into a context menu.
        foreach (MenuItem mnuItem in mnuFile.MenuItems) {

            mnuContext.MenuItems.Add(mnuItem.CloneMenu());
        }

        // Attach the context menu to the text box.
        TextBox1.ContextMenu = mnuContext;
    }

    private void TextBox1_MouseDown(object sender,
      System.Windows.Forms.MouseEventArgs e) {
   
        if (e.Button == MouseButtons.Right){

            TextBox1.ContextMenu.Show(TextBox1, new Point(e.X, e.Y));
        }
    }

    private void mnuOpen_Click(object sender, System.EventArgs e) {
    
        MessageBox.Show("This is the event handler for Open.");
    }

    private void mnuSave_Click(object sender, System.EventArgs e) {
    
        MessageBox.Show("This is the event handler for Save.");
    }

    private void mnuClick_Click(object sender, System.EventArgs e) {
    
        MessageBox.Show("This is the event handler for Exit.");
    }
}

Technorati :

How To Link a Context Menu to a Control

You can link a control to a context menu by settings the control's ContextMenu property. However, this is only a convenience-to display the context menu you must retrieve the menu and call its Show method, supplying both a parent control and a pair of coordinates. Usually, you implement this logic in an event handler for the MouseDown event.

The good news is that the logic for showing a context menu is completely generic, no matter what the control is. Every control supports the ContextMenu property (which is inherited from the base Control class), which means you can easily write a generic event handler that will display context menus for all controls.

For example, consider a form with a label, a picture box, and a text box. You can write a single event handler that responds to the MouseDown event for all these objects. Here's how the designer code would look if you connected all these events to an event handler named Control_MouseDown:

this.label1.MouseDown += new MouseEventHandler(this.Control_MouseDown);
this.pictureBox1.MouseDown += new MouseEventHandler(this.Control_MouseDown);
this.textBox1.MouseDown += new MouseEventHandler(this.Control_MouseDown);

The event-handling code is completely generic. It just casts the sender to a Control, checks for a linked context menu, and displays it.

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

    if (e.Button == MouseButtons.Right) {
    
        // Get the source control.
        Control ctrl = (Control)sender;

        if (ctrl.ContextMenu != null) {
        
            // Show the linked menu over the control.
            ctrl.ContextMenu.Show(ctrl, new Point(e.X, e.Y));
        }
    }
}

Technorati :

How to Sort a List View by Any Column

The ListView control provides a Sort method that orders items alphabetically based on the text in the first column. If you want to sort based on other column values or order items in any other way, you need to create a custom implementation of the IComparer interface that can perform the work.

The IComparer interface defines a single method named Compare, which takes two objects and determines which one should be ordered first. Here is a custom ListViewItemComparer class that implements IComparer. It provides two additional properties: Column and Numeric. Column indicates the column that should be used for sorting, and Numeric is a Boolean flag that can be set to true if you want to perform number-based comparisons instead of alphabetic comparisons.

using System;
using System.Collections;
using System.Windows.Forms;

public class ListViewItemComparer : IComparer {

    private int column;
    private bool numeric = false;

    public int Column {
    
        get {return column;}
        set {column = value;}
    }
        
    public bool Numeric {
    
        get {return numeric;}
        set {numeric = value;}
    }
        
    public ListViewItemComparer(int columnIndex) {
    
        Column = columnIndex;
    }

    public int Compare(object x, object y) {
    
        ListViewItem listX = (ListViewItem)x;
        ListViewItem listY = (ListViewItem)y;

        if (Numeric) {
        
            // Convert column text to numbers before comparing.
            // If the conversion fails, just use the value 0.
            decimal listXVal, listYVal;
            try {
                listXVal = Decimal.Parse(listX.SubItems[Column].Text);
            }
            catch {
                listXVal = 0;
            }

            try {
                 listYVal = Decimal.Parse(listY.SubItems[Column].Text);
            }
            catch {
                 listYVal = 0;
            }

            return Decimal.Compare(listXVal, listYVal);
        }
        else {
        
            // Keep the column text in its native string format
            // and perform an alphabetic comparison.
            string listXText = listX.SubItems[Column].Text;
            string listYText = listY.SubItems[Column].Text;

            return String.Compare(listXText, listYText);
        }
    }
}

Now to sort the list view, you simply need to create a ListViewItemComparer instance, configure it appropriately, and then set it in the ListView.ListViewItemSorter property before you call the ListView.Sort method.

The following form demonstrates a simple test of the ListViewItemComparer. Every time the user clicks a column header in the list view, a new ListViewItemComparer is created and used to sort the list based on the clicked column.

using System;
using System.Windows.Forms;

public class ListViewItemSort : System.Windows.Forms.Form {

    // (Designer code omitted.)

    private void ListView1_ColumnClick(object sender, 
      System.Windows.Forms.ColumnClickEventArgs e) {
    
        ListViewItemComparer sorter = new ListViewItemComparer(e.Column);
        ListView1.ListViewItemSorter = sorter;
        ListView1.Sort();
    }
}

Technorati :

How to Use an Autocomplete Combo Box

There are many different variations to the autocomplete control. Sometimes, the control fills in values based on a list of recent selections (as Microsoft Excel does when entering cell values) or might display a drop-down list of near matches (as Microsoft Internet Explorer does when typing a URL). You can create a basic autocomplete combo box by handling the KeyPress and TextChanged events, or by creating a custom class that derives from ComboBox and overrides the OnKeyPress and OnTextChanged methods. This recipe takes the latter approach.

In the OnKeyPress method, the combo box determines whether or not an autocomplete replacement should be made. If the user pressed a character key (such as a letter) the replacement can be made, but if the user has pressed a control key (such as the backspace key, the cursor keys, and so on), no action should be taken. The OnTextChanged method performs the actual replacement after the key processing is complete. This method looks up the first match for the current text in the list of items, and then adds the rest of the matching text. After the text is added, the combo box selects the characters between the current insertion point and the end of the text. This allows the user to continue typing and replace the autocomplete text if it isn't what the user wants.

Here's the full code for the AutoCompleteComboBox class:

using System;
using System.Windows.Forms;

public class AutoCompleteComboBox : ComboBox {

    // Track if a special key is pressed
    // (in which case the text replacement operation will be skipped).
    private bool controlKey = false;

    // Determine whether a special key was pressed.
    protected override void OnKeyPress(
      System.Windows.Forms.KeyPressEventArgs e) {

        base.OnKeyPress(e);

        if (e.KeyChar == (int)Keys.Escape) {
        
            // Clear the text.
            this.SelectedIndex = -1;
            this.Text = "";
            controlKey = true;
        }
        else if (Char.IsControl(e.KeyChar)) {
        
            controlKey = true;
        }
        else {
        
            controlKey = false;
        }
    }

    // Perform the text substitution.
    protected override void OnTextChanged(System.EventArgs e) {
    
        base.OnTextChanged(e);

        if (this.Text != "" && !controlKey) {
        
            // Search for a matching entry.
            string matchText = this.Text;
            int match = this.FindString(matchText);

            // If a matching entry is found, insert it now.
            if (match != -1) {
            
                this.SelectedIndex = match;

                // Select the added text so it can be replaced
                // if the user keeps typing.
                this.SelectionStart = matchText.Length;
                this.SelectionLength = this.Text.Length - this.SelectionStart;
            }
        }
    }
}

To test the AutoCompleteComboBox, you can create a simple Windows client that adds the combo box to a form and fills it with a list of words. In this example, the list of words is retrieved from a text file and the control is added manually. You could also compile the AutoCompleteComboBox class to a separate class library assembly and then add it to the Toolbox, so you could add it to a form at design time.

using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;

public class AutoCompleteComboBoxTest : System.Windows.Forms.Form {

    // (Designer code omitted.)

    private void AutoCompleteComboBox_Load(object sender, 
      System.EventArgs e) {
    
        // Add the combo box to the form.
        AutoCompleteComboBox combo = new AutoCompleteComboBox();
        combo.Location = new Point(10,10);
        this.Controls.Add(combo);

        // Add the word list.
        FileStream fs = new FileStream("words.txt", FileMode.Open);
        using (StreamReader r = new StreamReader(fs)) {

            while (r.Peek() > -1) {
        
                string word = r.ReadLine();
                combo.Items.Add(word);
            }
        }
    }
}

Technorati :

Restrict a Text Box to Numeric Input

The best way to correct invalid input is to prevent it from being entered in the first place. This approach is easy to implement with the text box because it provides a KeyPress event that occurs after the keystroke has been received but before it's displayed. You can use the KeyPressEventArgs event parameter to effectively "cancel" an invalid keystroke by setting the Handled property to true.

To allow only numeric input, you must allow a keystroke only if it corresponds to a number (0 through 9) or a special control key (such as delete or the arrow keys). The keystroke character is provided to the KeyPress event through the KeyPressEventArgs.KeyChar property. You can use two static methods of the System.Char class-IsDigit and IsControl-to quickly test the character.

Here's the event handler you would use to prevent non-numeric input:

private void textBox1_KeyPress(object sender, 
  System.Windows.Forms.KeyPressEventArgs e) {

    if (!Char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar)) {
        e.Handled = true;
    }
}

Notice that this code rejects the decimal separator. If you need to allow this character (for example, to permit the user to enter a fractional currency amount), you'll have to modify the code slightly, as shown here:

// Get the decimal separator character on this platform
// (typically a "." for US-English).
string decimalString =
  Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
char decimalChar = Convert.ToChar(decimalString);

if (Char.IsDigit(e.KeyChar) || Char.IsControl(e.KeyChar)) {}
else if (e.KeyChar == '.' && textBox1.Text.IndexOf(".") == -1) {}
else {
    e.Handled = true;
}

This code allows only a single decimal point, but it makes no restriction about how many significant digits can be used. It also doesn't allow the entry of negative numbers. (You could change this behavior by allowing the minus sign (-) provided it's the first character.) Remember that this code also assumes you have imported the System.Threading namespace.

Technorati :

Force a List Box to Scroll

In some cases, you might have a list box that stores a significant amount of information or one that you add information to periodically. It's often the case that the most recent information, which is added at the end of the list, is more important than the information at the top of the list. One solution is to scroll the list box so that recently added items are visible.

The following example form includes a list box and a button that adds 20 items to the list and then scrolls to the last full page using the TopIndex property.

using System;
using System.Windows.Forms;

public class ListBoxScrollTest : System.Windows.Forms.Form {

    // (Designer code omitted.)

    int counter = 0;

    private void cmdTest_Click(object sender, System.EventArgs e) {
    
        for (int i = 0; i < 20; i++) {
        
            counter++;
            listBox1.Items.Add("Item " + counter.ToString());
        }
        listBox1.TopIndex = listBox1.Items.Count - 1;
    }
}

Technorati :

Save the Size and Location of a Form

The Windows registry is an ideal place for storing position and size information for a form. Typically, you'll store information about each form in a separate key, perhaps using the class name of the form. These keys will be stored under an application-specific key.

To automate this process, it helps to create a dedicated class that saves and retrieves form settings. The FormSettingStore class shown in the following example fills this role. This class provides a SaveSettings method that accepts a form and writes its size and position information to the registry, as well as an ApplySettings method that accepts a form and applies the settings from the registry. The registry key path and the name of the form subkey are stored as class member variables.

using System;
using System.Windows.Forms;
using Microsoft.Win32;

public class FormSettingStore {

    private string regPath;
    private string formName;
    private RegistryKey key;

    public string RegistryPath {
        get {return regPath;)
    }

    public string FormName {
        get {return formName;}
    }

    public FormSettingStore(string registryPath, string formName) {
    
        this.regPath = registryPath;
        this.formName = formName;

        // Create the key if it doesn't exist.
        key = Registry.LocalMachine.CreateSubKey(registryPath + formName);
    }

    public void SaveSettings(System.Windows.Forms.Form form) {
    
        key.SetValue("Height", form.Height);
        key.SetValue("Width", form.Width);
        key.SetValue("Left", form.Left);
        key.SetValue("Top", form.Top);
    }

    public void ApplySettings(System.Windows.Forms.Form form) {
    
        // If form settings are not available, the current form settings
        // are used instead.
        form.Height = (int)key.GetValue("Height", form.Height);
        form.Width = (int)key.GetValue("Width", form.Width);
        form.Left = (int)key.GetValue("Left", form.Left);
        form.Top = (int)key.GetValue("Top", form.Top);
    }
}

To use the FormSettingStore class, simply add the event-handling code shown here to any form. This code saves the form properties when the form closes and restores them when the form is loaded.

private FormSettingStore formSettings;

private void Form1_Load(object sender, System.EventArgs e) {

    formSettings = new FormSettingStore(@"Software\MyApp\", this.Name);
    formSettings.ApplySettings(this);
}

private void Form1_Closed(object sender, System.EventArgs e) {

    formSettings.SaveSettings(this);
}

Technorati :

Find All MDI Child Forms

The .NET Framework includes two convenient shortcuts for managing MDI applications: the MdiChildren and the MdiParent properties of the Form class. You can investigate the MdiParent property of any MDI child to find the containing parent window. You can use the MdiChildren collection of the parent to find all the MDI child windows.

For example, consider the following example, which displays simple child windows. Each child window includes a label with some date information, and a button. When the button is clicked, the event handler walks through all the child windows and displays the label text that each one contains. Each window also exposes the label text through a read- only property.

Here's the form code for the child window:

public class MDIChild : System.Windows.Forms.Form {

    private System.Windows.Forms.Button cmdShowAllWindows;
    private System.Windows.Forms.Label label;

    // (Designer code omitted.)

    public string LabelText {
    
        get {
            return label.Text;
        }
    }

    private void cmdShowAllWindows_Click(object sender, System.EventArgs e) {

        // Walk through the collection of child windows.
        foreach (Form frm in this.MdiParent.MdiChildren) {
        
            // Cast the generic Form to the expended derived class type.
            MDIChild child = (MDIChild)frm;
            MessageBox.Show(child.LabelText, frm.Text);
        }
    }

    private void MDIChild_Load(object sender, System.EventArgs e){

        label.Text = DateTime.Now.ToString();
    }
}

Check that when the code walks through the collection of child forms, it must convert the generic Form reference to the custom derived MDIChild form class so that it can use the LabelText property.

Track the Visible Forms in an Application

NET does not provide any way of determining what forms are currently being displayed in an application. If you want to determine what forms are in existence, or what forms are displayed, or you want one form to call the methods or set the properties of another form, you'll need to keep track of form instances on your own.

One useful approach is to create a class consisting of static members. This class can track open forms using a collection, or dedicated properties. Here's an example of a class that can track two forms:

public class OpenForms {

    public static Form MainForm;
    public static Form SecondaryForm;
}

Now when either the main or the secondary form is shown, it can register itself with the OpenForms class. A logical place to put this code is in the Form.Load event handler.

private void MainForm_Load(object sender, EventArgs e) {

    // Register the newly created form instance.
    OpenForms.MainForm = this;
}

You can use similar code to remove the reference when the form is closed.

private void MainForm_Unload(object sender, EventArgs e) {

    // Remove the form instance.
    OpenForms.MainForm = null;
}

Now another form can interact with this form through the OpenForms class. For example, here's how the main form would hide the second form:

if (OpenForms.SecondaryForm != null) {
    OpenForms.SecondaryForm.Hide();
}

In this approach, we assume that every form is created only once. If you have a document-based application where the user can create multiple instances of the same form, you should track these forms using a collection. Here's an example with an ArrayList collection:

public class OpenForms {

    public static Form MainForm;
    public static ArrayList DocForms = new ArrayList();
}

Forms can then add themselves to the document collection as needed, as shown in the following code:

private void DocForm_Load(object sender, EventArgs e) {

    // Register the newly created form instance.
    OpenForms.DocForms.Add(this);
}

Process All the Controls on a Form

You can iterate through the controls on a form using the Form.Controls collection, which includes all the controls that are placed directly on the form surface. However, if any of these controls are container controls (such as GroupBox, Panel, or TabPage), they might contain more controls. Thus, it's necessary to use recursive logic that searches the Controls collection of every control on the form.

The following example shows a form that performs this recursive logic to find every text box on a form and clears the text they contain. The form tests each control to determine whether it's a text box by using the typeof operator.

using System;
using System.Windows.Forms;

public class ProcessAllControls : System.Windows.Forms.Form {

    // (Designer code omitted.)

    private void cmdProcessAll_Click(object sender, System.EventArgs e) {
        ProcessControls(this);
    }

    private void ProcessControls(Control ctrl) {
    
        // Ignore the control unless it's a text box.
        if (ctrl.GetType() == typeof(TextBox)) {
            ctrl.Text = "";
        }
        
        // Process controls recursively. 
        // This is required if controls contain other controls
        // (for example, if you use panels, group boxes, or other
        // container controls).
        foreach (Control ctrlChild in ctrl.Controls) {
            ProcessControls(ctrlChild);
        }
    }
}

Link Data to a Control in C#

Every class that derives from System.Windows.Forms.Control provides a Tag property that you can use to store a reference to any type of object. The Tag property isn't used by the control or the Microsoft .NET Framework. Instead, it's reserved as a convenient storage place for application-specific information. In addition, some other non-Control-derived classes provide a Tag property. Examples include the ListViewItem and TreeNode classes (which represent items in a ListView or a TreeView control). One class that does not provide a Tag property is MenuItem.

The Tag property is defined as a generic Object type, which means that you can use it to store any value type or reference type, from a simple number or string to a custom object you have defined. When retrieving data from the Tag property, you'll need to cast the object to its original type.

The following example adds a list of files to a list view. The corresponding FileInfo object for each file is stored in the Tag property. When a user double- clicks one of the list items, the code retrieves the FileInfo object from the Tag property and displays the file size in a MessageBox.

using System;
using System.Windows.Forms;
using System.IO;

public class TagPropertyExample : System.Windows.Forms.Form (

    // (Designer code omitted.)

    private void TagPropertyExample_Load(object sender, System.EventArgs e) {
    
        // Get all the files in the directory.
        DirectoryInfo directory = new DirectoryInfo("C:\\");
        FileInfo[] files = directory.GetFiles();

        // Display all the files in the ListView.
        foreach (FileInfo file in files) {
        
            ListViewItem item = listView.Items.Add(file.Name);
            item.ImageIndex = 0;
            item.Tag = file;
        }
    }

    private void listView_ItemActivate(object sender, System.EventArgs e) {
    
        // Get the file's size from the linked FileInfo.
        ListViewItem item = ((ListView)sender).SelectedItems[0];
        FileInfo file = (FileInfo)item.Tag;
        string info = file.FullName + " is " + file.Length + " bytes.";

        // Display the file's size.
        MessageBox.Show(info, "File Information");
    }
}

Add a Control Programmatically in C#

In a .NET Windows-based application, there's really no difference between creating a control at design time and creating it at runtime. When you create controls at design time (using a tool like Microsoft Visual Studio .NET), the necessary code is added to your form class, typically in a special method named InitializeComponent. You can use the same code in your application to create controls on the fly. Just follow these steps:

  1. Create an instance of the appropriate control class.

  2. Configure the control properties accordingly (particularly the size and position coordinates).

  3. Add the control to the form or another container.

  4. In addition, if you need to handle the events for the new control, you can wire them up to existing methods.

Every control provides a Controls property that references a ControlCollection that contains all of its child controls. To add a child control, you invoke the ControlCollection.Add method. The following example demonstrates this by dynamically creating a list of check boxes. One check box is added for each item in an array. All the check boxes are added to a panel that has its AutoScroll property set to true, which gives basic scrolling support to the check box list.

using System;
using System.Windows.Forms;

public class DynamicCheckBox : System.Windows.Forms.Form {

    // (Designer code omitted.)

    private void DynamicCheckBox_Load(object sender, System.EventArgs e) {
    
        // Create the array.
        string[] foods = {"Grain", "Bread", "Beans", "Eggs",
          "Chicken", "Milk", "Fruit", "Vegetables",
          "Pasta", "Rice", "Fish", "Beef"};

        int topPosition = 10;
        foreach (string food in foods)
        {
            // Create a new check box.
            CheckBox checkBox = new CheckBox();
            checkBox.Left = 10;
            checkBox.Top = topPosition;
            topPosition += 30;
            checkBox.Text = food;

            // Add the check box to the form.
            panel.Controls.Add(checkBox);
        }
    }
}

Perform an XSL Transform

XSLT (or XSL transforms) is an XML-based language designed to transform one XML document into another document. XSLT can be used to create a new XML document with the same data but arranged in a different structure or to select a subset of the data in a document. It can also be used to create a different type of structured document. XSLT is commonly used in this manner to format an XML document into an HTML page.

XSLT is a rich language, and creating XSL transforms is beyond the scope of this book. However, you can learn how to create simple XSLT documents by looking at a basic example. This recipe transforms the orders.xml document shown in the post into an HTML document with a table and then displays the results. To perform this transformation, you'll need the following XSLT stylesheet:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0" >

  <xsl:template match="Order">
    <html><body><p>
    Order <b><xsl:value-of select="Client/@id"/></b>
    for <xsl:value-of select="Client/Name"/></p>
    <table border="1">
    <td>ID</td><td>Name</td><td>Price</td>
    <xsl:apply-templates select="Items/Item"/>
    </table></body></html>
  </xsl:template>
 
  <xsl:template match="Items/Item">
    <tr>
    <td><xsl:value-of select="@id"/></td>
    <td><xsl:value-of select="Name"/></td>
    <td><xsl:value-of select="Price"/></td>
    </tr>
  </xsl:template>

</xsl:stylesheet>

Essentially, every XSL stylesheet consists of a set of templates. Each template matches some set of elements in the source document and then describes the contribution that the matched element will make to the resulting document.

The orders.xslt stylesheet contains two template elements (as children of the root stylesheet element). The first template matches the root Order element. When the XSLT processor finds an Order element, it outputs the tags necessary to start an HTML table with appropriate column headings and inserts some data about the client using the value-of command, which outputs the text result of an XPath expression. In this case, the XPath expressions (Client/@id and Client/Name) match the id attribute and the Name element.

Next the apply-templates command is used to branch off and perform processing of any contained Item elements. This is required because there might be multiple Item elements. Each Item element is matched using the XPath expression Items/Item. The root Order node isn't specified because Order is the current node. Finally, the initial template writes the tags necessary to end the HTML document.

If you execute this transform on the sample orders.xml file shown in this post, you'll end up with the following HTML document:

<html>
  <body>
    <p>
    Order <b>ROS-930252034</b>
    for Remarkable Office Supplies</p>
    <table border="1">
      <td>ID</td>
      <td>Name</td>
      <td>Price</td>
      <tr>
        <td>1001</td>
        <td>Electronic Protractor</td>
        <td>42.99</td>
      </tr>
      <tr>
        <td>1002</td>
        <td>Invisible Ink</td>
        <td>200.25</td>
      </tr>
    </table>
  </body>
</html>

To apply an XSLT stylesheet in .NET, you use the XslTransform class. The following code shows a Windows-based application that programmatically applies the transformation and then displays the transformed file in a Web browser window. In this example, the code uses the overloaded version of the Transform method that saves the result document directly to disk, although you could receive it as a stream and process it inside your application instead.

using System;
using System.Windows.Forms;
using System.Xml.Xsl;

public class TransformXml : System.Windows.Forms.Form {

    private AxSHDocVw.AxWebBrowser webBrowser;

     // (Designer code omitted.)

    private void TransformXml_Load(object sender, System.EventArgs e) {

        XslTransform transform = new XslTransform();
            
        // Load the XSL stylesheet.
        transform.Load("orders.xslt");
            
        // Transform orders.xml into orders.html using orders.xslt.
        transform.Transform("orders.xml", "orders.html", null);

        object var = null;
        webBrowser.Navigate(
          "file:///" + Application.StartupPath + @"\orders.html",
          ref var, ref var, ref var, ref var);
    }
}

Technorati :

Generate a Class from a Schema

Previous Post introduced the xsd.exe command-line utility, which can be used to generate schemas based on class definitions. The reverse operation-generating C# source code based on an XML schema document-is also possible. This is primarily useful if you want to write a certain format of XML document, but you don't want to manually create the document by writing individual nodes with the XmlDocument or XmlTextWriter classes. Instead, by using xsd.exe, you can generate a set of full .NET objects. You can then serialize these objects to the required XML representation using the XmlSerializer, as described in recipe 5.9.

To generate source code from a schema, you simply need to supply the filename of the schema document and add the /c parameter to indicate that you want to generate the required classes. For example, consider the schema shown in this post. You can generate C# code for this schema with the following command-line:

xsd ProductCatalog.xsd /c

This will generate one file (ProductCatalog.cs) with two classes: product and productCalalog.

Technorati :

Create a Schema for a .NET Class

Previous Post demonstrated how to use the XmlSerializer to serialize .NET objects to XML, and deserialize XML into .NET objects. But if you want to use the XML as a way to interact with other applications, business process, or non-Framework applications, you'll need an easy way to validate the XML before you attempt to deserialize it. You'll also need to define an XML schema document that defines the structure and data types used in your XML format, so that other applications can work with it. One quick solution is to generate an XML schema using the xsd.exe command-line utility.

The xsd.exe utility is included with the .NET Framework. If you've installed Microsoft Visual Studio .NET, you'll find it in a directory like C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin. The xsd.exe utility can generate schema documents from compiled assemblies. You simply need to supply the filename and indicate the class that represents the XML document with the /t:[TypeName] parameter.

For example, consider the ProductCatalog and Product classes shown in this post. You could create the XML schema for a product catalog with the following command line:

xsd Recipe5-09.exe /t:ProductCatalog

You need to specify only the ProductCatalog class on the command line because this class represents the actual XML document. The generated schema in this example will represent a complete product catalog, with contained product items. It will be given the default filename schema0.xsd. You can now use the XmlValidatingReader shown in this post to test whether the XML document can be successfully validated with the schema.

Technorati :

Use XML Serialization with Custom Objects

The XmlSerializer class allows you to convert objects to XML data, and vice versa. This process is used natively by Web services and provides a customizable serialization mechanism that won't require a single line of custom code. The XmlSerializer class is even intelligent enough to correctly create arrays when it finds nested elements.

The only requirements for using XmlSerializer are as follows:

  • The XmlSerializer only serializes properties and public variables.

  • The classes you want to serialize must include a default zero-argument constructor. The XmlSerializer uses this constructor when creating the new object during deserialization.

  • All class properties must be readable and writable. This is because XmlSerializer uses the property get accessor to retrieve information, and the property set accessor to restore the data after deserialization.

To use XML serialization, you must first mark up your data objects with attributes that indicate the desired XML mapping. These attributes are found in the System.Xml.Serialization namespace and include the following:

  • XmlRoot Specifies the name of the root element of the XML file. By default, XmlSerializer will use the name of the class. This attribute can be applied to the class declaration.

  • XmlElementIndicates the element name to use for a property or public variable. By default, XmlSerializer will use the name of the property or public variable.

  • XmlAttributeIndicates that a property or public variable should be serialized as an attribute, not an element, and specifies the attribute name.

  • XmlEnumConfigures the text that should be used when serializing enumerated values. If you don't use XmlEnum, the name of the enumerated constant will be used.

  • XmlIgnoreIndicates that a property or public variable should not be serialized.

Here's the class code that you might use:

using System;
using System.Xml.Serialization;

[XmlRoot("productCatalog")]
public class ProductCatalog {

    [XmlElement("catalogName")]
    public string CatalogName;
    
    // Use the date data type (and ignore the time portion in the 
    // serialized XML).
    [XmlElement(ElementName="expiryDate", DataType="date")]
    public DateTime ExpiryDate;
    
    // Configure the name of the tag that holds all products,
    // and the name of the product tag itself.
    [XmlArray("products")]
    [XmlArrayItem("product")]
    public Product[] Products;

    public ProductCatalog() {
        // Default constructor for deserialization.
    }

    public ProductCatalog(string catalogName, DateTime expiryDate) {
        this.CatalogName = catalogName;
        this.ExpiryDate = expiryDate;
    }
}

public class Product {

    [XmlElement("productName")]
    public string ProductName;
    
    [XmlElement("productPrice")]
    public decimal ProductPrice;
    
    [XmlElement("inStock")]
    public bool InStock;
    
    [XmlAttributeAttribute(AttributeName="id", DataType="integer")]
    public string Id;

    public Product() {
        // Default constructor for serialization.
    }

    public Product(string productName, decimal productPrice) {
        this.ProductName = productName;
        this.ProductPrice = productPrice;
    }
}

Notice that these classes use the XML serialization attributes to rename element names (using Pascal casing in the class member names, and camel casing in the XML tag names), indicate data types that aren't obvious, and specify how <product> elements will be nested in the <productCatalog>.

Using these custom classes and the XmlSerializer object, you can translate XML into objects and vice versa. Here's the code you would need to create a new ProductCatalog object, serialize the results to an XML document, deserialize the document back to an object, and then display the XML document.

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

public class SerializeXml {

    private static void Main() {

        // Create the product catalog.
        ProductCatalog catalog = new ProductCatalog("New Catalog",
          DateTime.Now.AddYears(1));
        Product[] products = new Product[2];
        products[0] = new Product("Product 1", 42.99m);
        products[1] = new Product("Product 2", 202.99m);
        catalog.Products = products;

        // Serialize the order to a file.
        XmlSerializer serializer = new XmlSerializer(typeof(ProductCatalog));
        FileStream fs = new FileStream("ProductCatalog.xml", FileMode.Create);
        serializer.Serialize(fs, catalog);
        fs.Close();

        catalog = null;

        // Deserialize the order from the file.
        fs = new FileStream("ProductCatalog.xml", FileMode.Open);
        catalog = (ProductCatalog)serializer.Deserialize(fs);

        // Serialize the order to the Console window.
        serializer.Serialize(Console.Out, catalog);
        Console.ReadLine();
    }
}

Technorati :

Validate an XML Document Against a Schema

An XML schema defines the rules that a given type of XML document must follow. The schema includes rules that define

  • the elements and attributes that can appear in a document.

  • the data types for elements and attributes.

  • the structure of a document, including what elements are children of other elements.

  • the order and number of child elements that appear in a document.

  • whether elements are empty, can include text, or require fixed values.

XML schema documents are beyond the scope of this chapter, but much can be learned from a simple example. This example uses the product catalog first presented in post1.

At its most basic level, XML Schema Definition (XSD) is used to define the elements that can occur in an XML document. XSD documents are themselves written in XML, and you use a separate predefined element (named <element>) in the XSD document to indicate each element that's required in the target document. The type attribute indicates the data type. Here's an example for a product name:

<xsd:element name="productName" type="xsd:string" />

And here's an example for the product price:

<xsd:element name="productPrice" type="xsd:decimal" />

The basic schema data types are defined at http://www.w3.org/TR/xmlschema-2. They map closely to .NET data types and include string, int, long, decimal, float, dateTime, boolean, and base64Binary, to name a few of the most frequently used types.

Both the productName and productPrice are simple types because they contain only character data. Elements that contain nested elements are called complex types. You can nest them together using a <sequence> tag, if order is important, or an <all> tag if it's not. Here's how you might model the <product> element in the product catalog. Notice that attributes are always declared after elements, and they aren't grouped with a <sequence> or <all> tag because order is never important.

<xsd:complexType name="product">
  <xsd:sequence>
    <xsd:element name="productName" type="xsd:string"/>
    <xsd:element name="productPrice" type="xsd:decimal"/>
    <xsd:element name="inStock" type="xsd:boolean"/>
  </xsd:sequence>
  <xsd:attribute name="id" type="xsd:integer"/>
</xsd:complexType>

By default, a listed element can occur exactly one time in a document. You can configure this behavior by specifying the maxOccurs and minOccurs attributes. Here's an example that allows an unlimited number of products in the catalog:

<xsd:element name="product" type="product" maxOccurs="unbounded" />

Here's the complete schema for the product catalog XML:

<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

   <!-- Define the complex type product. -->
   <xsd:complexType name="product">
      <xsd:sequence>
         <xsd:element name="productName" type="xsd:string"/>
         <xsd:element name="productPrice" type="xsd:decimal"/>
         <xsd:element name="inStock" type="xsd:boolean"/>
      </xsd:sequence>
      <xsd:attribute name="id" type="xsd:integer"/>
   </xsd:complexType>

   <!-- This is the structure the document must match.
        It begins with a productCatalog element that nests other elements. -->
   <xsd:element name="productCatalog">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="catalogName" type="xsd:string"/>
            <xsd:element name="expiryDate" type="xsd:date"/>

            <xsd:element name="products">
               <xsd:complexType>
                  <xsd:sequence>
                     <xsd:element name="product" type="product"
                      maxOccurs="unbounded" />
                  </xsd:sequence>
               </xsd:complexType>
            </xsd:element>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>

</xsd:schema>

The XmlValidatingReader class enforces all these schema rules-ensuring the document is valid-and it also checks that the XML document is well formed (which means there are no illegal characters, all opening tags have a corresponding closing tag, and so on). To check a document, you read through it one node at a time by calling the XmlValidatingReader.Read method. If an error is found, XmlValidatingReader raises a ValidationEventHandler event with information about the error. If you wish, you can handle this event and continue processing the document to find more errors. If you don't handle this event, an XmlException will be raised when the first error is encountered and processing will be aborted. To test only if a document is well formed, you can use the XmlValidatingReader without a schema.

The next example shows a utility class that displays all errors in an XML document when the ValidateXml method is called. Errors are displayed in a console window, and a final Boolean variable is returned to indicate the success or failure of the entire validation operation.

using System;
using System.Xml;
using System.Xml.Schema;

public class ConsoleValidator {

    // Set to true if at least one error exists.
    private bool failed;

    public bool Failed {
        get {return failed;}
    }

    public bool ValidateXml(string xmlFilename, string schemaFilename) {

        // Create the validator.
        XmlTextReader r = new XmlTextReader(xmlFilename);
        XmlValidatingReader validator = new XmlValidatingReader(r);
        validator.ValidationType = ValidationType.Schema;

        // Load the schema file into the validator.
        XmlSchemaCollection schemas = new XmlSchemaCollection();
        schemas.Add(null, schemaFilename);
        validator.Schemas.Add(schemas);

        // Set the validation event handler.
        validator.ValidationEventHandler += 
          new ValidationEventHandler(ValidationEventHandler);
            
        failed = false;
        try {

            // Read all XML data.
            while (validator.Read())
            {}
        }catch (XmlException err) {

            // This happens if the XML document includes illegal characters
            // or tags that aren't properly nested or closed.
            Console.WriteLine("A critical XML error has occurred.");
            Console.WriteLine(err.Message);
            failed = true;
        }finally {
            validator.Close();
        }

        return !failed;
    }

    private void ValidationEventHandler(object sender, 
      ValidationEventArgs args) {

        failed = true;

        // Display the validation error.
        Console.WriteLine("Validation error: " + args.Message);
        Console.WriteLine();
    }
}

Here's how you would use the class to validate the product catalog:

using System;

public class ValidateXml {

    private static void Main() {

        ConsoleValidator consoleValidator = new ConsoleValidator();
        Console.WriteLine("Validating ProductCatalog.xml.");

        bool success = consoleValidator.ValidateXml("ProductCatalog.xml",
          "ProductCatalog.xsd");
        if (!success) {
            Console.WriteLine("Validation failed.");
        }else {
            Console.WriteLine("Validation succeeded.");
        }

        Console.ReadLine();
    }
}

If the document is valid, no messages will appear, and the success variable will be set to true. But consider what happens if you use a document that breaks schema rules, such as the ProductCatalog_Invalid.xml file shown here:

<?xml version="1.0" ?>
<productCatalog>
    <catalogName>Acme Fall 2003 Catalog</catalogName>
    <expiryDate>Jan 1, 2004</expiryDate>

    <products>
        <product id="1001">
            <productName>Magic Ring</productName>
            <productPrice>$342.10</productPrice>
            <inStock>true</inStock>
        </product>
        <product id="1002">
            <productName>Flying Carpet</productName>
            <productPrice>982.99</productPrice>
            <inStock>Yes</inStock>
        </product>
    </products>
</productCatalog>

If you attempt to validate this document, the success variable will be set to false and the output will indicate each error:

Validating ProductCatalog_Invalid.xml.

Validation error: The 'expiryDate' element has an invalid value according to
 its data type. [path information truncated] 

Validation error: The 'productPrice' element has an invalid value according to
 its data type. [path information truncated]

Validation error: The 'inStock' element has an invalid value according to its
 data type. [path information truncated]

Validation failed.

Finally, if you want to validate an XML document and then process it, you can use XmlValidatingReader to scan a document as it's read into an in-memory XmlDocument. Here's how it works:

XmlDocument doc = new XmlDocument();
XmlTextReader r = new XmlTextReader("ProductCatalog.xml");
XmlValidatingReader validator = new XmlValidatingReader(r);

// Load the schema into the validator.
validator.ValidationType = ValidationType.Schema;
XmlSchemaCollection schemas = new XmlSchemaCollection();
schemas.Add(null, "ProductCatalog.xsd");
validator.Schemas.Add(schemas);

// Load the document and validate it at the same time.
// Don't handle the ValidationEventHandler event. Instead, allow any errors
/// to be thrown as an XmlSchemaException.
try {
    doc.Load(validator);
    // (Validation succeeded if you reach here.)
}catch (XmlSchemaException err) {
    // (Validation failed if you reach here.)
}

Technorati :

Read and Write XML Without Loading an Entire Document into Memory

The XmlTextWriter and XmlTextReader classes read or write XML directly from a stream one node at a time. These classes don't provide the same features for navigating and manipulating your XML as XmlDocument, but they do provide higher performance and a smaller memory footprint, particularly if you need to deal with extremely large XML documents.

To write XML to any stream, you can use the streamlined XmlTextWriter. It provides Write methods that write one node at a time. These include

  • WriteStartDocument, which writes the document prologue, and WriteEndDocument, which closes any open elements at the end of the document.

  • WriteStartElement, which writes an opening tag for the element you specify. You can then add more elements nested inside this element, or you can call WriteEndElement to write the closing tag.

  • WriteElementString, which writes an entire element, with an opening tag, a closing tag, and text content.

  • WriteAttributeString, which writes an entire attribute for the nearest open element, with a name and value.

Using these methods usually requires less code than creating an XmlDocument by hand, as demonstrated in posts1 and post2

To read the XML, you use the Read method of the XmlTextReader. This method advances the reader to the next node, and returns true. If no more nodes can be found, it returns false. You can retrieve information about the current node through XmlTextReader properties, including its Name, Value, and NodeType.

To find out if an element has attributes, you must explicitly test the HasAttributes property and then use the GetAttribute method to retrieve the attributes by name or index number. The XmlTextReader class can access only one node at a time, and it can't move backward or jump to an arbitrary node, which gives much less flexibility than the XmlDocument class.

The following console application writes and reads a simple XML document using the XmlTextWriter and XmlTextReader classes. This is the same XML document created in posts1 and post2 using the XmlDocument class.

using System;
using System.Xml;
using System.IO;
using System.Text;

public class ReadWriteXml {

    private static void Main() {

        // Create the file and writer.
        FileStream fs = new FileStream("products.xml", FileMode.Create);
        XmlTextWriter w = new XmlTextWriter(fs, Encoding.UTF8);

        // Start the document.
        w.WriteStartDocument();
        w.WriteStartElement("products");

        // Write a product.
        w.WriteStartElement("product");
        w.WriteAttributeString("id", "1001");
        w.WriteElementString("productName", "Gourmet Coffee");
        w.WriteElementString("productPrice", "0.99");
        w.WriteEndElement();

        // Write another product.
        w.WriteStartElement("product");
        w.WriteAttributeString("id", "1002");
        w.WriteElementString("productName", "Blue China Tea Pot");
        w.WriteElementString("productPrice", "102.99");
        w.WriteEndElement();

        // End the document.
        w.WriteEndElement();
        w.WriteEndDocument();
        w.Flush();
        fs.Close();

        Console.WriteLine("Document created. " +
         "Press Enter to read the document.");
        Console.ReadLine();

        fs = new FileStream("products.xml", FileMode.Open);
        XmlTextReader r = new XmlTextReader(fs);

        // Read all nodes.
        while (r.Read()) {
 
           if (r.NodeType == XmlNodeType.Element) {

                Console.WriteLine();
                Console.WriteLine("<" + r.Name + ">");

                if (r.HasAttributes) {

                    for (int i = 0; i < r.AttributeCount; i++) {
                        Console.WriteLine("\tATTRIBUTE: " +
                          r.GetAttribute(i));
                    }
                }
            }else if (r.NodeType == XmlNodeType.Text) {
                Console.WriteLine("\tVALUE: " + r.Value);
            }
        }
        Console.ReadLine();
    }
}

Technorati :

Find Elements with an XPath Search

The XmlNode class defines two methods that perform XPath searches: SelectNodes and SelectSingleNode. These methods operate on all contained child nodes. Because the XmlDocument inherits from XmlNode, you can call XmlDocument.SelectNodes to search an entire document.

For example, consider the following XML document, which represents an order for two items. This document includes text and numeric data, nested elements, and attributes, and so is a good way to test simple XPath expressions.

<?xml version="1.0"?>
<Order id="2004-01-30.195496">
  <Client id="ROS-930252034">
    <Name>Remarkable Office Supplies</Name>
  </Client>

  <Items>
    <Item id="1001">
      <Name>Electronic Protractor</Name>
      <Price>42.99</Price>
    </Item>
    <Item id="1002">
      <Name>Invisible Ink</Name>
      <Price>200.25</Price>
    </Item>
  </Items>
</Order>

Basic XPath syntax uses a path-like notation. For example, the path /Order/Items/Item indicates an <Item> element that is nested inside an <Items> element, which, in turn, in nested in a root <Order> element. This is an absolute path. The following example uses an XPath absolute path to find the name of every item in an order.

using System;
using System.Xml;

public class XPathSelectNodes {

    private static void Main() {

        // Load the document.
        XmlDocument doc = new XmlDocument();
        doc.Load("orders.xml");

        // Retrieve the name of every item.
        // This could not be accomplished as easily with the
        // GetElementsByTagName() method, because Name elements are
        // used in Item elements and Client elements, and so
        // both types would be returned.
        XmlNodeList nodes = doc.SelectNodes("/Order/Items/Item/Name");
            
        foreach (XmlNode node in nodes) {
            Console.WriteLine(node.InnerText);
        }
     
        Console.ReadLine();
    }
}

The output of this program is as follows:

Electronic Protractor
Invisible Ink

XPath provides a rich and powerful search syntax, and it's impossible to explain all the variations you can use in a short example. However, Table 5.1 outlines some of the key ingredients in more advanced XPath expressions and includes examples that show how they would work with the order document. For a more detailed reference, refer to the W3C XPath recommendation at http://www.w3.org/TR/xpath.

Table 5.1: XPath Expression Syntax

Expression

Description

/

Starts an absolute path that selects from the root node.

/Order/Items/Item selects all Item elements that are children of anItems element, which is itself a child of the root Order element.

//

Starts a relative path that selects nodes anywhere.

//Item/Name selects all the Name elements that are children of an Item element, regardless of where they appear in the document.

@

Selects an attribute of a node.

/Order/@id selects the attribute named id from the root Order element.

*

Selects any element in the path.

/Order/* selects both Items and Client nodes because both are contained by a root Order element.

|

Combines multiple paths.

/Order/Items/Item/Name|Order/Client/Name selects the Name nodes used to describe a Client and the Name nodes used to describe an Item.

.

Indicates the current (default) node.

If the current node is an Order, the expression ./Items refers to the related items for that order.

..

Indicates the parent node.

//Name/.. selects any element that is parent to a Name, which includes the Client and Item elements.

[ ]

Define selection criteria that can test a contained node or attribute value.

/Order[@id="2004-01-30.195496"] selects the Order elements with the indicated attribute value.

/Order/Items/Item[Price > 50] selects products above $50 in price.

/Order/Items/Item[Price > 50 and Name="Laser Printer"] selects products that match two criteria.

starts-with

This function retrieves elements based on what text a contained element starts with.

/Order/Items/Item[starts-with(Name, "C")] finds all Item elements that have a Name element that starts with the letter C.

position

This function retrieves elements based on position.

/Order/Items/Item[position ()=2] selects the second Item element.

count

This function counts elements. You specify the name of the child element to count or an asterisk (*) for all children.

/Order/Items/Item[count(Price) = 1] retrieves Item elements that have exactly one nested Price element.

Note

XPath expressions and all element and attribute names that you use inside them are always case sensitive, as XML itself is case sensitive.

Technorati :

Get XML Nodes in a Specific XML Namespace

Many XML documents contain nodes from more than one namespace. For example, an XML document that represents a scientific article might use a separate type of markup for denoting math equations and vector diagrams, or an XML document with information about a purchase order might aggregate client and order information with a shipping record. Similarly, an XML document that represents a business-to-business transaction might include portions from both companies, written in separate markup languages.

A common task in XML programming is to retrieve the elements that are found in a specific namespace. You can perform this task with the overloaded version of the XmlDocument.GetElementsByTagName method that requires a namespace name. You can use this method to find tags by name, or to find all the tags in the specified namespace if you supply an asterisk for the tag name parameter.

As an example, consider the following compound XML document that includes order and client information, in two different namespaces (http://mycompany/OrderML and http://mycompany/ClientML).

<?xml version="1.0" ?>
<ord:order xmlns:ord="http://mycompany/OrderML"
 xmlns:cli="http://mycompany/ClientML">

  <cli:client>
    <cli:firstName>Sally</cli:firstName>
    <cli:lastName>Sergeyeva</cli:lastName>
  </cli:client>

  <ord:orderItem itemNumber="3211"/>
  <ord:orderItem itemNumber="1155"/>

</ord:order>

Here's a simple console application that selects all the tags in the http://mycompany/OrderML namespace:

using System;
using System.Xml;

public class SelectNodesByNamespace {

    private static void Main() {

        // Load the document.
        XmlDocument doc = new XmlDocument();
        doc.Load("Order.xml");

        // Retrieve all order tags.
        XmlNodeList matches = doc.GetElementsByTagName("*",
          "http://mycompany/OrderML");

        // Display all the information.
        Console.WriteLine("Element \tAttributes");
        Console.WriteLine("******* \t**********");

        foreach (XmlNode node in matches) {

            Console.Write(node.Name + "\t");
            foreach (XmlAttribute attribute in node.Attributes) {
                Console.Write(attribute.Value + "  ");
            }
            Console.WriteLine();
        }
 
        Console.ReadLine();
    }
}

The output of this program is as follows:

Element         Attributes
*******         **********
ord:order       http://mycompany/OrderML  http://mycompany/ClientML
ord:orderItem   3211
ord:orderItem   1155

Technorati :

Find Specific Elements by Name

The XmlDocument class provides a convenient GetElementsByTagName method that searches an entire document for nodes that have the indicated element name. It returns the results as a collection of XmlNode objects.

This code demonstrates how you could use GetElementsByTagName to calculate the total price of items in a catalog by retrieving all elements with the name "productPrice":

using System;
using System.Xml;

public class FindNodesByName {

    private static void Main() {

        // Load the document.
        XmlDocument doc = new XmlDocument();
        doc.Load("ProductCatalog.xml");

        // Retrieve all prices.
        XmlNodeList prices = doc.GetElementsByTagName("productPrice");

        decimal totalPrice = 0;
        foreach (XmlNode price in prices) {

            // Get the inner text of each matching element.
            totalPrice += Decimal.Parse(price.ChildNodes[0].Value);
        }

        Console.WriteLine("Total catalog value: " + totalPrice.ToString());
        Console.ReadLine();
    }
}

You can also search portions of an XML document by using the XmlElement.GetElementsByTagName method. It searches all the descendant nodes looking for matches. To use this method, first retrieve an XmlNode that corresponds to an element. Then cast this object to an XmlElement. The following example demonstrates how to find the price node under the first product element.

// Retrieve a reference to the first product.
XmlNode product = doc.GetElementsByTagName("products")[0];

// Find the price under this product.
XmlNode price = ((XmlElement)product).GetElementsByTagName("productPrice")[0];
Console.WriteLine("Price is " + price.InnerText);

If your elements include an attribute of type ID, you can also use a method called GetElementById to retrieve an element that has a matching ID value.

Technorati :

Quickly Append Nodes in an XML Document

Inserting a single element into an XmlDocument requires several lines of code. There are several ways that you can shorten this code. One approach is to create a dedicated helper class with higher-level methods for adding elements and attributes. For example, you could create an AddElement method that generates a new element, inserts it, and adds any contained text-the three operations needed to insert most elements.

Here's an example of one such helper class:

using System;
using System.Xml;

public class XmlHelper {

    public static XmlNode AddElement(string tagName, 
      string textContent, XmlNode parent) {

        XmlNode node = parent.OwnerDocument.CreateElement(tagName);
        parent.AppendChild(node);

        if (textContent != null) {

            XmlNode content;
            content = parent.OwnerDocument.CreateTextNode(textContent);
            node.AppendChild(content);
        }
        return node;
    }

    public static XmlNode AddAttribute(string attributeName,
      string textContent, XmlNode parent) {

        XmlAttribute attribute;
        attribute = parent.OwnerDocument.CreateAttribute(attributeName);
        attribute.Value = textContent;
        parent.Attributes.Append(attribute);

        return attribute;
    }
}

You can now condense the XML-generating code from previous post with the simpler syntax shown here:

public class GenerateXml {

    private static void Main() {

        // Create the basic document.
        XmlDocument doc = new XmlDocument();
        XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
        doc.AppendChild(docNode);
        XmlNode products = doc.CreateElement("products");
        doc.AppendChild(products);

        // Add two products.
        XmlNode product = XmlHelper.AddElement("product", null, products);
        XmlHelper.AddAttribute("id", "1001", product);
        XmlHelper.AddElement("productName", "Gourmet Coffee", product);
        XmlHelper.AddElement("productPrice", "0.99", product);

        product = XmlHelper.AddElement("product", null, products);
        XmlHelper.AddAttribute("id", "1002", product);
        XmlHelper.AddElement("productName", "Blue China Tea Pot", product);
        XmlHelper.AddElement("productPrice", "102.99", product);

        // Save the document (to the Console window rather than a file).
        doc.Save(Console.Out);
        Console.ReadLine();
    }
}

Alternatively, you might want to take the helper methods such as AddAttribute and AddElement and make them instance methods in a custom class you derive from XmlDocument.

Another approach to simplifying writing XML is to duplicate nodes using the XmlNode.CloneNode method. CloneNode accepts a Boolean deep parameter. If you supply true, CloneNode will duplicate the entire branch, with all nested nodes.

Here's an example that creates a new product node by copying the first node.

// (Add first product node.)

// Create a new element based on an existing product.
product = product.CloneNode(true);

// Modify the node data.
product.Attributes[0].Value = "1002";
product.ChildNodes[0].ChildNodes[0].Value = "Blue China Tea Pot";
product.ChildNodes[1].ChildNodes[0].Value = "102.99";

// Add the new element.
products.AppendChild(product);

Notice that in this case, certain assumptions are being made about the existing nodes (for example, that the first child in the item node is always the name, and the second child is always the price). If this assumption isn't guaranteed to be true, you might need to examine the node name programmatically.

Technorati :

Insert Nodes in an XML Document

Inserting a node into the XmlDocument class is a two-step process. You must first create the node, and then you insert it at the appropriate location. Optionally, you can then call XmlDocument.Save to persist changes.

To create a node, you use one of the XmlDocument methods that start with the word Create, depending on the type of node. This ensures that the node will have the same namespace as the rest of the document. (Alternatively, you can supply a namespace as an additional string argument.) Next you must find a suitable related node and use one of its insertion methods to add the new node to the tree.

The following example demonstrates this technique by programmatically creating a new XML document.

using System;
using System.Xml;

public class GenerateXml {

    private static void Main() {

        // Create a new, empty document.
        XmlDocument doc = new XmlDocument();
        XmlNode docNode = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
        doc.AppendChild(docNode);

        // Create and insert a new element.
        XmlNode productsNode = doc.CreateElement("products");
        doc.AppendChild(productsNode);

        // Create a nested element (with an attribute).
        XmlNode productNode = doc.CreateElement("product");
        XmlAttribute productAttribute = doc.CreateAttribute("id");
        productAttribute.Value = "1001";
        productNode.Attributes.Append(productAttribute);
        productsNode.AppendChild(productNode);

        // Create and add the sub-elements for this product node
        // (with contained text data).
        XmlNode nameNode = doc.CreateElement("productName");
        nameNode.AppendChild(doc.CreateTextNode("Gourmet Coffee"));
        productNode.AppendChild(nameNode);
        XmlNode priceNode = doc.CreateElement("productPrice");
        priceNode.AppendChild(doc.CreateTextNode("0.99"));
        productNode.AppendChild(priceNode);

        // Create and add another product node.
        productNode = doc.CreateElement("product");
        productAttribute = doc.CreateAttribute("id");
        productAttribute.Value = "1002";
        productNode.Attributes.Append(productAttribute);
        productsNode.AppendChild(productNode);
        nameNode = doc.CreateElement("productName");
        nameNode.AppendChild(doc.CreateTextNode("Blue China Tea Pot"));
        productNode.AppendChild(nameNode);
        priceNode = doc.CreateElement("productPrice");
        priceNode.AppendChild(doc.CreateTextNode("102.99"));
        productNode.AppendChild(priceNode);

        // Save the document (to the Console window rather than a file).
        doc.Save(Console.Out);
        Console.ReadLine();
    }
}

The generated document looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<products>
  <product id="1001">
    <productName>Gourmet Coffee</productName>
    <productPrice>0.99</productPrice>
  </product>
  <product id="1002">
    <productName>Blue China Tea Pot</productName>
    <productPrice>102.99</productPrice>
  </product>
</products>

Connecting to a Password-Protected Access Database in ADO

Use the Jet OLEDB:Database Password attribute in the connection string to specify the password. The sample code contains a single event handler: Connect Button.Click Creates and opens a connection to a password-secured Microsoft Access database using the OLE DB .NET data provider. Information about the database is displayed from the properties of the OleDbConnection object. File: AccessPasswordForm.cs// Namespaces, variables, and constants using System; using System.Configuration; using System.Text; using System.Data; using System.Data.OleDb; // . . . private void connectButton_Click(object sender, System.EventArgs e) { StringBuilder result = new StringBuilder( ); // Build the connections string incorporating the password. String connectionString = ConfigurationSettings.AppSettings["MsAccess_Secure_ConnectString"]+ "Jet OLEDB:Database Password=" + passwordTextBox.Text + ";"; result.Append("ConnectionString: " + Environment.NewLine + connectionString + Environment.NewLine + Environment.NewLine); OleDbConnection conn = new OleDbConnection(connectionString); try { conn.Open( ); // Retrieve some database information. result.Append( "Connection State: " + conn.State + Environment.NewLine + "OLE DB Provider: " + conn.Provider + Environment.NewLine + "Server Version: " + conn.ServerVersion + Environment.NewLine); conn.Close( ); result.Append("Connection State: " + conn.State); } catch(System.Data.OleDb.OleDbException ex) { conn.Close( ); result.Append("ERROR: " + ex.Message); } resultTextBox.Text = result.ToString( ); } A Microsoft Access database password requires that users enter a password to obtain access to the database and database objects. This is also known as share-level security. A password does not allow groups or users to have distinct levels of access or permissions. Anyone with the password has unrestricted access to the database. The Set Database command from the Tools Security menu is used to set up a database password. The OLE DB provider for Microsoft Jet has several provider-specific connection string attributes in addition to those defined by ADO.NET. To open a database secured by a Microsoft Access database password, use the Jet OLEDB:Database Password attribute in the connection string to specify the password. This corresponds to the OLE DB property DBPROP_JETOLEDB_DATABASEPASSWORD.

Connecting to a Microsoft Excel Workbook in ADO

Use the OLE DB Jet provider to create, access, and modify data stored in an Excel workbook. The sample code contains two event handlers: Form.Load Creates an OleDbDataAdapter that uses the Jet OLE DB provider to access an Excel workbook. Custom insert and update logic is created for the DataAdapter. A DataTable is filled from the first worksheet, Sheet1, in the Excel workbook and the default view of the table is bound to a data grid on the form. Update Button.Click Uses the DataAdapter created in the Form.Load event handler to update the Excel workbook with the programmatic changes. File: ExcelForm.cs// Namespaces, Variables, and Constants using System; using System.Configuration; using System.Data; private OleDbDataAdapter da; private DataTable dt; // . . . private void ExcelForm_Load(object sender, System.EventArgs e) { // Create the DataAdapter. da = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", ConfigurationSettings.AppSettings["Excel_0115_ConnectString"]); // Create the insert command. String insertSql = "INSERT INTO [Sheet1$] " + "(CategoryID, CategoryName, Description) " + "VALUES (?, ?, ?)"; da.InsertCommand = new OleDbCommand(insertSql, da.SelectCommand.Connection); da.InsertCommand.Parameters.Add("@CategoryID", OleDbType.Integer, 0,"CategoryID"); da.InsertCommand.Parameters.Add("@CategoryName", OleDbType.Char, 15, "CategoryName"); da.InsertCommand.Parameters.Add("@Description", OleDbType.VarChar, 100,"Description"); // Create the update command. String updateSql = "UPDATE [Sheet1$] " + "SET CategoryName=?, Description=? " + "WHERE CategoryID=?"; da.UpdateCommand =new OleDbCommand(updateSql, da.SelectCommand.Connection); da.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.Char, 15,"CategoryName"); da.UpdateCommand.Parameters.Add("@Description", OleDbType.VarChar, 100, "Description"); da.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer, 0, "CategoryID"); // Fill the table from the Excel spreadsheet. dt = new DataTable( ); da.Fill(dt); // Define the primary key. dt.PrimaryKey = new DataColumn[] {dt.Columns[0]}; // Records can only be inserted using this technique. dt.DefaultView.AllowDelete = false; dt.DefaultView.AllowEdit = true; dt.DefaultView.AllowNew = true; // Bind the default view of the table to the grid. dataGrid.DataSource = dt.DefaultView; } private void updateButton_Click(object sender, System.EventArgs e) { da.Update(dt); } You can use the Jet OLE DB provider to access Microsoft Excel as a data source. The Jet database engine can access other database file formats through Indexed Sequential Access Method (ISAM) drivers specified in the Extended Properties attribute of the connection. Excel 2000 and 2002 are supported with the Excel 8.0 source database type. :Provider=Microsoft.Jet.OLEDB.4.0;Data Source=myBook.xls; Extended Properties="Excel 8.0;HDR=YES"; The Extended Properties attribute can, in addition to the ISAM version property, specify whether or not tables include headers as field names in the first row of a range using an HDR attribute. There are three ways in which you can reference Excel workbook data within a SQL statement: Specify the worksheet name followed by a dollar sign to access the entire range used in the worksheet:SELECT * FROM [MySheet$] Specify a range explicitly using cells:SELECT * FROM [MySheet$A1:E5] Specify a range with a defined name, as shown in the solution:SELECT * FROM MyRange The following subsections discuss how to use Excel as an ADO.NET data source. Create table The CREATE TABLE command will create a table in an Excel workbook. The workbook for the connection will be created if it does not exist. For example:CREATE TABLE MySheet (Field1 char(10), Field2 float, Field3 date) Create data You can use the INSERT command, either static or parameterized, to insert data into a worksheet or range:INSERT INTO [MySheet$] (Field1, Field2, Field3) VALUES ('testdata', 1.234, '09/28/1979'); Retrieve data Use either a DataAdapter or a DataReader to retrieve data from an Excel workbook. Create a SQL SELECT statement referencing a worksheet or a range in an Excel workbook and execute the statement to fill a DataSet using a DataAdapter or to create a DataReader. For example:SELECT * FROM [MySheet$] Update data The UPDATE command, either static or parameterized, can update data in a worksheet or range. For example:UPDATE [MySheet$] SET Field2 = '2.345', Field3 = '10/18/1964' WHERE Field1 = 'testdata' Delete data The Jet OLE DB provider does not allow DELETE operations. An error will be raised if an attempt is made to execute a DELETE statement affecting one or more records.

Connecting to an ODBC Data Source in ADO

Use the ODBC .NET data provider to access data exposed through an ODBC driver.
The sample code contains a single event handler:
Connect Button.Click
Creates an OdbcDataAdapter and uses it to fill a DataTable with the Category table from the Northwind sample database. The default view of the table is bound to a data grid on the form.

 OdbcConnectForm.cs// Namespaces, variables, and constants
using System;
using System.Configuration;
using System.Data;
using System.Data.Odbc;
// . . .
private void connectButton_Click(object sender, System.EventArgs e)
{
// Create the DataAdapter.
String sqlSelect = "SELECT CategoryID, CategoryName, Description " +
"FROM Categories";
OdbcDataAdapter da = new OdbcDataAdapter(sqlSelect,
ConfigurationSettings.AppSettings["Odbc_ConnectString"]);
// Create the table, fill it, and bind the default view to the grid.
DataTable dt = new DataTable( );
da.Fill(dt);
dataGrid.DataSource = dt.DefaultView;
}
Discussion
The ODBC .NET data provider communicates with native ODBC drivers through COM interop (.NET's interoperability layer for COM). The following ODBC providers are guaranteed to be compatible with the ODBC.NET data provider:
Microsoft SQL Server ODBC Driver
Microsoft ODBC Driver for Oracle
Microsoft Access (Jet) ODBC Driver
The .NET data provider for ODBC connects to ODBC data sources through the OdbcConnection object. The ODBC driver connection string is specified using the ConnectionString property. It includes settings needed to establish the connection to the data source. The connection string format closely matches the ODBC connection string format. Additionally, you can specify an ODBC data source name (DSN) or file DSN by setting the ConnectionString attribute to "DSN=myDSN" or "FileDSN=myFileDSN". For more information about specifying ODBC connection strings, see the topic "SQLDriverConnect" in the ODBC Programmer's Reference within MSDN Library.
Visual Studio also supports creating ODBC data source connections visually:
Create a data connection in Server Explorer and drag it onto a form or design surface. Configure the OdbcConnection object that appears in the component tray.
Drag an OdbcConnection from the Data tab of the Toolbox onto a form or design surface. Configure the ConnectionString property in the Properties window of the OdbcConnection object that appears.
The .NET ODBC data provider requires a reference to the System.Data.Odbc namespace in .NET Framework Version 1.1. In Version 1.0, the namespace is Microsoft.Data.Odbc. Add a .NET Reference to Microsoft.Data.Odbc.dll for a .NET Framework Version 1.0 project.

Ensure That Only One Instance of an Application Can Execute Concurrently

The Mutex provides a mechanism for synchronizing the execution of threads across process boundaries and in addition provides a convenient mechanism through which to ensure that only a single instance of an application is running concurrently. By trying to acquire ownership of a named Mutex at startup and exiting if the Mutex can't be acquired, you can ensure that only one instance of your application is running. This example uses a Mutex named MutexExample to ensure that only a single instance of the example can execute.
using System;
using System.Threading;

public class MutexExample {

    public static void Main() {

        // A boolean that indicates whether this application has
        // initial ownership of the Mutex.
        bool ownsMutex;

        // Attempt to create and take ownership of a Mutex named
        // MutexExample.
        using (Mutex mutex = 
                   new Mutex(true, "MutexExample", out ownsMutex)) {

            // If the application owns the Mutex it can continue to execute;
            // otherwise, the application should exit. 
            if (ownsMutex) {

                Console.WriteLine("This application currently owns the" +
                    " mutex named MutexExample. Additional instances of" +
                    " this application will not run until you release" +
                    " the mutex by pressing Enter.");

                Console.ReadLine();

                // Release the mutex
                mutex.ReleaseMutex();

            } else {

                Console.WriteLine("Another instance of this application " +
                    " already owns the mutex named MutexExample. This" +
                    " instance of the application will terminate.");
            }
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Start a New Process

The Process class provides a managed representation of an operating system process and provides a simple mechanism through which you can execute both managed and unmanaged applications. The Process class implements four overloads of the Start method, which you use to start a new process. Two of these overloads are static methods that allow you to specify only the name and arguments for the new process. For example, the following statements both execute Notepad in a new process:
// Execute notepad.exe with no command-line arguments.
Process.Start("notepad.exe");

// Execute notepad.exe passing the name of the file to open as a 
// command-line argument.
Process.Start("notepad.exe", "SomeFile.txt");
 
The other two Start method overloads require you to create a ProcessStartInfo object configured with the details of the process you want to run; use of the ProcessStartInfo object provides greater control over the behavior and configuration of the new process.

Properties of the ProcessStartInfo Class
Property
Description
Arguments
The command-line arguments to pass to the new process.
ErrorDialog
If Process.Start can't start the specified process, it will throw a System.ComponentModel.Win32Exception. If ErrorDialog is true, Start displays an error dialog to the user before throwing the exception.
FileName
The name of the application to start. You can also specify any type of file for which you have configured an application association. For example, you could specify a file with a .doc or .xls extension, which would cause Microsoft Word or Microsoft Excel to run.
WindowStyle
A member of the System.Diagnostics.ProcessWindowStyle enumeration, which controls how the window is displayed. Valid values include Hidden, Maximized, Minimized, and Normal.
WorkingDirectory
The fully qualified name of the initial directory for the new process.
When finished with a Process object, you should dispose of it in order to release system resources—call Close, Dispose, or create the Process object within the scope of a using statement. Disposing of a Process object has no effect on the underlying system process, which will continue to run.
The following example uses Process to execute Notepad in a maximized window and open a file named C:\Temp\file.txt. After creation, the example calls the Process.WaitForExit method, which blocks the calling thread until a process terminates or a specified time-out expires.
using System;
using System.Diagnostics;

public class StartProcessExample {

    public static void Main () {

        // Create a ProcessStartInfo object and configure it with the 
        // information required to run the new process.
        ProcessStartInfo startInfo = new ProcessStartInfo();

        startInfo.FileName = "notepad.exe";
        startInfo.Arguments = "file.txt";
        startInfo.WorkingDirectory = @"C:\Temp";
        startInfo.WindowStyle = ProcessWindowStyle.Maximized;
        startInfo.ErrorDialog = true;

        // Create a new Process object.
        using (Process process = new Process()) {

            // Assign the ProcessStartInfo to the Process.
            process.StartInfo = startInfo;

            try {

                // Start the new process.
                process.Start();

                // Wait for the new process to terminate before exiting.
                Console.WriteLine("Waiting 30 seconds for process to" +
                    " finish.");
                process.WaitForExit(30000);

            } catch (Exception ex) {

                Console.WriteLine("Could not start process.");
                Console.WriteLine(ex);
            }
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Terminate a Process

If you start a new process from managed code using the Process class , you can terminate the process using the Process object that represents the new process. You can also obtain Process objects that refer to other currently running processes using the static methods of the Process class summarized Here
 
Methods for Obtaining Process References
Method
Description
GetCurrentProcess
Returns a Process object representing the currently active process.
GetProcessById
Returns a Process object representing the process with the specified ID.
GetProcesses
Returns an array of Process objects representing all currently active processes.
GetProcessesByName
Returns an array of Process objects representing all currently active processes with a specified friendly name. The friendly name is the name of the executable excluding file extension or path; for example, notepad or calc.
Once you have a Process object representing the process that you want to terminate, you need to call either the CloseMainWindow method or the Kill method. The CloseMainWindow method sends a close message to a Windows- based application's main window. This method has the same effect as if the user had closed the main window using the system menu, and it gives the application the opportunity to perform its normal shutdown routine. CloseMainWindow won't terminate applications that do not have a main window or applications with a disabled main window—possibly because a modal dialog box is currently displayed. Under such circumstances, CloseMainWindow will return false.
CloseMainWindow returns true if the close message was successfully sent, but doesn't guarantee that the process is actually terminated. For example, applications used to edit data will usually give the user the opportunity to save unsaved data if a close message is received. The user usually has the chance to cancel the close operation under such circumstances. This means that CloseMainWindow will return true, but the application will still be running once the user cancels. You can use the Process.WaitForExit method to signal process termination and the Process.HasExited property to test if a process has terminated. Alternatively, you can use the Kill method.
The Kill method simply terminates a process immediately; the user has no chance to stop the termination, and all unsaved data is lost. Kill is the only option for terminating Windows-based applications that do not respond to CloseMainWindow and for terminating non-Windows-based applications.
The following example starts a new instance of Notepad, waits five seconds, and then terminates the Notepad process. The example first tries to terminate the process using CloseMainWindow. If CloseMainWindow returns false, or the Notepad process is still running after CloseMainWindow is called, the example calls Kill and forces the Notepad process to terminate; you can force CloseMainWindow to return false by leaving the File Open dialog box open.
using System;
using System.Threading;
using System.Diagnostics;

public class TerminateProcessExample {

    public static void Main () {

        // Create a new Process and run notepad.exe.
        using (Process process = Process.Start("notepad.exe")) {

            // Wait for 5 seconds and terminate the notepad process.
            Console.WriteLine("Waiting 5 seconds before terminating" +
                " notepad.exe.");
            Thread.Sleep(5000);

            // Terminate notepad process.
            Console.WriteLine("Terminating Notepad with CloseMainWindow.");

            // Try to send a close message to the main window.
            if (!process.CloseMainWindow()) {

                // Close message did not get sent - Kill Notepad.
                Console.WriteLine("CloseMainWindow returned false - " +
                    " terminating Notepad with Kill.");
                process.Kill();

            } else {

                // Close message sent successfully; wait for 2 seconds
                // for termination confirmation before resorting to Kill.
                if (!process.WaitForExit(2000)) {

                    Console.WriteLine("CloseMainWindow failed to" +
                        " terminate - terminating Notepad with Kill.");
                    process.Kill();
                }
            }
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Create a Thread-Safe Collection Instance

By default, the standard collection classes from the System.Collections and System.Collections.Specialized namespaces will support multiple threads reading the collection's content concurrently. However, if one or more of these threads tries to modify the collection, you will almost certainly encounter problems. This is because the operating system can interrupt the actions of the thread while modifications to the collection have been only partially applied. This leaves the collection in an indeterminate state, which will almost certainly cause another thread accessing the collection to fail, return incorrect data, or corrupt the collection.

The most commonly used collections all implement a static method named Synchronized; this includes the ArrayList, Hashtable, Queue, SortedList, and Stack classes from the System.Collections namespace. The Synchronized method takes a collection object of the appropriate type as an argument and returns an object that provides a synchronized wrapper around the specified collection object. The wrapper object is returned as the same type as the original collection, but all of the methods and properties that read and write the collection ensure that only a single thread has access to the collection content concurrently. The following code shows how to create a thread-safe Hashtable. (You can test if a collection is thread-safe using the IsSynchronized property.)
// Create a standard Hashtable
Hashtable hUnsync = new Hashtable();

// Create a synchronized wrapper
Hashtable hSync = Hashtable.Synchronized(hUnsync);
The collection classes such as HybridDictionary, ListDictionary, and StringCollection from the System.Collections.Specialized namespace don't implement a Synchronized method. To provide thread-safe access to instances of these classes, you must implement manual synchronization using the object returned by their SyncRoot property, as shown in the following code:
// Create a NameValueCollection.
NameValueCollection nvCollection = new NameValueCollection();

// Obtain a lock on the NameValueCollection before modification.
lock (((ICollection)nvCollection).SyncRoot) {

    // Modify the NameValueCollection...
}
 
Notice that the NameValueCollection class derives from the NameObjectCollectionBase class, which uses explicit interface implementation to implement the ICollection.SyncRoot property. As shown, you must cast the NameValueCollection to an ICollection instance before you can access the SyncRoot property. Casting is not necessary with other specialized collection classes such as HybridDictionary, ListDictionary, and StringCollection, which do not use explicit interface implementation to implement SyncRoot.
If you need to use the synchronized collection class extensively, you can simplify your code by creating a new class that derives from the collection class you need to use. Override the members of the base class that provide access to the collection's content and perform synchronization before calling the equivalent base class member. You would normally use the lock statement to synchronize on the object returned by the SyncRoot property of the base class as discussed previously. However, by creating a derived class it's also possible to implement more advanced synchronization techniques, such as using the System.Threading.ReaderWriterLock to allow multiple reader threads but only a single writer thread.

Synchronize the Execution of Multiple Threads

The greatest challenge in writing a multithreaded application is ensuring that the threads work in concert. This is commonly referred to as thread synchronization and includes
  • Ensuring threads access shared objects and data correctly so that they do not cause corruption.
  • Ensuring threads execute only when they are meant to and cause minimum overhead when they are idle.
The most commonly used synchronization mechanism is the Monitor class. The Monitor class allows a single thread to obtain an exclusive lock on an object by calling the static method Monitor.Enter. By acquiring an exclusive lock prior to accessing a shared resource or data, you ensure that only one thread can access the resource concurrently. Once the thread has finished with the resource, release the lock to allow another thread to access it. A block of code that enforces this behavior is often referred to as a critical section.
You can use any object to act as the lock, and it's common to use the keyword this in order to obtain a lock on the current object. The key point is that all threads attempting to access a shared resource must try to acquire the same lock. Other threads that attempt to acquire a lock on the same object will block (enter a WaitSleepJoin state) and are added to the lock's ready queue until the thread that owns the lock releases it by calling the static method Monitor.Exit. When the owning thread calls Exit, one of the threads from the ready queue acquires the lock. If the owner of a lock does not release it by calling Exit, all other threads will block indefinitely. Therefore, it's important to place the Exit call within a finally block to ensure that it's called even if an exception occurs.
Because Monitor is used so frequently in multithreaded applications, C# provides language-level support through the lock statement, which the compiler translates to use of the Monitor class. A block of code encapsulated in a lock statement is equivalent to calling Monitor.Enter when entering the block, and Monitor.Exit when exiting the block. In addition, the compiler automatically places the Monitor.Exit call in a finally block to ensure that the lock is released if an exception is thrown.
The thread that currently owns the lock can call Monitor.Wait, which will release the lock and place the calling thread on the lock's wait queue. Threads in a wait queue also have a state of WaitSleepJoin and will continue to block until a thread that owns the lock calls either of the Pulse or PulseAll methods of the Monitor class. Pulse moves one of the waiting threads from the wait queue to the ready queue, whereas PulseAll moves all threads. Once a thread has moved from the wait queue to the ready queue, it can acquire the lock next time it's released. It's important to understand that threads on a lock's wait queue will not acquire a released lock; they will wait indefinitely until you call Pulse or PulseAll to move them to the ready queue. The use of Wait and Pulse is a common approach when a pool of threads is used to process work items from a shared queue.
The ThreadSyncExample class shown here demonstrates the use of both the Monitor class and the lock statement. The example starts three threads that each (in turn) acquire the lock to an object named consoleGate. Each thread then calls Monitor.Wait. When the user presses the Enter key the first time, Monitor.Pulse is called to release one waiting thread. The second time the user presses Enter, Monitor.PulseAll is called, releasing all remaining waiting threads.
using System;
using System.Threading;

public class ThreadSyncExample {

    // Declare a static object to use for locking in static methods 
    // because there is no access to 'this'.
    private static object consoleGate = new Object();

    private static void DisplayMessage() {

        Console.WriteLine("{0} : Thread started, acquiring lock...",
            DateTime.Now.ToString("HH:mm:ss.ffff"));

        // Acquire a lock on the consoleGate object.
        try {

            Monitor.Enter(consoleGate);

            Console.WriteLine("{0} : {1}",
                DateTime.Now.ToString("HH:mm:ss.ffff"),
                "Acquired consoleGate lock, waiting...");

            // Wait until Pulse is called on the consoleGate object.
            Monitor.Wait(consoleGate);

            Console.WriteLine("{0} : Thread pulsed, terminating.",
                DateTime.Now.ToString("HH:mm:ss.ffff"));

        } finally {

            Monitor.Exit(consoleGate);
        }
    }

    public static void Main() {

        // Acquire a lock on the consoleGate object.
        lock (consoleGate) {

            // Create and start three new threads running the 
            // DisplayMesssage method.
            for (int count = 0; count < 3; count++) {

                (new Thread(new ThreadStart(DisplayMessage))).Start();
            }
        }

        Thread.Sleep(1000);

        // Wake up a single waiting thread.
        Console.WriteLine("{0} : {1}", 
            DateTime.Now.ToString("HH:mm:ss.ffff"),
            "Press Enter to pulse one waiting thread.");

        Console.ReadLine();

        // Acquire a lock on the consoleGate object.
        lock (consoleGate) {
            
            // Pulse 1 waiting thread.
            Monitor.Pulse(consoleGate);
        }

        // Wake up all waiting threads.
        Console.WriteLine("{0} : {1}",  
            DateTime.Now.ToString("HH:mm:ss.ffff"),
            "Press Enter to pulse all waiting threads.");

        Console.ReadLine();

        // Acquire a lock on the consoleGate object.
        lock (consoleGate) {
            
            // Pulse all waiting threads.
            Monitor.PulseAll(consoleGate);
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}
Other classes commonly used to provide synchronization between threads are the subclasses of the System.Threading.WaitHandle class. These include the AutoResetEvent, ManualResetEvent, and Mutex. Instances of these classes can be in either a signaled or unsignaled state.
WaitHandle Methods for Synchronizing Thread Execution
Method
Description
WaitAny
A static method that causes the calling thread to enter a WaitSleepJoin state and wait for any one of the WaitHandle objects in a WaitHandle array to be signaled. You can also specify a time-out value.
WaitAll
A static method that causes the calling thread to enter a WaitSleepJoin state and wait for all the WaitHandle objects in a WaitHandle array to be signaled. You can also specify a time-out value.
WaitOne
Causes the calling thread to enter a WaitSleepJoin state and wait for a specific WaitHandle object to be signaled.
The key differences between the AutoResetEvent, ManualResetEvent, and Mutex classes are how they transition from a signaled to an unsignaled state, and their visibility. The AutoResetEvent and ManualResetEvent classes are local to a process. To signal an AutoResetEvent, call its Set method, which will release only one thread that is waiting on the event. The AutoResetEvent will automatically return to an unsignaled state.The ManualResetEvent class must be manually switched back and forth between signaled to unsignaled using its Set and Reset methods. Calling Set on a ManualResetEvent will set it to a signaled state, releasing all threads that are waiting on the event. Only by calling Reset does the ManualResetEvent become unsignaled.
A Mutex is signaled when it's not owned by any thread. A thread acquires ownership of the Mutex at construction . Ownership of the Mutex is released by calling the Mutex.ReleaseMutex method, which signals the Mutex and allows another thread to gain ownership. The key benefit of a Mutex is that you can use them to synchronize threads across process boundaries; a key difference between the WaitHandle classes and the Monitor class is that Monitor is implemented completely in managed code, whereas the WaitHandle classes provide wrappers around operating system primitives. This has the following consequences:
  • Use of the Monitor class means your code is more portable because you are not dependent on the capabilities of the underlying operating system.
  • You can use the classes derived from WaitHandle to synchronize the execution of both managed and unmanaged threads, whereas Monitor can synchronize only managed threads

How To Know When a Thread Finishes

The easiest way to test if a thread has finished executing is to test the Thread.IsAlive property. The IsAlive property returns true if the thread has been started but has not terminated or been aborted.
Commonly, you will need one thread to wait for another thread to complete its processing. Instead of testing IsAlive in a loop, you can use the Thread.Join method. Join causes the calling thread to block until the referenced thread terminates, at which point the calling thread will continue. You can optionally specify an int or a TimeSpan value that specifies the time after which the Join operation will time out and execution of the calling thread will resume. If you specify a time-out value, Join returns true if the thread terminated, and false if Join timed out.
The following example executes a second thread and then calls Join to wait for the second thread to terminate. Because the second thread takes around five seconds to execute, but the Join method specifies a time-out of three seconds, Join will always time out and the example will display a message to the console.
using System;
using System.Threading;

public class ThreadFinishExample {

    private static void DisplayMessage() {

        // Display a message to the console 5 times.
        for (int count = 0; count < 5; count++) {

            Console.WriteLine("{0} : Second thread",  
                DateTime.Now.ToString("HH:mm:ss.ffff"));

            // Sleep for 1 second.
            Thread.Sleep(1000);
        }
    }

    public static void Main() {

        // Create a ThreadStart delegate instance that references
        // DisplayMessage.
        ThreadStart method = new ThreadStart(DisplayMessage);

        // Create a new Thread object and pass the ThreadStart
        // delegate instance to its constructor.
        Thread thread = new Thread(method);

        Console.WriteLine("{0} : Starting second thread.",  
            DateTime.Now.ToString("HH:mm:ss.ffff"));

        // Start the second thread.
        thread.Start();

        // Block until the second thread finishes, or time out after
        // 3 seconds.
        if (!thread.Join(3000)) {

            Console.WriteLine("{0} : Join timed out !!",  
                DateTime.Now.ToString("HH:mm:ss.ffff"));
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Control the Execution of a Thread

The methods of the Thread class provide a high degree of control over the execution of a thread. Each of these methods returns to the calling thread immediately. However, the current thread state plays an important role in the result of the method call, and the state of a thread can change rapidly. As a result, you must code defensively to catch and handle the different exceptions that can be thrown when you try to control the execution of a Thread.
Controlling the Execution of a Thread
Method
Description
Abort
Terminates a thread by throwing a System.Threading.ThreadAbortException in the code that the thread is running. The aborted thread's code can catch the ThreadAbortException to perform cleanup, but the runtime will automatically throw the exception again to ensure that the thread terminates, unless ResetAbort is called. Abort returns immediately, but the runtime determines exactly when the exception is thrown, so you can't assume the thread has terminated by Abort returns.
Interrupt
Throws a System.Threading.ThreadInterruptedException in the code that the thread is running as long as the thread is currently in a WaitSleepJoin state. This means that the thread has called Sleep, Join or is waiting to be signaled by a WaitHandle or acquire an object used for thread synchronization . If the thread is not in the WaitSleepJoin state, ThreadInterruptedException is thrown the next time the thread does enter the WaitSleepJoin state.
Resume
Resumes the execution of a suspended thread.  Calling Resume on a thread that isn't suspended generates a System.Threading.ThreadStateException in the calling thread.
Start
Starts the execution of a new thread;
Suspend
Suspends the execution of a thread until the Resume method is called. Suspending an already suspended thread has no effect, but calling Suspend on a thread that hasn't started or is already finished will generate a ThreadStateException in the calling thread.
The example starts a second thread that periodically displays a message to the console and then goes to sleep. By entering commands at the command prompt, you can interrupt, suspend, resume, and abort the secondary thread.
using System;
using System.Threading;

public class ThreadControlExample {

    private static void DisplayMessage() {

        // Repeatedly display a message to the console.
        while (true) {

            try {

                Console.WriteLine("{0} : Second thread running. Enter"
                    + " (S)uspend, (R)esume, (I)nterrupt, or (E)xit.",
                    DateTime.Now.ToString("HH:mm:ss.ffff"));

                // Sleep for 2 seconds.
                Thread.Sleep(2000);

            } catch (ThreadInterruptedException) {

                // Thread has been interrupted. Catching the 
                // ThreadInterruptedException allows the example to 
                // take appropriate action and continue execution.
                Console.WriteLine("{0} : Second thread interrupted.",
                    DateTime.Now.ToString("HH:mm:ss.ffff"));

            } catch (ThreadAbortException abortEx) {

                // The object in the ThreadAbortException.ExceptionState
                // property is provided by the thread that called 
                // Thread.Abort. In this case it contains a string that 
                // describes the reason for the abort.
                Console.WriteLine("{0} : Second thread aborted ({1})",  
                    DateTime.Now.ToString("HH:mm:ss.ffff"),
                    abortEx.ExceptionState);

                // Even though ThreadAbortException has been handled, the
                // runtime will throw it again to ensure the thread 
                // terminates.
            }
        }
    }

    public static void Main() {

        // Create a new Thread object and pass it a ThreadStart
        // delegate instance that references DisplayMessage.
        Thread thread = new Thread(new ThreadStart(DisplayMessage));

        Console.WriteLine("{0} : Starting second thread.",  
            DateTime.Now.ToString("HH:mm:ss.ffff"));

        // Start the second thread.
        thread.Start();

        // Loop and process the command entered by the user.
        char command = ' ';

        do {

            string input = Console.ReadLine();
            if (input.Length > 0) command = input.ToUpper()[0];
            else command = ' ';

            switch (command) {

                case 'S':
                    // Suspend the second thread.
                    Console.WriteLine("{0} : Suspending second thread.",
                        DateTime.Now.ToString("HH:mm:ss.ffff"));
                    thread.Suspend();
                    break;

                case 'R':
                    // Resume the second thread.
                    try { 
                        Console.WriteLine("{0} : Resuming second thread.",
                            DateTime.Now.ToString("HH:mm:ss.ffff"));
                        thread.Resume();
                    } catch (ThreadStateException) {
                        Console.WriteLine("{0} : Thread wasn't suspended.",
                            DateTime.Now.ToString("HH:mm:ss.ffff"));
                    }
                    break;

                case 'I':
                    // Interrupt the second thread.
                    Console.WriteLine("{0} : Interrupting second thread.",
                        DateTime.Now.ToString("HH:mm:ss.ffff"));
                    thread.Interrupt();
                    break;

                case 'E':
                    // Abort the second thread and pass a state object to
                    // the thread being aborted, in this case a message.
                    Console.WriteLine("{0} : Aborting second thread.",  
                        DateTime.Now.ToString("HH:mm:ss.ffff"));

                    thread.Abort("Terminating example.");

                    // Wait for the second thread to terminate.
                    thread.Join();
                    break;
            }
        } while (command != 'E');

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Execute a Method Using a New Thread

For maximum control and flexibility when creating multithreaded applications, you need to take a direct role in the creation and management of threads. This is the most complex approach to multithreaded programming, but it's the only way to overcome the restrictions and limitations inherent in the approaches using thread-pool threads, as discussed in the four preceding recipes. The Thread class provides the mechanism through which you create and control threads. To create and start a new thread, follow this process:



  1. Create a ThreadStart delegate instance that references the method that contains the code you want the new thread to run. As with any delegate, ThreadStart can reference a static method or an instance method. The referenced method must take no arguments and return void.



  2. Create a new Thread object, and pass the ThreadStart delegate instance as an argument to the Thread constructor. The new thread has an initial state of Unstarted (a member of the System.Threading.ThreadState enumeration).



  3. Call Start on the Thread object, which changes its state to ThreadState.Running and begins execution of the method referenced by the ThreadStart delegate instance. (If you call Start more than once, it will throw a System.Threading.ThreadStateException.)
Because the ThreadStart delegate declares no arguments, you can't pass data directly to the referenced method. To pass data to a new thread, you must configure the data such that it's accessible to the code running in the new thread. The most common approach is to declare a class that encapsulates both the data required by the thread and the method executed by the thread. When you want to start a new thread, create an instance of the container object, configure its state, and then start the new thread. Here is an example.
using System;
using System.Threading;

public class ThreadExample {

    // Private member variables hold state for use by the new thread.
    private int iterations;
    private string message;
    private int delay;

    public ThreadExample(int iterations, string message, int delay) {

        this.iterations = iterations;
        this.message = message;
        this.delay = delay;
    }

    public void Start() {

        // Create a ThreadStart delegate instance that references
        // DisplayMessage.
        ThreadStart method = new ThreadStart(this.DisplayMessage);

        // Create a new Thread object and pass the ThreadStart
        // delegate instance to its constructor.
        Thread thread = new Thread(method);

        Console.WriteLine("{0} : Starting new thread.",  
            DateTime.Now.ToString("HH:mm:ss.ffff"));

        // Start the new thread.
        thread.Start();
    }

    private void DisplayMessage() {

        // Display the message to the console the specified number of 
        // times, sleeping between each message for the specified duration.
        for (int count = 0; count < iterations; count++) {

            Console.WriteLine("{0} : {1}",  
                DateTime.Now.ToString("HH:mm:ss.ffff"), message);

            // Sleep for the specified period.
            Thread.Sleep(delay);
        }
    }

    public static void Main() {

        // Create a new ThreadExample object.
        ThreadExample example = 
            new ThreadExample(5, "A thread example.", 500);

        // Start the ThreadExample object.
        example.Start();

        // Continue with other processing.
        for (int count = 0; count < 13; count++) {
            Console.WriteLine("{0} : Continue processing...", 
                DateTime.Now.ToString("HH:mm:ss.ffff"));
            Thread.Sleep(200);
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Execute a Method by Signaling a WaitHandle Object

You can use classes derived from the WaitHandle class to trigger the execution of a method. Using the RegisterWaitForSingleObject method of the ThreadPool class, you can register a WaitOrTimerCallback delegate instance for execution by a thread-pool thread when a specified WaitHandle-derived object enters a signaled state. You can configure the thread pool to execute the method only once or to automatically reregister the method for execution each time the WaitHandle is signaled. If the WaitHandle is already signaled when you call RegisterWaitForSingleObject, the method will execute immediately. The Unregister method of the System.Threading.RegisteredWaitHandle object returned by the RegisterWaitForSingleObject method is used to cancel a registered wait operation.
The class most commonly used as a trigger is AutoResetEvent, which automatically returns to an unsignaled state after it's signaled. However, you can also use the ManualResetEvent and Mutex classes, which require you to change the signaled state manually. The following example demonstrates the use of an AutoResetEvent to trigger the execution of a method named EventHandler.
using System;
using System.Threading;

public class EventExecutionExample {

    // A method that is executed when the AutoResetEvent is signaled
    // or the wait operation times out.
    private static void EventHandler(object state, bool timedout) {

        // Display appropriate message to the console based on whether 
        // the wait timed out or the AutoResetEvent was signaled.
        if (timedout) {
            
            Console.WriteLine("{0} : Wait timed out.",
                DateTime.Now.ToString("HH:mm:ss.ffff"));
        } else {

            Console.WriteLine("{0} : {1}", 
                DateTime.Now.ToString("HH:mm:ss.ffff"), state);
        }
    }

    public static void Main() {

        // Create the new AutoResetEvent in an unsignaled state. 
        AutoResetEvent autoEvent = new AutoResetEvent(false);

        // Create a new WaitOrTimerCallback delegate instance that 
        // references the static EventHandler method. EventHandler 
        // will be called when the AutoResetEvent is signaled or
        // the wait times out.
        WaitOrTimerCallback handler = 
            new WaitOrTimerCallback(EventHandler);

        // Create the state object that is passed to the event handler
        // method when it is triggered. In this case a message to display.
        string state = "AutoResetEvent signaled.";

        // Register the delegate instance to wait for the AutoResetEvent to
        // be signaled. Set a time-out of 3 seconds, and configure the wait 
        // operation to reset after activation (last argument).
        RegisteredWaitHandle handle = 
            ThreadPool.RegisterWaitForSingleObject(autoEvent, handler, 
            state, 3000, false);

        Console.WriteLine("Press ENTER to signal the AutoResetEvent" +
            " or enter \"Cancel\" to unregister the wait operation.");

        while (Console.ReadLine().ToUpper() != "CANCEL") {

            // If "Cancel" has not been entered into the console, signal
            // the AutoResetEvent, which will cause the EventHandler
            // method to execute. The AutoResetEvent will automatically
            // revert to an unsignaled state.
            autoEvent.Set();
        }

        // Unregister the wait operation.
        Console.WriteLine("Unregistering wait operation.");
        handle.Unregister(null);

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Execute a Method Using a Timer

It's often useful to execute a method at a particular time or at regular intervals. For example, you might need to back up data at 1:00 A.M. every day or to clean a data cache every 20 minutes. The Timer class makes the timed execution of methods straightforward, allowing you to execute a method referenced by a TimerCallback delegate at specified intervals. The referenced method executes in the context of a thread from the thread pool.
When you create a Timer object, you specify two time intervals. The first value specifies the millisecond delay until the Timer first executes your method. Specify 0 to execute the method immediately, and System.Threading.Timeout.Infinite to create the Timer in an unstarted state. The second value specifies the interval after which the Timer will repeatedly call your method following the initial execution. If you specify a value of 0 or Timeout.Infinite, the Timer will execute the method only once (as long as the initial delay is not Timeout.Infinite). The time intervals can be specified as int, long, uint, or System.TimeSpan values.
Once you have created a Timer object, you can modify the intervals used by the timer using the Change method, but you can't change the method that is called. When you have finished with a Timer you should call its Dispose method to free system resources held by the timer. Disposing of the Timer cancels any method that is scheduled for execution.
The TimerExample class shown here demonstrates the use of a Timer to call a method named TimerHandler. Initially, the Timer is configured to call TimerHandler after two seconds and then at one-second intervals. The example allows you to enter a new millisecond interval in the console, which is applied using the Timer.Change method.
 
using System;
using System.Threading;

public class TimerExample {

    // The method that is executed when the timer expires. Displays
    // a message to the console.
    private static void TimerHandler(object state) {

        Console.WriteLine("{0} : {1}",
            DateTime.Now.ToString("HH:mm:ss.ffff"), state);
    }

    public static void Main() {

        // Create a new TimerCallback delegate instance that 
        // references the static TimerHandler method. TimerHandler 
        // will be called when the timer expires.
        TimerCallback handler = new TimerCallback(TimerHandler);

        // Create the state object that is passed to the TimerHandler
        // method when it is triggered. In this case a message to display.
        string state = "Timer expired.";

        Console.WriteLine("{0} : Creating Timer.",
            DateTime.Now.ToString("HH:mm:ss.ffff"));

        // Create a Timer that fires first after 2 seconds and then every
        // second.
        using (Timer timer = new Timer(handler, state, 2000, 1000)) {

            int period;

            // Read the new timer interval from the console until the
            // user enters 0 (zero). Invalid values use a default value
            // of 0, which will stop the example.
            do {

                try {
                    period = Int32.Parse(Console.ReadLine());
                } catch {
                    period = 0;
                }

                // Change the timer to fire using the new interval starting
                // immediately.
                if (period > 0) timer.Change(0, period);

            } while (period > 0);
        }

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}
 
Although primarily used for calling methods at regular intervals, the Timer also provides the flexibility to call a method at a specific time. You must calculate the difference between the current time and the desired execution time as demonstrated in the RunAt method shown here.
public static void RunAt(DateTime execTime) {

    // Calculate the difference between the specified execution 
    // time and the current time.
    TimeSpan waitTime = execTime - DateTime.Now;

    if (waitTime < new TimeSpan(0)) waitTime = new TimeSpan(0);

    // Create a new TimerCallback delegate instance that 
    // references the static TimerHandler method. TimerHandler 
    // will be called when the timer expires.
    TimerCallback handler = new TimerCallback(TimerHandler);

    // Create a Timer that fires once at the specified time. Specify
    // an interval of -1 to stop the timer executing the method
    // repeatedly.
    new Timer(handler, null, waitTime, new TimeSpan(-1));
}

Execute a Method Asynchronously

Typically, when you invoke a method you do so synchronously, meaning that the calling code blocks until the method is complete. Most of the time, this is the expected and desired behavior because your code requires the operation to complete before it can continue. However, sometimes it's useful to execute a method asynchronously, meaning that you start the method in a separate thread and then continue with other operations.
The .NET Framework implements an asynchronous execution pattern that allows you to call any method asynchronously using a delegate. When you declare and compile a delegate, the compiler automatically generates two methods that support asynchronous execution: BeginInvoke and EndInvoke. When you call BeginInvoke on a delegate instance, the method referenced by the delegate is queued for asynchronous execution. Control returns to the caller immediately, and the referenced method executes in the context of the first available thread-pool thread.
The signature of the BeginInvoke method includes the same arguments as those specified by the delegate signature, followed by two additional arguments to support asynchronous completion. These additional arguments are
  • A System.AsyncCallback delegate instance that references a method that the runtime will call when the asynchronous method completes. The method is executed in the context of a thread-pool thread. Passing null means that no method is called and you must use another mechanism (discussed later in this recipe) to determine when the asynchronous method is complete.
  • An object reference that the runtime associates with the asynchronous operation. The asynchronous method does not use nor have access to this object but it's available to your code when the method completes, allowing you to associate useful state information with an asynchronous operation. For example, this object allows you to map results against initiated operations in situations where you initiate many asynchronous operations that use a common callback method to perform completion.
The EndInvoke method allows you to retrieve the return value of a method that was executed asynchronously, but you must first determine when it has finished. Here are the four techniques for determining if an asynchronous method has finished.
  • Blocking Blocking stops the execution of the current thread until the asynchronous method completes execution. In effect, this is much the same as synchronous execution. However, you do have the flexibility to decide exactly when your code enters the blocked state, giving you the opportunity to carry out some additional processing before blocking.
  • Polling Polling involves repeatedly testing the state of an asynchronous method to determine if it's complete. This is a very simple technique and is not particularly efficient from a processing perspective. You should avoid tight loops that consume processor time; it's best to put the polling thread to sleep for a period using Thread.Sleep between completion tests. Because polling involves maintaining a loop, the actions of the waiting thread are limited, but you can easily update some kind of progress indicator.
  • Waiting Waiting uses an object derived from the System.Threading.WaitHandle class to signal when the asynchronous method completes. Waiting is a more efficient version of polling and in addition allows you to wait for multiple asynchronous methods to complete. You can also specify time-out values to allow your waiting thread to fail if the asynchronous method takes too long, or if you want to periodically update a status indicator.
  • Callbacks A callback is a method that the runtime calls when an asynchronous operation completes. The calling code does not have to take any steps to determine when the asynchronous method is complete and is free to continue with other processing. Callbacks provide the greatest flexibility, but also introduce the greatest complexity, especially if you have many asynchronous operations active concurrently that all use the same callback. In such cases, you must use appropriate state objects to match completed methods against those you initiated.
The AsyncExecutionExample class in the sample code for this chapter demonstrates use of the asynchronous execution pattern. It uses a delegate named AsyncExampleDelegate to execute a method named LongRunningMethod asynchronously. LongRunningMethod simulates a long-running method using a configurable delay (produced using Thread.Sleep). Here is the code for AsyncExampleDelegate and LongRunningMethod.
// A delegate that allows you to perform asynchronous execution of 
// AsyncExecutionExample.LongRunningMethod.
public delegate DateTime AsyncExampleDelegate(int delay, string name);

// A simulated long running method.
public static DateTime LongRunningMethod(int delay, string name) {

    Console.WriteLine("{0} : {1} example - thread starting.", 
        DateTime.Now.ToString("HH:mm:ss.ffff"), name);

    // Simulate time consuming processing.
    Thread.Sleep(delay);

    Console.WriteLine("{0} : {1} example - thread finishing.", 
        DateTime.Now.ToString("HH:mm:ss.ffff"), name);

    // Return the method's completion time.
    return DateTime.Now;
}
AsyncExecutionExample contains five methods that demonstrate different approaches for handled asynchronous method completion. A description of these methods and their code is provided here.
The BlockingExample method executes LongRunningMethod asynchronously and continues with a limited set of processing. Once this processing is complete, BlockingExample blocks until LongRunningMethod completes. To block, BlockingExample calls the EndInvoke method of the AsyncExampleDelegate delegate instance. If LongRunningMethod has already finished, EndInvoke returns immediately; otherwise, BlockingExample blocks until LongRunningMethod completes.
public static void BlockingExample() {

    Console.WriteLine(Environment.NewLine + 
        "*** Running Blocking Example ***");

    // Invoke LongRunningMethod asynchronously. Pass null for both the 
    // callback delegate and the asynchronous state object.
    AsyncExampleDelegate longRunningMethod = 
        new AsyncExampleDelegate(LongRunningMethod);

    IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, 
        "Blocking", null, null);

    // Perform other processing until ready to block.
    for (int count = 0; count < 3; count++) {
        Console.WriteLine("{0} : Continue processing until ready " +
            "to block...", DateTime.Now.ToString("HH:mm:ss.ffff"));
        Thread.Sleep(200);
    }

    // Block until the asynchronous method completes and obtain 
    // completion data.
    Console.WriteLine("{0} : Blocking until method is complete...",
        DateTime.Now.ToString("HH:mm:ss.ffff"));
    DateTime completion = longRunningMethod.EndInvoke(asyncResult);

    // Display completion information
    Console.WriteLine("{0} : Blocking example complete.",
        completion.ToString("HH:mm:ss.ffff"));
}
The PollingExample method executes LongRunningMethod asynchronously and then enters a polling loop until LongRunningMethod completes. PollingExample tests the IsCompleted property of the IAsyncResult instance returned by BeginInvoke to determine if LongRunningMethod is complete; otherwise, PollingExample calls Thread.Sleep.
public static void PollingExample() {

    Console.WriteLine(Environment.NewLine + 
        "*** Running Polling Example ***");

    // Invoke LongRunningMethod asynchronously. Pass null for both the 
    // callback delegate and the asynchronous state object.
    AsyncExampleDelegate longRunningMethod = 
        new AsyncExampleDelegate(LongRunningMethod);

    IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, 
        "Polling", null, null);

    // Poll the asynchronous method to test for completion. If not 
    // complete sleep for 300ms before polling again.
    Console.WriteLine("{0} : Poll repeatedly until method is " +
        "complete...", DateTime.Now.ToString("HH:mm:ss.ffff"));
    while(!asyncResult.IsCompleted) {
        Console.WriteLine("{0} : Polling...", 
            DateTime.Now.ToString("HH:mm:ss.ffff"));
        Thread.Sleep(300);
    }

    // Obtain the completion data for the asynchronous method.
    DateTime completion = longRunningMethod.EndInvoke(asyncResult);

    // Display completion information
    Console.WriteLine("{0} : Polling example complete.",
        completion.ToString("HH:mm:ss.ffff"));
}
The WaitingExample method executes LongRunningMethod asynchronously and then waits until LongRunningMethod completes. WaitingExample uses the AsyncWaitHandle property of the IAsyncResult instance returned by BeginInvoke to obtain a WaitHandle and then calls its WaitOne method. Use of a time-out allows WaitingExample to break out of waiting in order to perform other processing or to fail completely if the asynchronous method is taking too long.
public static void WaitingExample() {

    Console.WriteLine(Environment.NewLine + 
        "*** Running Waiting Example ***");

    // Invoke LongRunningMethod asynchronously. Pass null for both the 
    // callback delegate and the asynchronous state object.
    AsyncExampleDelegate longRunningMethod = 
        new AsyncExampleDelegate(LongRunningMethod);

    IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, 
        "Waiting", null, null);

    // Wait for the asynchronous method to complete. Time out after 
    // 300ms and display status to the console before continuing to
    // wait.
    Console.WriteLine("{0} : Waiting until method is complete...", 
        DateTime.Now.ToString("HH:mm:ss.ffff"));
    while(!asyncResult.AsyncWaitHandle.WaitOne(300, false)) {
        Console.WriteLine("{0} : Wait timeout...",
            DateTime.Now.ToString("HH:mm:ss.ffff"));
    }

    // Obtain the completion data for the asynchronous method.
    DateTime completion = longRunningMethod.EndInvoke(asyncResult);

    // Display completion information
    Console.WriteLine("{0} : Waiting example complete.",
        completion.ToString("HH:mm:ss.ffff"));
}
 
The WaitAllExample method executes LongRunningMethod asynchronously multiple times and then uses an array of WaitHandle objects to wait efficiently until all of the methods are complete.
public static void WaitAllExample() {

    Console.WriteLine(Environment.NewLine + 
        "*** Running WaitAll Example ***");

    // An ArrayList to hold the IAsyncResult instances for each of the 
    // asynchronous methods started.
    ArrayList asyncResults = new ArrayList(3);

    // Invoke three LongRunningMethods asynchronously. Pass null for 
    // both the callback delegate and the asynchronous state object.
    // Add the IAsyncResult instance for each method to the ArrayList.
    AsyncExampleDelegate longRunningMethod = 
        new AsyncExampleDelegate(LongRunningMethod);

    asyncResults.Add(longRunningMethod.BeginInvoke(3000, 
        "WaitAll 1", null, null));

    asyncResults.Add(longRunningMethod.BeginInvoke(2500, 
        "WaitAll 2", null, null));

    asyncResults.Add(longRunningMethod.BeginInvoke(1500, 
        "WaitAll 3", null, null));

    // Create an array of WaitHandle objects that will be used to wait 
    // for the completion of all of the asynchronous methods.
    WaitHandle[] waitHandles = new WaitHandle[3];

    for (int count = 0; count < 3; count++) {

        waitHandles[count] = 
            ((IAsyncResult)asyncResults[count]).AsyncWaitHandle;
    }

    // Wait for all three asynchronous method to complete. Time out
    // after 300ms and display status to the console before continuing
    // to wait.
    Console.WriteLine("{0} : Waiting until all 3 methods are " + 
        "complete...", DateTime.Now.ToString("HH:mm:ss.ffff"));
    while(!WaitHandle.WaitAll(waitHandles, 300, false)) {
        Console.WriteLine("{0} : WaitAll timeout...", 
            DateTime.Now.ToString("HH:mm:ss.ffff"));
    }

    // Inspect the completion data for each method and determine the
    // time at which the final method completed.
    DateTime completion = DateTime.MinValue;

    foreach (IAsyncResult result in asyncResults) {

        DateTime time = longRunningMethod.EndInvoke(result);
        if ( time > completion) completion = time;
    }

    // Display completion information
    Console.WriteLine("{0} : WaitAll example complete.",
        completion.ToString("HH:mm:ss.ffff"));
}
 
The CallbackExample method executes LongRunningMethod asynchronously and passes an AsyncCallback delegate instance (that references the CallbackHandler method) to the BeginInvoke method. The referenced CallbackHandler method is called automatically when the asynchronous LongRunningMethod completes, leaving the CallbackExample method completely free to continue processing.
public static void CallbackExample() {

    Console.WriteLine(Environment.NewLine + 
        "*** Running Callback Example ***");

    // Invoke LongRunningMethod asynchronously. Pass an AsyncCallback
    // delegate instance referencing the CallbackHandler method which
    // will be called automatically when the asynchronous method 
    // completes. Pass a reference to the AsyncExampleDelegate delegate 
    // instance as asynchronous state; otherwise, the callback method
    // has no access to the delegate instance in order to call 
    // EndInvoke.
    AsyncExampleDelegate longRunningMethod = 
        new AsyncExampleDelegate(LongRunningMethod);

    IAsyncResult asyncResult = longRunningMethod.BeginInvoke(2000, 
        "Callback", new AsyncCallback(CallbackHandler), 
        longRunningMethod);

    // Continue with other processing.
    for (int count = 0; count < 15; count++) {
        Console.WriteLine("{0} : Continue processing...", 
            DateTime.Now.ToString("HH:mm:ss.ffff"));
        Thread.Sleep(200);
    }
}

// A method to handle asynchronous completion using callbacks.
public static void CallbackHandler(IAsyncResult result) {

    // Extract the reference to the AsyncExampleDelegate instance 
    // from the IAsyncResult instance. This allows us to obtain the 
    // completion data.
    AsyncExampleDelegate longRunningMethod = 
        (AsyncExampleDelegate)result.AsyncState;

    // Obtain the completion data for the asynchronous method.
    DateTime completion = longRunningMethod.EndInvoke(result);

    // Display completion information
    Console.WriteLine("{0} : Callback example complete.",
        completion.ToString("HH:mm:ss.ffff"));
}

Execute a Method Using the Thread Pool

Applications that use many short-lived threads or maintain large numbers of concurrent threads can suffer performance degradation because of the overhead associated with the creation, operation, and destruction of threads. In addition, it's common in multithreaded systems for threads to sit idle a large portion of the time while they wait for the appropriate conditions to trigger their execution. Use of a thread pool provides a common solution to improve the scalability, efficiency, and performance of multithreaded systems.
The .NET Framework provides a simple thread-pool implementation accessible through the static members of the ThreadPool class. The QueueUserWorkItem method allows you to execute a method using a thread-pool thread by placing a work item on a queue. The work item is represented by a WaitCallback delegate instance that references the method you want to execute. As a thread from the thread pool becomes available, it takes the next work item from the queue and executes it. The thread carries out the work assigned to it, and when it's finished, instead of terminating, the thread returns to the thread pool and takes the next work item from the work queue.
Use of the runtime's thread pool simplifies multithreaded programming dramatically; however, be aware that the implementation is a simple, general- purpose thread pool. Before deciding to use the thread pool, consider the following points:

  • The runtime host determines the maximum number of threads allocated to the thread pool; you can't change this maximum number using configuration parameters or from within managed code. The default limit is 25 threads per CPU in your system. The maximum number of threads in the thread pool does not limit the number of items that can be waiting in the queue.

  • As well as allowing you to use the thread pool to execute code directly, the runtime uses the thread pool for many purposes internally.  All of these uses can lead to heavy contention for the thread-pool threads, meaning that the work queue can become very long. Although the work queue's maximum length is limited only by the amount of memory available to the runtime's process, an excessively long queue will result in long delays before queued work items are executed.

  • You shouldn't use the thread pool to execute long-running processes. The limited number of threads in the thread pool means that a handful of threads tied up with long-running processes can have a significant effect on the overall performance of the thread pool. Specifically, you should avoid putting thread-pool threads to sleep for any length of time.

  • You have no control over the scheduling of thread-pool threads, nor can you prioritize work items. The thread pool handles each work item in the sequence in which you add it to the work queue.

  • Once a work item is queued, it can't be cancelled or stopped.
The following example demonstrates the use of the ThreadPool class to execute a method named DisplayMessage. The example passes DisplayMessage to the thread pool twice, first with no arguments, and then with a MessageInfo object, which allows you to control which message the new thread will display.
using System;
using System.Threading;

// A class used to pass data to the DisplayMessage method when it is
// executed using the thread pool.
public class MessageInfo {

    private int iterations;
    private string message;

    // A constructor that takes configuration settings for the thread.
    public MessageInfo(int iterations, string message) {

        this.iterations = iterations;
        this.message = message;
    }

    // Properties to retrieve configuration settings.
    public int Iterations { get { return iterations; } }
    public string Message { get { return message; } }
}

public class ThreadPoolExample {

    // Displays a message to the console.
    public static void DisplayMessage(object state) {

        // Cast the state argument to a MessageInfo object.
        MessageInfo config = state as MessageInfo;

        // If the config argument is null, no arguments were passed to
        // the ThreadPool.QueueUserWorkItem method, use default values.
        if (config == null) {

            // Display a fixed message to the console 3 times.
            for (int count = 0; count < 3; count++) {

                Console.WriteLine("A thread pool example.");

                // Sleep for the purpose of demonstration. Avoid sleeping
                // on thread-pool threads in real applications.
                Thread.Sleep(1000);
            }

        } else {

            // Display the specified message the specified number of times.
            for (int count = 0; count < config.Iterations; count++) {

                Console.WriteLine(config.Message);

                // Sleep for the purpose of demonstration. Avoid sleeping
                // on thread-pool threads in real applications.
                Thread.Sleep(1000);
            }
        }
    }

    public static void Main() {

        // Create a delegate instance to enable us to pass the 
        // DisplayMessage method to the thread pool for execution.
        WaitCallback workMethod = 
            new WaitCallback(ThreadPoolExample.DisplayMessage);

        // Execute DisplayMessage using the thread pool and no arguments.
        ThreadPool.QueueUserWorkItem(workMethod);

        // Execute DisplayMessage using the thread pool and providing a 
        // MessageInfo object to pass to the DisplayMessage method.
        MessageInfo info = 
            new MessageInfo(5, "A thread pool example with arguments.");

        ThreadPool.QueueUserWorkItem(workMethod, info);

        // Wait to continue.
        Console.WriteLine("Main method complete. Press Enter.");
        Console.ReadLine();
    }
}

Inspect the Attributes of a Program Element Using Reflection

All of the classes that represent program elements derive from the MemberInfo class. This class includes Type, EventInfo, FieldInfo, PropertyInfo, and MethodBase. MethodBase has two further subclasses: ConstructorInfo and MethodInfo. If you obtain instances of any of these classes, you can call the inherited method GetCustomAttributes, which will return an object array containing the custom attributes applied to the program element. The object array contains only custom attributes, not those contained in the .NET Framework base class library.
The GetCustomAttributes method provides two overloads. The first takes a bool that controls whether GetCustomAttributes should return attributes inherited from parent classes. The second GetCustomAttributes overload takes an additional Type argument that acts as a filter, resulting in GetCustomAttributes returning only attributes of the specified type.
The following example uses the custom AuthorAttribute  and applies it to the GetCustomAttributesExample class. The Main method calls the GetCustomAttributes method, filtering the attributes so that the method returns only AuthorAttribute instances. You can safely cast this set of attributes to AuthorAttribute references and access their members without the need to use reflection.
using System;

[Author("Lena")]
[Author("Allen", Company = "Principal Objective Ltd.")]
public class GetCustomAttributesExample {

    public static void Main() {

        // Get a Type object for this class. 
        Type type = typeof(GetCustomAttributesExample);

        // Get the attributes for the type. Apply a filter so that only
        // instances of AuthorAttribute are returned.
        object[] attrs = 
            type.GetCustomAttributes(typeof(AuthorAttribute), true);

        // Enumerate the attributes and display their details.
        foreach (AuthorAttribute a in attrs) {
            Console.WriteLine(a.Name + ", " + a.Company);
        }

        // Wait to continue
        Console.ReadLine();
    }
}

Create a Custom Attribute

Attributes provide a generic mechanism for associating declarative information (metadata) with program elements. This metadata is contained in the compiled assembly allowing programs to retrieve it through reflection at run time.  Other programs, particularly the CLR, use this information to determine how they should interact with and manage program elements.
To create a custom attribute, derive a class from the abstract base class System.Attribute. Custom attribute classes must be public and by convention should have a name ending in "Attribute". A custom attribute must have at least one public constructor. The constructor parameters become the attribute's positional parameters. As with any other class, you can declare more than one constructor, giving users of the attribute the option of using different sets of positional parameters when applying the attribute. Any public read/write fields and properties declared by an attribute are automatically exposed as named parameters.
To control how and where a user can apply your attribute, apply the attribute AttributeUsageAttribute to your custom attribute. AttributeUsageAttribute supports the one positional and two named parameters described in Table. The default values specify the value that's applied to your custom attribute if you don't apply AttributeUsageAttribute or don't specify a value for that particular parameter.
 
Members of the AttributeUsage Enumeration
Parameter
Type
Description
Default
ValidOn
positional
A member of the System.AttributeTargets enumeration that identifies the program elements on which the attribute is valid
AttributeTargets.All
AllowMultiple
named
Whether the attribute can be specified more than once for a single element
false
Inherited
named
Whether the attribute is inherited by derived classes or overridden members
true
The following example shows a custom attribute named AuthorAttribute, which you can use to identify the name and company of the person who created an assembly or a class. AuthorAttribute declares a single public constructor that takes a string containing the author's name. This means that users of AuthorAttribute must always provide a positional string parameter containing the author's name. The Company property is public, making it an optional named parameter, but the Name property is read only—no set accessor is declared—meaning that it isn't exposed as a named parameter.
using System;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Assembly,
     AllowMultiple = true, Inherited = false)]
public class AuthorAttribute : System.Attribute {

    private string company; // creator's company
    private string name;    // creator's name

    // Declare a public constructor
    public AuthorAttribute(string name) {
        this.name = name;
        company = "";
    }

    // Declare a property to get/set the company field
    public string Company {
        get { return company; }
        set { company = value; }
    }

    // Declare a property to get the internal field
    public string Name{
        get { return name;}
    }
}
The following example demonstrates some uses of AuthorAttribute:
// Declare Allen as the assembly author
[assembly:Author("Allen", Company = "Principal Objective Ltd.")]

// Declare a class authored by Allen
[Author("Allen", Company = "Principal Objective Ltd.")]
public class SomeClass {
    §
}

// Declare a class authored by Lena
[Author("Lena")]
public class SomeOtherClass {
    §
}

Instantiate an Object Using Reflection

The first step in creating an object using reflection is to obtain a Type object that represents the type you want to instantiate. Once you have a Type instance, call its GetConstructor method to obtain a ConstructorInfo representing one of the type's constructors. The most commonly used overload of the GetConstructor method takes a Type array argument and returns a ConstructorInfo representing the constructor that takes the number, order, and type of arguments specified in the Type array. To obtain a ConstructorInfo representing a parameterless (default) constructor, pass an empty Type array (use the static field Type.EmptyTypes); don't use null, or GetConstructor will throw a System.ArgumentNullException. If GetConstructor can't find a constructor with a signature that matches the specified arguments, it will return null.
Once you have the desired ConstructorInfo, call its Invoke method. You must provide an object array containing the arguments you want to pass to the constructor. Invoke instantiates the new object and returns an object reference to it, which you must cast to the appropriate type. The following code demonstrates how to instantiate a System.Text.StringBuilder object, specifying the initial content for the StringBuilder (a string) and its capacity (an int).
// Obtain the Type for the StringBuilder class.
Type type = typeof(System.Text.StringBuilder);

// Create a Type[] containing Type instances for each
// of the constructor arguments - a string and an int.
Type[] argTypes = new Type[] {typeof(System.String), typeof(System.Int32)};

// Obtain the ConstructorInfo object.
ConstructorInfo cInfo = type.GetConstructor(argTypes);

// Create an object[] containing the constructor arguments.
object[] argVals = new object[] {"Some string", 30};

// Create the object and cast it to StringBuilder.
StringBuilder sb = (StringBuilder)cInfo.Invoke(argVals);
Reflection functionality is commonly used to implement factories in which you use reflection to instantiate concrete classes that either extend a common base class or implement a common interface. Often both an interface and a common base class are used. The abstract base class implements the interface and any common functionality, and then each concrete implementation extends the base class.
There's no mechanism to formally declare that each concrete class must implement constructors with specific signatures. If you intend third parties to implement concrete classes, your documentation must specify the constructor signature called by your factory. A common approach to avoid this problem is to use a default (empty) constructor and configure the object after instantiation using properties and methods. The following code demonstrates a factory to instantiate objects that implement the IPlugin interface
using System;
using System.Reflection;

// A common interface that all plug-ins must implement.
public interface IPlugin {
    string Description { get; set; }
    void Start();
    void Stop();
}

// An abstract base class from which all plug-ins must derive.
public abstract class AbstractPlugin : IPlugin {

    // Hold a description for the plug-in instance
    private string description = "";

    // Sealed property to get the plug-in description.
    public string Description { 
        get { return description; }
        set { description = value; }
    }

    // Declare the members of the IPlugin interface as abstract.
    public abstract void Start();
    public abstract void Stop();
}

// A simple IPlugin implementation to demonstrate the PluginFactory class.
public class SimplePlugin : AbstractPlugin {

    // Implement Start method.
    public override void Start() {
        Console.WriteLine(Description  + ": Starting...");
    }

    // Implement Stop method.
    public override void Stop() {
        Console.WriteLine(Description + ": Stopping...");
    }
}

// A factory to instantiate instances of IPlugin.
public sealed class PluginFactory {

    public static IPlugin CreatePlugin(string assembly, 
        string pluginName, string description) {

        // Obtain the Type for the specified plug-in.
        Type type = Type.GetType(pluginName + ", " + assembly);

        // Obtain the ConstructorInfo object.
        ConstructorInfo cInfo = type.GetConstructor(Type.EmptyTypes);

        // Create the object and cast it to StringBuilder.
        IPlugin plugin = (IPlugin)cInfo.Invoke(null);

        // Configure the new IPlugin
        plugin.Description = description;

        return plugin;
    }
}
This statement will create an instance of SimplePlugin using the PluginFactory class.
IPlugin plugin = PluginFactory.CreatePlugin(
    "CreateObjectExample",  // Private assembly name
    "SimplePlugin",         // Plug-in class name
    "A Simple Plugin"       // Plug-in instance description
);

Test an Object's Type

All types inherit the GetType method from the Object base class. This method returns a Type reference representing the type of the object. The runtime maintains a single instance of Type for each type loaded and all references for this type refer to this same object. This means that you can compare two type references efficiently. The IsStringReader method shown here demonstrates how to test if an object is a System.IO.StringReader.
// Create a new StringReader for testing.
Object someObject = new StringReader("This is a StringReader");

// Test if someObject is a StringReader by obtaining and 
// comparing a Type reference using the typeof operator.
if (typeof(System.IO.StringReader) == someObject.GetType()) 
{
    // Do something
   }
 
C# provides the is operator as a quick way to perform the same test as the IsStringReader method. In addition, is will return true if the tested object is derived from the specified class. This code fragment tests if someObject is an instance of System.IO.TextReader, or a derived class (such as StringReader).
// Test if someObject is, or is derived from, a TextReader
// using the is operator.
if (someObject is System.IO.TextReader) 
{
    // Do something
    }
Both of these approaches require that the type used with the typeof and is operators be known and resolvable at compile time. A more flexible (but slower) alternative is to use the Type.GetType method to return a Type reference for a named type. The Type reference isn't resolved until run time, which causes the performance hit, but allows you to change the type comparison at run time based on the value of a string. The IsType method here returns true if an object is of a named type and uses the Type.IsSubclassOf method to test if the object is a subclass of the named type.
public static bool IsType(object obj, string type) {

    // Get the named type, use case insensitive search, throw 
    // an exception if the type is not found.
    Type t = Type.GetType(type, true, true);

    return t == obj.GetType() || obj.GetType().IsSubclassOf(t);
}
 
Finally, you can use the as operator to perform a safe cast of any object to a specified type. If the object can't be cast to the specified type, the as operator returns null. This allows you to perform safe casts that are easy to verify, but the compared type must be resolvable at run time. Here's an example:
// Use the "as" operator to perform a safe cast.
StringReader reader = someObject as System.IO.StringReader;
if (reader != null)
{
    // Do something with reader
    }

Retrieve Type Information

The Type object provides a starting point for working with types using reflection. A Type object allows you to inspect the metadata of the type, obtain details of the type's members, and create instances of the type. Because of its importance, the .NET Framework provides a variety of mechanisms for obtaining reference to Type objects.
The most efficient method of obtaining a Type object for a specific type is to use the typeof operator shown here.
System.Type t1 = typeof(System.Text.StringBuilder);
The type name isn't enclosed in quotes and must be resolvable by the compiler. Because the reference is resolved at compile time, the assembly containing the type becomes a static dependency of your assembly and will be listed as such in your assembly's manifest.
An alternative to the typeof operator is the static method Type.GetType, which takes a string containing the type name. Because you use a string to specify the type, you can vary it at run time, which opens the door to a world of dynamic programming opportunities using reflection. If you specify just the type name, the runtime must be able to locate the type in an already loaded assembly. Alternatively, you can specify an assembly-qualified type name. Refer to the .NET Framework SDK documentation for the Type.GetType method for a complete description of how to structure assembly-qualified type names. The following statements demonstrate the use of the GetType method:
// Case sensitive, return null if not found
Type t2 = Type.GetType("System.String");
// Case sensitive, throw TypeLoadException if not found
Type t3 = Type.GetType("System.String", true);
// Case insensitive, throw TypeLoadException if not found
Type t4 = Type.GetType("system.string", true, true);
// Assembly qualifed type name
Type t5 = Type.GetType("System.Data.DataSet,System.Data," +
    "Version=1.0.5000.0,Culture=neutral,PublicKeyToken=b77a5c561934e089");
To obtain a Type object representing the type of an existing object, use the GetType method, implemented by Object and inherited by all types. Here's an example:
System.Text.StringBuilder sb = new System.Text.StringBuilder();
Type t6 = sb.GetType();
Table 3.2 summarizes other methods that provide access to Type objects.
Table 3.2: Methods That Return Type Objects
Method
Description
Type.GetNestedType
Gets a specified type declared as a nested type within the existing Type object
Type.GetNestedTypes
Gets an array of Type objects representing the nested types declared within the existing Type object
Assembly.GetType
Gets a Type object for the specified type declared within the assembly
Assembly.GetTypes
Gets an array of Type objects representing the types declared within the assembly
Module.GetType
Gets a Type object for the specified type declared within the module
Module.GetTypes
Gets an array of Type objects representing the types declared within the module
Module.FindTypes
Gets a filtered array of Type objects representing the types declared within the module—the types are filtered using a delegate that determines if each Type should appear in the final array

Retrieve Type Information

The Type object provides a starting point for working with types using reflection. A Type object allows you to inspect the metadata of the type, obtain details of the type's members, and create instances of the type. Because of its importance, the .NET Framework provides a variety of mechanisms for obtaining reference to Type objects.
The most efficient method of obtaining a Type object for a specific type is to use the typeof operator shown here.
System.Type t1 = typeof(System.Text.StringBuilder);
 
The type name isn't enclosed in quotes and must be resolvable by the compiler. Because the reference is resolved at compile time, the assembly containing the type becomes a static dependency of your assembly and will be listed as such in your assembly's manifest.
An alternative to the typeof operator is the static method Type.GetType, which takes a string containing the type name. Because you use a string to specify the type, you can vary it at run time, which opens the door to a world of dynamic programming opportunities using reflection. If you specify just the type name, the runtime must be able to locate the type in an already loaded assembly. Alternatively, you can specify an assembly-qualified type name. Refer to the .NET Framework SDK documentation for the Type.GetType method for a complete description of how to structure assembly-qualified type names.
The following statements demonstrate the use of the GetType method:
// Case sensitive, return null if not found
Type t2 = Type.GetType("System.String");
// Case sensitive, throw TypeLoadException if not found
Type t3 = Type.GetType("System.String", true);
// Case insensitive, throw TypeLoadException if not found
Type t4 = Type.GetType("system.string", true, true);
// Assembly qualifed type name
Type t5 = Type.GetType("System.Data.DataSet,System.Data," +
    "Version=1.0.5000.0,Culture=neutral,PublicKeyToken=b77a5c561934e089");
 
To obtain a Type object representing the type of an existing object, use the GetType method, implemented by Object and inherited by all types. 
Here's an example:
System.Text.StringBuilder sb = new System.Text.StringBuilder();
Type t6 = sb.GetType();

 
Methods That Return Type Objects
Method
Description
Type.GetNestedType
Gets a specified type declared as a nested type within the existing Type object
Type.GetNestedTypes
Gets an array of Type objects representing the nested types declared within the existing Type object
Assembly.GetType
Gets a Type object for the specified type declared within the assembly
Assembly.GetTypes
Gets an array of Type objects representing the types declared within the assembly
Module.GetType
Gets a Type object for the specified type declared within the module
Module.GetTypes
Gets an array of Type objects representing the types declared within the module
Module.FindTypes
Gets a filtered array of Type objects representing the types declared within the module—the types are filtered using a delegate that determines if each Type should appear in the final array

Unload Assemblies and Application Domains

The only way to unload an assembly is to unload the application domain in which the assembly is loaded. Unfortunately, unloading an application domain will unload all of the assemblies that have been loaded into it. This might seem like a heavy-handed and inflexible approach, but with appropriate planning of your application domain and assembly loading structure, it isn't overly restrictive.
You unload an application domain using the static AppDomain.Unload method and passing it an AppDomain reference to the application domain you wish to unload. You can't unload the default application domain created by the CLR at startup. This code fragment demonstrates the Unload method.
// Create a new application domain
AppDomain newDomain = AppDomain.CreateDomain("New Domain");

// Load assemblies into the application domain
§

// Unload the new application domains
AppDomain.Unload(newDomain);
 
The Unload method stops any new threads from entering the specified application domain and calls the Thread.Abort method on all threads currently active in the application domain. If the thread calling the Unload method is currently running in the specified application domain (making it the target of a Thread.Abort call), a new thread is started to carry out the unload operation. If there's a problem unloading an application domain, a System.CannotUnloadAppDomainException is thrown by the thread performing the unload operation.
While an application domain is unloading, the CLR calls the finalization method of all objects in the application domain. Depending on the number of objects and nature of their finalization methods, this can take an arbitrary amount of time. The AppDomain.IsFinalizingForUnload method returns true if the application domain is unloading and the CLR has started to finalize contained objects; otherwise, it returns false.

Pass Data Between Application Domains

You can pass data between application domains as arguments and return values when you invoke the members of objects that exist in other application domains. However, at times it's useful to pass data between application domains in such a way that the data is easily accessible by all code within the application domain.
Every application domain maintains a data cache that contains a set of name/value pairs. Most of the cache content reflects configuration settings of the application domain, such as the values from the AppDomainSetup object provided during application domain creation. (See here for more) You can also use this data cache as a mechanism to exchange data between application domains or as a simple state storage mechanism for code running within the application domain.
The SetData method allows you to associate a string key with an object and store it in the application domain's data cache. The GetData method allows you to retrieve an object from the data cache using the key. If code in one application domain calls the SetData or GetData methods to access the data cache of another application domain, the data object must support marshal-by- value or marshal-by-reference semantics or System.Runtime.Serialization.SerializationException is thrown. (See this post for details on the characteristics required to allow objects to transcend application domain boundaries.) This code demonstrates the use of the SetData and GetData methods by passing a System.Collections.ArrayList between two application domains.
using System;
using System.Reflection;
using System.Collections;

public class ListModifier {

    public ListModifier () {

        // Get the list from the data cache.
        ArrayList list = 
            (ArrayList)AppDomain.CurrentDomain.GetData("Pets");
        // Modify the list. 
        list.Add("turtle");
    }
}

public class PassDataExample {

    public static void Main() {

        // Create a new application domain.
        AppDomain domain = AppDomain.CreateDomain("Test");

        // Create an ArrayList and populate with information.
        ArrayList list = new ArrayList();
        list.Add("dog");
        list.Add("cat");
        list.Add("fish");

        // Place the list in the data cache of the new application domain.
        domain.SetData("Pets", list);

        // Instantiate a ListModifier in the new application domain.
        domain.CreateInstance("Recipe03-08", "ListModifier");

        // Get the list and display its contents.
        foreach (string s in (ArrayList)domain.GetData("Pets")) {
            Console.WriteLine(s);
        }

        // Wait to continue
        Console.ReadLine();
    }
}

Instantiate a Type in a Different Application Domain

The ExecuteAssembly method discussed in last post is straightforward to use, but when you are developing sophisticated applications that make use of application domains, you are likely to want more control over the loading of assemblies, instantiation of types, and the invocation of object members within the application domain.
The CreateInstance and CreateInstanceFrom methods provide a variety of overloads that offer fine-grained control over the process of object instantiation. The simplest overloads assume use of a type's default constructor, but both methods implement overloads that allow you to provide arguments to use any constructor.
The CreateInstance method loads a named assembly into the application domain using the process described for the Assembly.Load method previous post. CreateInstance then instantiates a named type and returns a reference to the new object wrapped in an ObjectHandle (described in this post). The CreateInstanceFrom method also instantiates a named type and returns an ObjectHandle wrapped object reference; however, CreateInstanceFrom loads the specified assembly into the application domain using the process described here for the Assembly.LoadFrom method.

Tip 
AppDomain also provides two convenience methods named CreateInstanceAndUnwrap and CreateInstanceFromAndUnwrap that automatically extract the reference of the instantiated object from the returned ObjectHandle object; you must cast the returned object to the correct type.
Be aware that if you use CreateInstance or CreateInstanceFrom to instantiate MBV types in another application domain, the object will be created but the returned object reference won't refer to that object. Because of the way MBV objects cross application domain boundaries, the reference will refer to a copy of the object created automatically in the local application domain. Only if you create an MBR type will the returned reference refer to the object in the other application domain. (See here for more details about MBV and MBR types.)
A common technique to simplify the management of application domains is to use a controller class. A controller class is a custom MBR type. You create an application domain and then instantiate your controller class in the application domain using CreateInstance. The controller class implements the functionality required by your application to manipulate the application domain and its contents. This could include the loading of assemblies, creating further application domains, cleaning up prior to the deletion of the application domain, or enumerating program elements (something you can't normally do from outside an application domain).
The following example demonstrates the use of a simplified controller class named PluginManager. When instantiated in an application domain, PluginManager allows you to instantiate classes that implement the IPlugin interface, start and stop those plug-ins, and return a list of currently loaded plug-ins.
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Specialized;

// A common interface that all plug-ins must implement.
public interface IPlugin {
    void Start();
    void Stop();
}

// A simple IPlugin implementation to demonstrate the PluginManager
// controller class.
public class SimplePlugin : IPlugin {

    public void Start() {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + 
            ": SimplePlugin starting...");
    }

    public void Stop() {
        Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + 
            ": SimplePlugin stopping...");
    }
}

// The controller class, which manages the loading and manipulation
// of plug-ins in its application domain.
public class PluginManager : MarshalByRefObject {

    // A ListDictionary to hold keyed references to IPlugin instances.
    private ListDictionary plugins = new ListDictionary();

    // Default constructor.
    public PluginManager() {}

    // Constructor that loads a set of specified plug-ins on creation.
    public PluginManager(ListDictionary pluginList) {

        // Load each of the specified plug-ins.
        foreach (string plugin in pluginList.Keys) {

            this.LoadPlugin((string)pluginList[plugin], plugin);
        }
    }

    // Load the specified assembly and instantiate the specified
    // IPlugin implementation from that assembly.
    public bool LoadPlugin(string assemblyName, string pluginName) {

        try {

            // Load the named private assembly.
            Assembly assembly = Assembly.Load(assemblyName);

            // Create the IPlugin instance, ignore case.
            IPlugin plugin = 
                (IPlugin)assembly.CreateInstance(pluginName, true);

            if (plugin != null) {

                // Add new IPlugin to ListDictionary
                plugins[pluginName] = plugin;

                return true;

            } else {
                return false;
            }
        } catch {
            return false;
        }
    }

    public void StartPlugin(string plugin) {

        // Extract the IPlugin from the ListDictionary and call Start.
        ((IPlugin)plugins[plugin]).Start();
    }

    public void StopPlugin(string plugin) {

        // Extract the IPLugin from the ListDictionary and call Stop.
        ((IPlugin)plugins[plugin]).Stop();
    }

    public ArrayList GetPluginList() {

        // Return an enumerable list of plug-in names. Take the keys
        // and place them in an ArrayList, which supports marshal-by-value.
        return new ArrayList(plugins.Keys);
    }
}

public class CreateInstanceExample {

    public static void Main() {

        // Create a new application domain.
        AppDomain domain1 = AppDomain.CreateDomain("NewAppDomain1");

        // Create a PluginManager in the new application domain using 
        // the default constructor.
        PluginManager manager1 = 
            (PluginManager)domain1.CreateInstanceAndUnwrap(
            "CreateInstanceExample", "PluginManager");

        // Load a new plug-in into NewAppDomain1.
        manager1.LoadPlugin("CreateInstanceExample", "SimplePlugin");

        // Start and stop the plug-in in NewAppDomain1.
        manager1.StartPlugin("SimplePlugin");
        manager1.StopPlugin("SimplePlugin");

        // Create a new application domain.
        AppDomain domain2 = AppDomain.CreateDomain("NewAppDomain2");

        // Create a ListDictionary containing a list of plug-ins to create.
        ListDictionary pluginList = new ListDictionary();
        pluginList["SimplePlugin"] = "CreateInstanceExample";

        // Create a PluginManager in the new application domain and 
        // specify the default list of plug-ins to create.
        PluginManager manager2 = 
            (PluginManager)domain1.CreateInstanceAndUnwrap(
            "CreateInstanceExample", "PluginManager", true, 0,
            null, new object[] {pluginList}, null, null, null);

        // Display the list of plug-ins loaded into NewAppDomain2.
        Console.WriteLine("Plugins in NewAppDomain2:");
        foreach (string s in manager2.GetPluginList()) {
            Console.WriteLine(" - " + s);
        }

        // Wait to continue
        Console.ReadLine();
    }
}

Execute an Assembly in a Different Application Domain

If you have an executable assembly that you want to load and run in an application domain, the ExecuteAssembly method provides the easiest solution. The ExecuteAssembly method provides four overloads. The simplest overload takes only a string containing the name of the executable assembly to run; you can specify a local file or a URL. Other ExecuteAssembly overloads allow you to specify evidence for the assembly and arguments to pass to the assembly's entry point (equivalent to command-line arguments).
The ExecuteAssembly method loads the specified assembly and executes the method defined in metadata as the assembly's entry point (usually the Main method). If the specified assembly isn't executable, 
ExecuteAssembly throws a System.Runtime.InteropServices.COMException. The CLR doesn't start execution of the assembly in a new thread, so control won't return from the ExecuteAssembly method until the newly executed assembly exits. Because the ExecuteAssembly method loads an assembly using partial information (only the filename), the CLR won't use the GAC or probing to resolve the assembly.
The following example demonstrates the use of the ExecuteAssembly method to load and run an assembly. The ExecuteAssemblyExample class creates an AppDomain and executes itself in that AppDomain using the ExecuteAssembly method. This results in two copies of the ExecuteAssemblyExample assembly loaded into two different application domains.
 
using System;

public class ExecuteAssemblyExample {

public static void Main(string[] args) {

        // For the purpose of this example, if this assembly is executing
        // in an AppDomain with the friendly name "NewAppDomain", do not 
        // create a new AppDomain. This avoids an infinite loop of 
        // AppDomain creation.
        if (AppDomain.CurrentDomain.FriendlyName != "NewAppDomain") {

            // Create a new application domain
            AppDomain domain = AppDomain.CreateDomain("NewAppDomain");

            // Execute this assembly in the new application domain and
            // pass the array of command-line arguments.
            domain.ExecuteAssembly("ExecuteAssemblyExample.exe", 
                null, args);
        }

        // Display the command-line arguments to the screen prefixed with
        // the friendly name of the AppDomain.
        foreach (string s in args) {

            Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + 
                " : " + s);
        }
    }
}

Load an Assembly into the Current Application Domain

The CLR will automatically load the assemblies identified at build time as being referenced by your assembly. However, you can also explicitly instruct the runtime to load assemblies. The Load and LoadFrom methods both result in the runtime loading an assembly into the current application domain, and both return an Assembly instance that represents the newly loaded assembly. The difference between each method is the arguments that you must provide to identify the assembly to load, and the process that the runtime undertakes to locate the specified assembly.
The Load method provides overloads that allow you to specify the assembly to load using one of the following:
  • A string containing the fully or partially qualified display name of the assembly
  • A System.Reflection.AssemblyName containing details of the assembly
  • A byte array containing the raw bytes that constitute the assembly
Most often, you will use a display name to load an assembly. A fully qualified display name contains the assembly's text name, version, culture, and public key token, separated by commas (for example, System.Data, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089). To specify an assembly that doesn't have a strong name, use PublicKeyToken=null. You can also specify a partial display name, but as a minimum, you must specify the assembly name (without the file extension). The following code demonstrates various uses of the Load method.
// Load the System.Data assembly using a fully 
// qualified display name.
string name1 = "System.Data,Version=1.0.5000.0," + 
    "Culture=neutral,PublicKeyToken=b77a5c561934e089";
Assembly a1 = Assembly.Load(name1);

// Load the System.Xml assembly using an AssemblyName.
AssemblyName name2 = new AssemblyName();
name2.Name = "System.Xml";
name2.Version = new Version(1,0,5000,0);
name2.CultureInfo = new CultureInfo("");
name2.SetPublicKeyToken(
    new byte[] {0xb7,0x7a,0x5c,0x56,0x19,0x34,0xe0,0x89});
Assembly a2 = Assembly.Load(name2);

// Load the SomeAssembly assembly using a partial display name
Assembly a3 = Assembly.Load("SomeAssembly");
In response to the Load call, the runtime undertakes an extensive process to locate and load the specified assembly. Here's a summary; consult the .NET Framework SDK documentation for more details.
  1. If you specify a strong-named assembly, the Load method will apply version policy and publisher policy to enable requests for one version of an assembly to be satisfied by another version. Version policy is specified in your machine or application configuration file using <bindingRedirect> elements. Publisher policy is specified in special resource assemblies installed into the global assembly cache (GAC).
  2. Once the runtime has established the correct version of an assembly to use, it attempts to load strong-named assemblies from the GAC.
  3. If the assembly isn't strong named or isn't found in the GAC, the runtime looks for applicable <codeBase> elements in your machine and application configuration files. A <codeBase> element maps an assembly name to a file or a URL. If the assembly is strong named, <codeBase> can refer to any location including Internet-based URLs; otherwise, <codeBase> must refer to a directory relative to the application directory. If the assembly doesn't exist at the specified location, Load throws a System.IO.FileNotFoundException.
  4. If there are no <codeBase> elements relevant to the requested assembly, the runtime will locate the assembly using probing. Probing looks for the first file with the assembly's name (with either a .dll or .exe extension) in the following locations:
    • the application root directory
    • directories below the application root that match the assembly's name and culture
    • directories below the application root that are specified in the private binpath
The Load method is the easiest way to locate and load assemblies, but can also be expensive in terms of processing if the runtime needs to start probing many directories for a weak-named assembly. The LoadFrom method allows you to load an assembly from a specific location. If the specified file isn't found, the runtime will throw a FileNotFoundException. The runtime won't attempt to locate the assembly in the same way as the Load method—LoadFrom provides no support for the GAC, policies, <codebase> elements, or probing. This code demonstrates use of the LoadFrom method to load the assembly named c:\shared\MySharedAssembly.dll. Notice that unlike the Load method, LoadFrom requires that you specify the extension of the assembly file.
// Load the assembly named c:\shared\MySharedAssembly.dll
Assembly a4 = Assembly.LoadFrom(@"c:\shared\MySharedAssembly.dll");

Create a Type That Can't Cross Application Domain Boundaries

Sometimes, you may want to ensure that instances of a type can't transcend application domain boundaries. To create a nonremotable type, ensure that it isn't serializable and that it doesn't derive (directly or indirectly) from the MarshalByRefObject class. If you take these steps, you ensure that an object's state can never be accessed from outside the application domain in which the object was instantiated—such objects can't be used as arguments or return values in cross-application domain method calls.
Ensuring that a type isn't serializable is easy because a class doesn't inherit the ability to be serialized from its parent class. To ensure that a type isn't serializable, make sure it doesn't have  
System.SerializableAttribute
applied to the type declaration.
Ensuring that a class can't be passed by reference requires a little more attention. Many classes in the .NET class library derive directly or indirectly from MarshalByRefObject; you must be careful that you don't inadvertently derive your class from one of these. Commonly used base classes that derive from MarshalByRefObject include System.ComponentModel.Component, System.IO.Stream, System.IO.TextReader, System.IO.TextWriter, System.NET.WebRequest, and System.Net.WebResponse. (Check the .NET Framework SDK documentation for a list of classes derived from MarshalByRefObject.)

Creating an Application Domain

The simplest overload of the CreateDomain method takes a single string argument specifying a human-readable name (friendly name) for the new application domain. Other overloads allow you to specify evidence and configuration settings for the new application domain. Evidence is specified using a System.Security.Policy.Evidence object; in future we will discuss the effects of providing evidence when you create an application domain. Configuration settings are specified using a System.AppDomainSetup object.
The AppDomainSetup class is a container of configuration information for an application domain. Table 3.1 lists some of the properties of the AppDomainSetup class that you will use most often when creating application domains. These properties are accessible after creation through members of the AppDomain object, and some are modifiable at run time; refer to the .NET Frameworks SDK documentation on the AppDomain class for a comprehensive discussion.
Table 3.1: Commonly Used AppDomainSetup Properties
Property
Description
ApplicationBase
The directory where the CLR will look during probing to resolve private assemblies. Probing is discussed in recipe 3.5. Effectively, ApplicationBase is the root directory for the executing application. By default, this is the directory containing the assembly. Readable after creation using the AppDomain.BaseDirectory property.
ConfigurationFile
The name of the configuration file used by code loaded into the application domain. Readable after creation using the AppDomain.GetData method with the key APP_CONFIG_FILE.
DisallowPublisherPolicy
Controls whether the publisher policy section of the application configuration file is taken into consideration when determining which version of a strong named assembly to bind to. Publisher policy is discussed in recipe 3.5.
PrivateBinPath
A semicolon-separated list of directories that the runtime uses when probing for private assemblies. These directories are relative to the directory specified in ApplicationBase. Readable after application domain creation using the AppDomain.RelativeSearchPath property. Modifiable at run time using the AppendPrivatePath and ClearPrivatePath methods.
The following example demonstrates the creation and configuration of an application domain.
// Instantiate an AppDomainSetup object.
AppDomainSetup setupInfo = new AppDomainSetup();

// Configure the application domain setup information.
setupInfo.ApplicationBase = @"C:\MyRootDirectory";
setupInfo.ConfigurationFile = "MyApp.config";
setupInfo.PrivateBinPath = "bin;plugins;external";

// Create a new application domain passing null as the evidence 
// argument. Remember to save a reference to the new AppDomain as 
// this cannot be retrieved any other way.
AppDomain newDomain = AppDomain.CreateDomain(
    "My New AppDomain",
    new System.Security.Policy.Evidence(),
    setupInfo);

Connecting to an ODBC Data Source

Use the ODBC .NET data provider to access data exposed through an ODBC driver. The sample code contains a single event handler: Connect Button.Click Creates an OdbcDataAdapter and uses it to fill a DataTable with the Category table from the Northwind sample database. The default view of the table is bound to a data grid on the form.
OdbcConnectForm.cs// Namespaces, variables, and constants using System; using System.Configuration; using System.Data; using System.Data.Odbc; // . . . private void connectButton_Click(object sender, System.EventArgs e) { // Create the DataAdapter. String sqlSelect = "SELECT CategoryID, CategoryName, Description " + "FROM Categories"; OdbcDataAdapter da = new OdbcDataAdapter(sqlSelect, ConfigurationSettings.AppSettings["Odbc_ConnectString"]); // Create the table, fill it, and bind the default view to the grid. DataTable dt = new DataTable( ); da.Fill(dt); dataGrid.DataSource = dt.DefaultView; } The ODBC .NET data provider communicates with native ODBC drivers through COM interop . The following ODBC providers are guaranteed to be compatible with the ODBC.NET data provider: Microsoft SQL Server ODBC Driver Microsoft ODBC Driver for Oracle Microsoft Access (Jet) ODBC Driver The .NET data provider for ODBC connects to ODBC data sources through the OdbcConnection object. The ODBC driver connection string is specified using the ConnectionString property. It includes settings needed to establish the connection to the data source. The connection string format closely matches the ODBC connection string format. Additionally, you can specify an ODBC data source name (DSN) or file DSN by setting the ConnectionString attribute to "DSN=myDSN" or "FileDSN=myFileDSN". For more information about specifying ODBC connection strings, see the topic "SQLDriverConnect" in the ODBC Programmer's Reference within MSDN Library. Visual Studio also supports creating ODBC data source connections visually: Create a data connection in Server Explorer and drag it onto a form or design surface. Configure the OdbcConnection object that appears in the component tray. Drag an OdbcConnection from the Data tab of the Toolbox onto a form or design surface. Configure the ConnectionString property in the Properties window of the OdbcConnection object that appears. The .NET ODBC data provider requires a reference to the System.Data.Odbc namespace in .NET Framework Version 1.1. In Version 1.0, the namespace is Microsoft.Data.Odbc. Add a .NET Reference to Microsoft.Data.Odbc.dll for a .NET Framework Version 1.0 project.

Avoid Loading Unnecessary Assemblies into Application Domains

When you pass a marshal-by-value (MBV) object across application domain boundaries, the runtime creates a new instance of that object in the destination application domain. This means that the runtime must load the assembly containing that type metadata into the application domain. Passing MBV references across intermediate application domains can result in the runtime loading unnecessary assemblies into application domains. Once loaded, these superfluous assemblies can't be unloaded without unloading the containing application domain.
The ObjectHandle class allows you to wrap an object reference so that you can pass it between application domains without the runtime loading additional assemblies. When the object reaches the destination application domain, you can unwrap the object reference, causing the runtime to load the required assembly and allowing you to access the object as normal. To wrap an object (such as a System.Data.DataSet), use the following statement:// Create a new DataSet. System.Data.DataSet data1 = new System.Data.DataSet(); // Configure/populate the DataSet. § // Wrap the DataSet. System.Runtime.Remoting.ObjectHandle objHandle = new System.Runtime.Remoting.ObjectHandle(data1); To unwrap the object, use the ObjectHandle.Unwrap method and cast the returned object to the correct type, as shown here. // Unwrap the DataSet System.Data.DataSet data2 = (System.Data.DataSet)objHandle.Unwrap();

Pass Objects Across Application Domain Boundaries

The .NET Remoting system makes passing objects across application domain boundaries straightforward. However, to those unfamiliar with .NET Remoting, the results can be very different from those expected. In fact, the most confusing aspect of using multiple application domains stems from the interaction with .NET Remoting and the way objects traverse application domain boundaries. All types fall into one of three categories: nonremotable, marshal-by-value (MBV), or marshal-by-reference (MBR). Nonremotable types can't cross application domain boundaries and can't be used as arguments or return values in cross-application domain calls. MBV types are serializable types. When you pass an MBV object across an application domain boundary as an argument or return value, the .NET Remoting system serializes the object's current state, passes it to the destination application domain, and creates a new copy of the object with the same state as the original. This results in a copy of the MBV object existing in both application domains. The two instances are initially identical, but they are independent; changes made to one instance are not reflected in the other instance. Here's an example of a serializable type named Employee that's passed by value across application domain boundaries. public class Employee { // Member implementations § } MBR types are those classes that derive from System.MarshalByRefObject. When you pass an MBR object across an application domain boundary as an argument or return value, the .NET Remoting system creates a proxy in the destination application domain that represents the remote MBR object. To any class in the destination application domain, the proxy looks and behaves like the remote MBR object that it represents. In reality, when a call is made against the proxy, the .NET Remoting system transparently passes the call and its arguments to the remote application domain and issues the call against the original object. Any results are passed back to the caller via the proxy. Here's a version of the Employee class that's passed by reference instead of by value. public class Employee : System.MarshalByRefObject { // Member implementations § }

Store a Serializable Object to a File

Using the BinaryFormatter and SoapFormatter classes, you can serialize an instance of any type that's decorated with the attribute System.SerializableAttribute. The BinaryFormatter produces a binary data stream representing the object and its state, whereas the SoapFormatter produces a SOAP document.
Both the BinaryFormatter and SoapFormatter classes implement the interface System.Runtime.Serialization.IFormatter, which defines two methods: Serialize and Deserialize. The Serialize method takes a System.IO.Stream reference and a System.Object reference as arguments, serializes the Object, and writes it to the Stream. The Deserialize method takes a Stream reference as an argument, reads the serialized object data from the Stream, and returns an Object reference to a deserialized object. You must cast the returned Object reference to the correct type.

Important 
To call the Serialize and Deserialize methods of the BinaryFormatter class, your code must be granted the SerializationFormatter element of the permission System.Security.Permissions.SecurityPermission.
To call the Serialize and Deserialize methods of the SoapFormatter class, your code must be granted full trust because the System.Runtime.Serialization.Formatters.Soap.dll assembly in which the SoapFormatter class is declared does not allow partially trusted callers. 
The BinarySerializationExample class listed here demonstrates the use of a BinaryFormatter to serialize a System.Collections.ArrayList containing a list of people to a file. The ArrayList is then deserialized from the file and the contents displayed to the console.
using System.IO;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;

public class BinarySerializationExample {

    public static void Main() {
        
        // Create and configure the ArrayList to serialize
        ArrayList people = new ArrayList();
        people.Add("Graeme");
        people.Add("Lin");                    
        people.Add("Andy");

        // Serialize the ArrayList object
        FileStream str = File.Create("people.bin");
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(str, people);
        str.Close();
        
        // Deserialize the ArrayList object
        str = File.OpenRead("people.bin");
        bf = new BinaryFormatter();
        people = (ArrayList)bf.Deserialize(str);
        str.Close();
        
        // Display the contents of the deserialized ArrayList object
        foreach (string s in people) {
            
            System.Console.WriteLine(s);
        }
    }   
}
You can use a SoapFormatter class in exactly the same way as shown in the BinarySerializationExample class; all you need to do is replace each instance of BinaryFormatter with SoapFormatter and change the using directives to import the System.Runtime.Serialization.Formatters.Soap namespace. You must also include a reference to the System.Runtime.Serialization.Formatters.Soap.dll assembly when you compile the code. The file SoapSerializationExample.cs in the sample code for this chapter contains an example of how to use the SoapFormatter class.
To illustrate the different results achieved using the BinaryFormatter and SoapFormatter classes, Figure 2.1 shows the contents of the people.bin file generated using the BinaryFormatter class, whereas Figure 2.2 shows the contents of the people.xml file generated using the SoapFormatter class.
Figure 2.1: Contents of the people.bin file.
 
Figure 2.2: Contents of the people.xml file.

Create a Strongly Typed Collection

The CollectionBase and DictionaryBase classes provide convenient base classes from which to derive type-safe collections without having to implement the standard IDictionary, IList, ICollection, and IEnumerable interfaces from scratch.
CollectionBase is for IList-based collections (such as ArrayList). Internally, CollectionBase maintains the collection using a standard ArrayList object, which is accessible through the protected property List. DictionaryBase is for IDictionary-based collections (such as Hashtable). Internally, DictionaryBase maintains the collection using a standard Hashtable object, which is accessible through the protected property Dictionary. The following code shows the implementation of a strongly typed collection (based on the CollectionBase class) to represent a list of System.Reflection.AssemblyName objects.
using System.Reflection;
using System.Collections;

public class AssemblyNameList : CollectionBase {

    public int Add(AssemblyName value) {

        return this.List.Add(value);
    }

    public void Remove(AssemblyName value) {

        this.List.Remove(value);
    }

    public AssemblyName this[int index] { 

        get {
            return (AssemblyName)this.List[index];
        }

        set {
            this.List[index] = value;
        }
    }

    public bool Contains(AssemblyName value) {

        return this.List.Contains(value);
    }

    public void Insert(int index, AssemblyName value) {

        this.List.Insert(index, value);
    }
}
Both the CollectionBase and DictionaryBase classes implement a set of protected methods with the prefix On*. These methods—such as OnClear, OnClearComplete, OnGet, OnGetComplete, and so on—are intended to be overridden by a derived class and allow you to implement any custom functionality necessary to manage the strongly typed collection. The CollectionBase and DictionaryBase classes call the appropriate method before and after modifications are made to the underlying collection through the List or Dictionary properties.

Copy a Collection to an Array

The ICollection.CopyTo method and the ToArray method perform roughly the same function—they perform a shallow copy of the elements contained in a collection to an array. The key difference is that CopyTo copies the collection's elements to an existing array, whereas ToArray creates a new array before copying the collection's elements into it.
The CopyTo method takes two arguments: an array and an index. The array is the target of the copy operation and must be of a type appropriate to handle the elements of the collection. If the types don't match or there is no implicit conversion possible from the collection element's type to the array element's type, a System.InvalidCastException is thrown. The index is the starting element of the array where the collection's elements will be copied. If the index is equal to or greater than the length of the array, or the number of collection elements exceeds the capacity of the array, a System.ArgumentException is thrown. This code excerpt shows how to copy the contents of an ArrayList to an array using the CopyTo method.
// Create a new ArrayList and populate it.
ArrayList list = new ArrayList(5);
list.Add("Brenda");
list.Add("George");
list.Add("Justin");
list.Add("Shaun");
list.Add("Meaghan");

// Create a string[] and use the ICollection.CopyTo method to 
// copy the contents of the ArrayList.
string[] array1 = new string[5];
list.CopyTo(array1,0);        
The ArrayList, Stack, and Queue classes also implement the ToArray method, which automatically creates an array of the correct size to accommodate a copy of all the elements of the collection. If you call ToArray with no arguments, it returns an object[] regardless of the type of objects contained in the collection. However, you can pass a System.Type object that specifies the type of array that the ToArray method should create. (You must cast the returned strongly typed array to the correct type.) Here is an example of how to use the ToArray method on the ArrayList created in the previous listing.
// Use ArrayList.ToArray to create an object[] from the contents
// of the collection.
object[] array2 = list.ToArray();

// Use ArrayList.ToArray to create a strongly typed string[] from 
// the contents of the collection.
string[] array3 = 
    (string[])list.ToArray(System.Type.GetType("System.String"));

Sort an Array or an ArrayList

The simplest Sort method overload sorts the objects contained in an array or ArrayList as long as the objects implement the System.IComparable interface and are of the same type—all of the basic data types implement IComparable. The following code excerpt demonstrates how to use the Sort method:
// Create a new array and populate it.
int[] array = {4, 2, 9, 3};

// Sort the array
Array.Sort(array);

// Display the contents of the sorted array 
foreach (int i in array) { Console.WriteLine(i);}

// Create a new ArrayList and populate it.
ArrayList list = new ArrayList(4);
list.Add("Michael");
list.Add("Kate");
list.Add("Andrea");
list.Add("Angus");

// Sort the ArrayList
list.Sort();

// Display the contents of the sorted ArrayList
foreach (string s in list) { Console.WriteLine(s);}
To sort objects that don't implement IComparable, you must pass the Sort method an object that implements the System.Collections.IComparer interface. The IComparer implementation must be capable of comparing the objects contained within the array or ArrayList. (Recipe 16.3 describes how to implement both the IComparable and IComparer interfaces.)

Add, Subtract, and Compare Dates and Times

A DateTime instance represents a specific time (such as 4:15 A.M. on September 5, 1970), whereas a TimeSpan instance represents a period of time (such as 2 hours, 35 minutes). You will often want to add, subtract, and compare TimeSpan and DateTime instances.
Internally, both DateTime and TimeSpan use ticks to represent time—a tick is equal to 100 nanoseconds. TimeSpan stores its time interval as the number of ticks equal to that interval, and DateTime stores time as the number of ticks since 12:00:00 midnight on January 1 in 0001 C.E. (C.E. stands for Common Era and is equivalent to A.D. in the Gregorian Calendar.) This approach and the use of operator overloading makes it easy for DateTime and TimeSpace to support basic arithmetic and comparison operations. Table2.4 summarizes the operator support provided by the DateTime and TimeSpan structures.
Table 2.4: Operators Supported by the DateTime and TimeSpan
Operator
TimeSpan
DateTime
Assignment (=)
Because TimeSpan is a structure, assignment returns a copy and not a reference.
Because DateTime is a structure, assignment returns a copy and not a reference.
Addition (+)
Adds two TimeSpan instances.
Adds a TimeSpan to a DateTime.
Subtraction (-)
Subtracts one TimeSpan instance from another.
Subtracts a TimeSpan or a DateTime from a DateTime.
Equality (==)
Compares two TimeSpan instances and returns true if they are equal.
Compares two DateTime instances and returns true if they are equal.
Inequality (!=)
Compares two TimeSpan instances and returns true if they aren't equal.
Compares two DateTime instances and returns true if they aren't equal.
Greater Than (>)
Determines if one TimeSpan is greater than another TimeSpan.
Determines if one DateTime is greater than another DateTime.
Greater Than or Equal (>=)
Determines if one TimeSpan is greater than or equal to another TimeSpan.
Determines if one DateTime is greater than or equal to another DateTime.
Less Than (<)
Determines if one TimeSpan is less than another TimeSpan.
Determines if one DateTime is less than another DateTime.
Less Than or Equal (<=)
Determines if one TimeSpan is less than or equal to another TimeSpan.
Determines if one DateTime is less than or equal to another DateTime.
Unary Negation (-)
Returns a TimeSpan with a negated value of the specified TimeSpan.
Not Supported.
Unary Plus (+)
Returns the TimeSpan specified.
Not Supported.
The DateTime structure also implements the methods AddTicks, AddMilliseconds, AddSeconds, AddMinutes, AddHours, AddDays, AddMonths, and AddYears. Each of these methods allows you to add (or subtract using negative values) the appropriate element of time to a DateTime instance. These methods and the operators listed in Table 2.4 don't modify the original DateTime— instead they create a new instance with the modified value. The following code demonstrates the use of operators to manipulate the DateTime and TimeSpan structures.
// Create a TimeSpan representing 2.5 days
TimeSpan timespan1 = new TimeSpan(2,12,0,0);
// Create a TimeSpan representing 4.5 days
TimeSpan timespan2 = new TimeSpan(4,12,0,0);
// Create a TimeSpan representing 1 week
TimeSpan oneWeek = timespan1 + timespan2;

// Create a DateTime with the current date and time
DateTime now = DateTime.Now;
// Create a DateTime representing 1 week ago
DateTime past = now - oneWeek;
// Create a DateTime representing 1 week in the future
DateTime future = now + oneWeek;

Creating Dates and Times from Strings

There are many different ways to represent dates and times as text. For example, 1st June 2003, 1/6/2003, 6/1/2003, and 1-Jun-2003 are all possible representations of the same date, and 16:43 and 4:43pm can both be used to represent the same time. The static DateTime.Parse method provides a flexible mechanism by which to create DateTime instances from a wide variety of string representations.
The Parse method goes to great lengths to generate a DateTime from a given string. It will even attempt to generate a DateTime from a string containing partial or erroneous information and will substitute defaults for those values it finds missing. Missing date elements default to the current date, and missing time elements default to 12:00:00 am. After all efforts, if Parse can't create a DateTime, it throws a System.FormatException. The following code demonstrates the flexibility of the Parse method.
// 01/09/2001 00:00:00
DateTime dt1 = DateTime.Parse("Sep 2001");

// 05/09/2001 14:15:33
DateTime dt2 = DateTime.Parse("Wed 5 September 2001 14:15:33");

// 05/09/2001 00:00:00
DateTime dt3 = DateTime.Parse("5,9,01");

// 05/09/2001 14:15:33
DateTime dt4 = DateTime.Parse("5/9/2001 14:15:33");

// 01/07/2003 14:15:00
DateTime dt5 = DateTime.Parse("2:15 PM");
The Parse method is both flexible and forgiving. However, for many applications this level of flexibility is unnecessary. Often you will want to ensure that DateTime parses only strings that match a specific format. In these circumstances, use the ParseExact method instead of Parse. The simplest overload of the ParseExact method takes three arguments: the time and date string to parse, a format string that specifies the structure that the time and date string must have, and an IFormatProvider reference that provides culture specific information to the ParseExact method. If the IFormatProvider is null, the current thread's culture information is used.
The time and date must meet the requirements specified in the format string or ParseExact will throw a System.FormatException. You use the same format specifiers for the format string as you use to format a DateTime for display as a string. This means you can use both standard and custom format specifiers. The following code demonstrates the use of the ParseExact method. Refer to the documentation for the System.Globalization.DateTimeFormatInfo class in the .NET Framework SDK document for complete details on all available format specifiers.
// Parse only strings containing LongTimePattern
DateTime dt6 = DateTime.ParseExact("2:13:30 PM",
    "h:mm:ss tt", null);

// Parse only strings containing RFC1123Pattern
DateTime dt7 = DateTime.ParseExact(
    "Wed, 05 Sep 2001 14:13:30 GMT",
    "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'", null);

// Parse only strings containing MonthDayPattern
DateTime dt8 = DateTime.ParseExact("September 03",
    "MMMM dd", null);

Use Compiled Regular Expressions

By default, when you create a Regex object, the regular expression pattern you specify in the constructor is compiled to an intermediate form (not MSIL). Each time you use the Regex object, the runtime interprets the pattern's intermediate form and applies it to the target string. With complex regular expressions that are used frequently, this repeated interpretation process can have a detrimental effect on the performance of your application. By specifying the RegexOptions.Compiled option when you create a Regex object, you force the .NET runtime to compile the regular expression to MSIL, instead of the interpreted intermediary form. This MSIL is just-in-time (JIT) compiled by the runtime to native machine code on first execution—just like regular assembly code. You use a compiled regular expression in the same way as you use any Regex object; compilation simply results in faster execution. However, there are downsides to offset the performance benefits provided by compiling regular expressions. First, the JIT compiler has to do more work, which will introduce delays during JIT compilation. This is most noticeable if you create your compiled regular expressions as your application starts up. Second, the runtime can't unload a compiled regular expression once you have finished with it. Unlike a normal regular expression, the runtime's garbage collector won't reclaim the memory used by the compiled regular expression. The compiled regular expression will remain in memory until your program terminates or you unload the application domain in which the compiled regular expression is loaded. This code shows how to create a Regex object that's compiled to MSIL instead of the usual intermediate form. Regex reg = new Regex(@"[\w-]+@([\w-]+\.)+[\w-]+", RegexOptions.Compiled); In addition, the static Regex.CompileToAssembly method allows you to create a compiled regular expression and write it to an external assembly. This means you can create assemblies containing standard sets of regular expressions that you can use from multiple applications. To compile a regular expression and persist it to an assembly, take the following steps: Create a System.Text.RegularExpressions.RegexCompilationInfo array large enough to hold one RegexCompilationInfo object for each of the compiled regular expressions you want to create. Create a RegexCompilationInfo object for each of the compiled regular expressions, and specify values for its properties as arguments to the object constructor; the most commonly used properties are IsPublic, a bool that specifies whether the generated regular expression class has public visibility. Name, a String that specifies the class name. Namespace, a String that specifies the namespace of the class. Pattern, a String that specifies the pattern that the regular expression will match. (See recipe 2.5 for more details.) Options, a System.Text.RegularExpressions.RegexOptions value that specifies options for the regular expression. Create a System.Reflection.AssemblyName object, and configure it to represent the name of the assembly that the Regex.CompileToAssembly method will create. Execute Regex.CompileToAssembly, passing the RegexCompilationInfo array and the AssemblyName object. This process creates an assembly that contains one class declaration for each compiled regular expression—each class derives from Regex. To use the compiled regular expression contained in the assembly, instantiate the regular expression you want to use and call its method as if you had simply created it with the normal Regex constructor. (Remember to add a reference to the assembly when you compile the code that uses the compiled regular expression classes.) The following code shows how to create an assembly named MyRegEx.dll, which contains two regular expressions named PinRegex and CreditCardRegex. using System.Text.RegularExpressions; using System.Reflection; public class CompiledRegexExample { public static void Main() { // Create the array to hold the Regex info objects RegexCompilationInfo[] regexInfo = new RegexCompilationInfo[2]; // Create the RegexCompilationInfo for PinRegex regexInfo[0] = new RegexCompilationInfo(@"^\d{4}$", RegexOptions.Compiled, "PinRegex", "", true); // Create the RegexCompilationInfo for CreditCardRegex regexInfo[1] = new RegexCompilationInfo( @"^\d{4}-?\d{4}-?\d{4}-?\d{4}$", RegexOptions.Compiled, "CreditCardRegex", "", true); // Create the AssemblyName to define the target assembly AssemblyName assembly = new AssemblyName(); assembly.Name = "MyRegEx"; // Create the compiled regular expression Regex.CompileToAssembly(regexInfo, assembly); } }

Validate Input Using Regular Expressions in Csharp

When a user inputs data to your application or your application reads data from a file, it's good practice to assume that the data is bad until you have verified its accuracy. One common validation requirement is to ensure that data such as e- mail addresses, telephone numbers, and credit card numbers follow the pattern and content constraints expected of such data. Obviously you can't be sure the data entered is valid until you use it, or compare it against values that are known to be correct, but ensuring the data has the correct structure and content is a good first step to determining whether the input is accurate. Regular expressions provide an excellent mechanism for evaluating strings for the presence of patterns; you can use this to your advantage when validating input data. The first thing you must do is figure out the regular expression syntax that will correctly match the structure and content of data you are trying to validate. This is by far the most difficult aspect of using regular expressions. Regular expressions are constructed from two types of elements: literals and metacharacters. Literals represent specific characters that appear in the pattern you want to match. Metacharacters provide support for wildcard matching, ranges, grouping, repetition, conditionals, and other control mechanisms. A full discussion of regular expression syntax is beyond the scope of this book, but Table 2. 2 describes some of the more commonly used elements. Table 2.2: Commonly Used Regular Expression Metacharacter Elements Element Description . Specifies any character except a new line (\n) \d Specifies any decimal digit \D Specifies any nondigit \s Specifies any white-space character \S Specifies any non-white-space character \w Specifies any word character \W Specifies any nonword character ^ Specifies the beginning of the string or line \A Specifies the beginning of the string $ Specifies the end of the string or line \z Specifies the end of the string Matches one of the expressions separated by the vertical bar; for example AAAABAABB will match one of AAA, ABA, or ABB. (The expression is evaluated left to right.) [abc] Specifies a match with one of the specified characters; for example [AbC] will match A, b, or C but no other character [^abc] Specifies a match with any one character except those specified; for example [^AbC] will not match A, b, or C but will match B, F and so on [a-z] Specifies a match with any one character in the specified range; for example [A-C] will match A, B, or C ( ) Identifies a subexpression so that it's treated as a single element by the regular expression elements described in this table ? Specifies one or zero occurrences of the previous character or subexpression; for example A?B matches B, AB, but not AAB * Specifies zero or more occurrences of the previous character or subexpression; for example, A*B matches B, AB, AAB, AAAB, and so on + Specifies one or more occurrences of the previous character or subexpression; for example, A+B matches AB, AAB, AAAB, and so on, but not B {n} Specifies exactly n occurrences of the preceding character or sub-expression; for example, A{2} matches only AA {n,} Specifies a minimum of n occurrences of the preceding character or subexpression; for example, A{2,} matches AA, AAA, AAAA and so on, but not A {n, m} Specifies a minimum of n and a maximum of m occurrences of the preceding character; for example, A{2,4} matches AA, AAA, and AAAA but not A or AAAAA The more complex the data you are trying to match, the more complex the regular expression syntax becomes. For example, ensuring that input contains only numbers or is of a minimum length is trivial, but ensuring a string contains a valid URL is extremely complex. Table 2.3 shows some example regular expressions that will match against commonly required data types. Table 2.3: Commonly Used Regular Expressions Input Type Description Regular Expression Numeric input The input consists of one or more decimal digits; for example "5", or "5683874674". ^\d+$ PIN The input consists of four decimal digits; for example "1234". ^\d{4}$ Simple password The input consists of between 6 through 8 characters; for example "ghtd6f" or "b8c7hogh". ^\w{6,8}$ Credit card number The input consists of data that matches the pattern of most major credit card numbers; for example "4921835221552042" or "4921-8352- 2155-2042". ^\d{4}-?\d{4}-?\d{4}- ?\d{4}$ E-mail address The input consists of an Internet e-mail address. The [\w-]+ expression indicates that each address element must consist of one or more word characters or hyphens; for example some-body@adatum.com. ^[\w-]+@([\w- ]+\.)+[\w-]+$ HTTP or HTTPS URL The input consists of an HTTP-based or HTTPS-based URL, for example http://www.microsoft.com. ^https?://([\w- ]+\.)+[\w-]+(/[\w- ./ ?%=]*)?$ Once you know the correct regular expression syntax, create a new System.Text.RegularExpressions.Regex object passing a string containing the regular expression to the Regex constructor. Then call the IsMatch method of the Regex object and pass the string that you want to validate; IsMatch returns a bool indicating whether the Regex object found a match in the string. The regular expression syntax determines whether the Regex will match only against the full string or whether it will match against patterns contained within the string. (See the ^, \A, $, and \z entries in Table 2.2.) The ValidateInput method shown here tests any input string to see if it matches a specified regular expression. public static bool ValidateInput(string regex, string input) { // Create a new Regex based on the specified regular expression. Regex r = new Regex(regex); // Test if the specified input matches the regular expression. return r.IsMatch(input); } You can use the Regex object repeatedly to test multiple strings, but you can't change the regular expression tested for by a Regex object; you must create a new Regex object to test for a different pattern. Because the ValidateInput method creates a new Regex each time it's called, instead you could use a static overload of the IsMatch method, as shown in the following variant of the ValidateInput method. public static bool ValidateInput(string regex, string input) { // Test if the specified input matches the regular expression. return Regex.IsMatch(input, regex); }

Encode Binary Data as Text in Csharp

Base64 is an encoding scheme that enables you to represent binary data as a series of ASCII characters so that it can be included in text files and e-mail messages in which raw binary data is unacceptable. Base64 encoding works by spreading the contents of 3 bytes of input data across 4 bytes and ensuring each byte uses only the 7 low-order bits to contain data. This means that each byte of Base64-encoded data is equivalent to an ASCII character and can be stored or transmitted anywhere ASCII characters are permitted. The ToBase64String and FromBase64String methods of the Convert class make it straightforward to Base64 encode and decode data. However, before Base64 encoding, you must convert your data to a byte array. Likewise, after decoding you must convert the byte array back to the appropriate data type;
The code shown here demonstrates how to Base64 encode and decode a Unicode string, an int, and a decimal using the Convert class. The DecimalToBase64 and Base64ToDecimal methods rely on the ByteArrayToDecimal and DecimalToByteArray methods listed . // Base64 encode a Unicode string public static string StringToBase64 (string src) { // Get a byte representation of the source string byte[] b = Encoding.Unicode.GetBytes(src); // Return the Base64-encoded string return Convert.ToBase64String(b); } // Decode a Base64-encoded Unicode string public static string Base64ToString (string src) { // Decode the Base64-encoded string to a byte array byte[] b = Convert.FromBase64String(src); // Return the decoded Unicode string return Encoding.Unicode.GetString(b); } // Base64 encode a decimal public static string DecimalToBase64 (decimal src) { // Get a byte representation of the decimal byte[] b = DecimalToByteArray(src); // Return the Base64-encoded decimal return Convert.ToBase64String(b); } // Decode a Base64-encoded decimal public static decimal Base64ToDecimal (string src) { // Decode the Base64-encoded decimal to a byte array byte[] b = Convert.FromBase64String(src); // Return the decoded decimal return ByteArrayToDecimal(b); } // Base64 encode an int public static string IntToBase64 (int src) { // Get a byte representation of the int byte[] b = BitConverter.GetBytes(src); // Return the Base64-encoded int return Convert.ToBase64String(b); } // Decode a Base64-encoded int public static int Base64ToInt (string src) { // Decode the Base64-encoded int to a byte array byte[] b = Convert.FromBase64String(src); // Return the decoded int return BitConverter.ToInt32(b,0); }

Convert Basic Value Types to Byte Arrays

The static method GetBytes of the BitConverter class provides overloads that take most of the standard value types and return the value encoded as an array of bytes. Support is provided for the bool, char, double, short, int, long, float, ushort, uint, and ulong data types. BitConverter also provides a set of static methods that support the conversion of byte arrays to each of the standard value types; these are named ToBoolean, ToUInt32, ToDouble, and so on. The following code (taken from the sample file ByteConversionExample.cs) demonstrates the use of BitConverter to convert a bool and an int to and from a byte array. The second argument to each of the ToBoolean and ToInt32 methods is a zero-based offset into the byte array where the BitConverter should start taking the bytes to create the data value.
byte[] b = null;

// Convert a bool to a byte array and display
b = BitConverter.GetBytes(true);
Console.WriteLine(BitConverter.ToString(b));

// Convert a byte array to a bool and display
Console.WriteLine(BitConverter.ToBoolean(b,0));

// Convert an int to a byte array and display
b = BitConverter.GetBytes(3678);
Console.WriteLine(BitConverter.ToString(b));

// Convert a byte array to an int and display
Console.WriteLine(BitConverter.ToInt32(b,0));
Unfortunately, BitConverter does not provide support for converting the decimal type. Instead, the following code shows how to convert a decimal to a byte array using a MemoryStream and a BinaryWriter.
// Create a byte array from a decimal
public static byte[] DecimalToByteArray (decimal src) {

    // Create a MemoryStream as a buffer to hold the binary data
    using (MemoryStream stream = new MemoryStream()) {

        // Create a BinaryWriter to write binary data to the stream
        using (BinaryWriter writer = new BinaryWriter(stream)) {

            // Write the decimal to the BinaryWriter/MemoryStream
            writer.Write(src);

            // Return the byte representation of the decimal 
            return stream.ToArray();
        }
    }
}
To convert a byte array to a decimal, use a BinaryReader to read from the MemoryStream, as shown in this code.
// Create a decimal from a byte array
public static decimal ByteArrayToDecimal (byte[] src) {

    // Create a MemoryStream containing the byte array
    using (MemoryStream stream = new MemoryStream(src)) {

        // Create a BinaryReader to read the decimal from the stream
        using (BinaryReader reader = new BinaryReader(stream)) {

            // Read and return the decimal from the 
            // BinaryReader/MemoryStream
            return reader.ReadDecimal();
        }
    }
}

Note 
The BitConverter.ToString method provides a convenient mechanism through which to obtain a String representation of a byte array. Calling ToString and passing a byte array as an argument will return a String containing the hexadecimal value of each byte in the array separated by a hyphen, for example "34-A7-2C". Unfortunately, there is no standard method for reversing this process to obtain a byte array from a string with this format.

Encode a String Using Alternate Character Encoding

Unicode is not the only character-encoding scheme, nor is UTF-16 the only way to represent Unicode characters. When your application needs to exchange character data with external systems (particularly legacy systems), you must convert the data between UTF-16 and the encoding scheme supported by the other system.
The abstract class Encoding, and its concrete subclasses, provide the functionality to convert characters to and from a variety of encoding schemes. Each subclass instance supports the conversion of characters between UTF-16 and one other encoding scheme. You obtain instances of the encoding specific classes using the static factory method Encoding.GetEncoding, which accepts either the name or the code page number of the required encoding scheme.
Table 2.1 lists some commonly used character encoding schemes and the code page number you must pass to the GetEncoding method to create an instance of the appropriate encoding class. The table also shows static properties of the Encoding class that provide shortcuts for obtaining the most commonly used types of encoding object.
Table 2.1: Character Encoding Classes
Encoding Scheme
Class
Create Using
ASCII
ASCIIEncoding
GetEncoding(20127) or the ASCII property
Default (current Microsoft Windows default)
Encoding
GetEncoding(0) or the Default property
UTF-7
UTF7Encoding
GetEncoding(65000) or the UTF7 property
UTF-8
UTF8Encoding
GetEncoding(65001) or the UTF8 property
UTF-16 (Big Endian)
UnicodeEncoding
GetEncoding(1201) or the BigEndianUnicode property
UTF-16 (Little Endian)
UnicodeEncoding
GetEncoding(1200) or the Unicode property
Windows OS
Encoding
GetEncoding(1252)
Once you have an Encoding object of the appropriate type, you convert a UTF-16 encoded Unicode string to a byte array of encoded characters using the GetBytes method and convert a byte array of encoded characters to a string using the GetString method. The following code demonstrates the use of some encoding classes.
using System;
using System.IO;
using System.Text;

public class CharacterEncodingExample {

    public static void Main() {
        
        // Create a file to hold the output
        using (StreamWriter output = new StreamWriter("output.txt")) {

            // Create and write a string containing the symbol for Pi
            string srcString = "Area = \u03A0r^2";
            output.WriteLine("Source Text : " + srcString);

            // Write the UTF-16 encoded bytes of the source string
            byte[] utf16String = Encoding.Unicode.GetBytes(srcString);
            output.WriteLine("UTF-16 Bytes: {0}", 
                BitConverter.ToString(utf16String));

            // Convert the UTF-16 encoded source string to UTF-8 and ASCII
            byte[] utf8String = Encoding.UTF8.GetBytes(srcString);
            byte[] asciiString = Encoding.ASCII.GetBytes(srcString);
            
            // Write the UTF-8 and ASCII encoded byte arrays        
            output.WriteLine("UTF-8  Bytes: {0}", 
                BitConverter.ToString(utf8String));
            output.WriteLine("ASCII  Bytes: {0}", 
                BitConverter.ToString(asciiString));

            // Convert UTF-8 and ASCII encoded bytes back to UTF-16 
            // encoded string and write
            output.WriteLine("UTF-8  Text : {0}", 
                Encoding.UTF8.GetString(utf8String));
            output.WriteLine("ASCII  Text : {0}", 
                Encoding.ASCII.GetString(asciiString));

            // Flush and close the output file
            output.Flush();
            output.Close();
        }
    }
}
Running CharacterEncodingExample will generate a file named output.txt. If you open this file in a text editor that supports Unicode, you will see the following content:
Source Text : Area = r^2
UTF-16 Bytes: 41-00-72-00-65-00-61-00-20-00-3D-00-20-00-A0-03-72-00-5E-00-32-00
UTF-8  Bytes: 41-72-65-61-20-3D-20-CE-A0-72-5E-32
ASCII  Bytes: 41-72-65-61-20-3D-20-3F-72-5E-32
UTF-8  Text : Area = r^2
ASCII  Text : Area = ?r^2
Notice that using UTF-16 encoding, each character occupies 2 bytes, but because most of the characters are standard characters, the high-order byte is 0. (The use of little-endian byte ordering means that the low-order byte appears first.) This means that most of the characters are encoded using the same numeric values across all three encoding schemes. However, the numeric value for the symbol pi (emphasized in bold in the preceding code) is different in each of the encodings. The value of pi requires more than one byte to represent—UTF-8 encoding uses 2 bytes, but ASCII has no direct equivalent and so replaces pi with the code 3F. As you can see in the text version of the string, 3F is the symbol for an English question mark (?).

Warning:
If you convert Unicode characters to ASCII or a specific code page encoding scheme, you risk losing data. Any Unicode character with a character code that can't be represented in the scheme will be ignored.
The Encoding class also provides the static method Convert to simplify the conversion of a byte array from one encoding scheme to another without the need to manually perform an interim conversion to UTF-16. For example, the following statement converts the ASCII encoded bytes contained in the asciiString byte array directly from ASCII encoding to UTF-8 encoding:
byte[] utf8String = Encoding.Convert(Encoding.ASCII, Encoding.UTF8,
    asciiString);

Prevent People from Decompiling Your Code

Because .NET assemblies consist of a standardized, platform-independent set of instruction codes and metadata that describes the types contained in the assembly, they are relatively easy to decompile. This allows decompilers to generate source code that is very close to your original code with ease, which can be problematic if your code contains proprietary information or algorithms that you want to keep secret.

The only way to ensure people can't decompile your assemblies is to stop people from getting your assemblies in the first place. Where possible, implement server-based solutions such as Microsoft ASP.NET applications and XML Web services. With the security correctly configured on your server, nobody will be able to access your assemblies and therefore won't be able to decompile them.

Where building a server solution is not appropriate, you have the following two options:

  • Use an obfuscator to make it difficult to decompile your code. (Visual Studio .NET 2003 includes the Community Edition of an obfuscator named Dotfuscator.) Obfuscators use a variety of techniques to make your assembly difficult to decompile; principal among these techniques are

    • Renaming of private methods and fields in such a way that it's difficult to read and understand the purpose of your code.

    • Inserting control flow statements to make the logic of your application difficult to follow.

  • Build the parts of your application that you want to keep secret in native DLLs or COM objects, and then call them from your managed application using P/Invoke or COM interop.

Neither approach will stop a skilled and determined person from reverse engineering your code, but they will make the job significantly more difficult and deter most casual observers.

The risks of application decompilation aren't specific to C# or .NET. A determined person can reverse engineer any software if he has the time and the skill.

Manage the Global Assembly Cache in Csharp

Before you can install an assembly in the GAC, the assembly must have a strong name. To install an assembly named SomeAssembly.dll into the GAC, use the command gacutil /i SomeAssembly.dll.

To uninstall the SomeAssembly.dll assembly from the GAC, use the command gacutil /u SomeAssembly. Notice that you don't use the .dll extension to refer to the assembly once it's installed in the GAC.

To view the assemblies installed in the GAC, use the command gacutil /l. This will produce a long list of all the assemblies installed in the GAC, as well as a listing of assemblies that have been precompiled to binary form and installed in the ngen cache. To avoid searching through this list to determine if a particular assembly is installed in the GAC, use the command gacutil /l SomeAssembly.

Note

The .NET Framework uses the GAC only at run time; the C# compiler won't look in the GAC to resolve any external references that your assembly references. During development, the C# compiler must be able to access a local copy of any referenced shared assemblies. You can either copy the shared assembly to the same directory as your source code or use the /lib switch of the C# compiler to specify the directory where the compiler can find the required assemblies.

Create and Trust a Test Software Publisher Certificate C#

To create a test SPC for a software publisher named Allen Jones, first create an X.509 certificate using the Certificate Creation tool. The command makecert - n "CN=Allen Jones" -sk MyKeys TestCertificate.cer creates a file named TestCertificate.cer containing an X.509 certificate and stores the associated private key in a CSP key container named MyKeys (which is automatically created if it does not exist). Alternatively, you can write the private key to a file by substituting the -sk switch with -sv. For example, to write the private key to a file named PrivateKeys.pvk use the command makecert -n "CN=Allen Jones" -sv PrivateKey.pvk TestCertificate.cer. If you write your private key to a file, the Certificate Creation tool will prompt you (as shown in Figure 1.5) to provide a password with which to protect the private key file.



Certificate Creation tool requests a password when accessing file-based private keys.
The Certificate Creation tool supports many arguments, and Table 1.3 lists some of the more useful ones. You should consult the .NET Framework SDK documentation for full coverage of the Certificate Creation tool.


Table 1.3: Commonly Used Switches of the Certificate Creation Tool
Switch
Description
-e
Specifies the date when the certificate becomes invalid
-m
Specifies the duration—in months—that the certificate remains valid
-n
Specifies an X.500 name to associate with the certificate. This is the name of the software publisher that people will see when they view details of the SPC you create
-sk
Specifies the name of the CSP key store in which to store the private key
-ss
Specifies the name of the certificate store where the Certificate Creation tool should store the generated X.509 certificate
-sv
Specifies the name of the file in which to store the private key

Once you have created your X.509 certificate with the Certificate Creation tool, you need to convert it to an SPC with the Software Publisher Certificate Test tool (cert2spc.exe). To convert the certificate TestCertificate.cer to an SPC, use the command cert2spc TestCertificate.cer TestCertificate.spc. The Software Publisher Certificate Test tool doesn't offer any optional switches.
The final step before you can use your test SPC is to trust the root test CA, which is the default issuer of the test certificate. The Set Registry tool (setreg.exe) makes this a simple task with the command setreg 1 true. When you have finished using your test SPC, you must remove trust of the root test CA using the command setreg 1 false.

Sign an Assembly with an Authenticode Digital Signature C #


Strong names provide a unique identity for an assembly as well as proof of the assembly's integrity, but they provide no proof as to the publisher of the assembly. The .NET Framework allows you to use Authenticode technology to sign your assemblies. This enables consumers of your assemblies to confirm that you are the publisher, as well as confirm the integrity of the assembly. Authenticode signatures also act as evidence for the signed assembly, which people can use when configuring code access security policy.

To sign your assembly with an Authenticode signature, you need an SPC issued by a recognized certificate authority (CA). A CA is a company entrusted to issue SPCs (along with many other types of certificates) for use by individuals or companies. Before issuing a certificate, the CA is responsible for confirming that the requesters are who they claim to be and also making sure the requestors sign contracts to ensure they don't misuse the certificates that the CA issues them.

To obtain an SPC, you should view the list of Microsoft Root Certificate Program Members at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/rootcertprog.asp. Here you will find a list of CAs, many of whom can issue you an SPC. For testing purposes, you can create a test SPC using the process. However, you can't distribute your software signed with this test certificate. Because a test SPC isn't issued by a trusted CA, most responsible users won't trust assemblies signed with it.
Once you have an SPC, you use the File Signing tool to sign your assembly. The File Signing tool creates a digital signature of the assembly using the private key component of your SPC and embeds the signature and the public part of your SPC into your assembly (including your public key). When verifying your assembly, the consumer decrypts the encrypted hash code using your public key, recalculates the hash of the assembly, and compares the two hash codes to ensure they are the same. As long as the two hash codes match, the consumer can be certain that you signed the assembly, and that it has not changed since you signed it.
To Authenticode sign an assembly named MyAssembly.exe with an SPC contained in a file named MyCert.spc and a private key contained in a file named MyPrivateKey.pvk, use the command signcode -spc MyCert.spc -v MyPrivateKey.pvk MyAssembly.exe. In this instance, the File Signing tool will display the dialog box shown in Figure 1.3, prompting you for the password used to protect the private key stored in the MyPrivateKey.pvk file.
Figure 1.3: File Signing tool requests a password when accessing file- based private keys.
You can also access keys and certificates contained in key and certificate stores. Table 1.2 lists the most commonly used switches of the File Signing tool. Refer to the .NET Framework SDK documentation for a complete listing.


Figure 1.3: File Signing tool requests a password when accessing file- based private keys.
 
You can also access keys and certificates contained in key and certificate stores. Table 1.2 lists the most commonly used switches of the File Signing tool. Refer to the .NET Framework SDK documentation for a complete listing.
Table 1.2: Commonly Used Switches of the File Signing Tool
Switch
Description
-k
Specifies the name of the CSP key container where your SPC private key is stored
-s
Specifies the name of the certificate store where your SPC is stored
-spc
Specifies the name of the file that contains your SPC
-v
Specifies the name of the file that contains your SPC private key

If you are signing a multi-file assembly, specify the name of the file that contains the assembly manifest. If you intend to both strong name and Authenticode sign your assembly, you must strong name the assembly first—see recipe 1.9 for details on strong naming assemblies.
To check the validity of a file signed with an Authenticode signature, use the Certificate Verification tool (chktrust.exe). For example, to test MyAssembly.exe, use the command chktrust MyAssembly.exe. If you have not already configured your machine to trust the SPC used to sign the assembly, you will see a dialog box similar to that shown in Figure 1.4, which shows you information about the publisher of the assembly, and gives you the opportunity to trust this publisher. (The certificate described in Figure 1.4 is a test certificate created using the process described in recipe 1-10.)


Figure 1.4: Certificate Verification tool.

If you click the Yes button, or you have previously chosen always to trust the SPC, the Certificate Verification tool confirms the validity of the signature and the assembly.

Archives

LocalsAdda.com-Variety In Web World

Fun Mail - Fun in the Mail