Sunday, September 15, 2013

Object And XML Serialization

In .Net, we can serialize and deserialize an object to/from an XML string using XmlSerializer.

The following methods provide easy ways to do this, with overloads that accept an existing instance of XmlSerializer if there is any.
  • Note: To avoid any performance hit, it's always better to use back an existing instance of XmlSerializer for the same object type if possible.

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

...

/// <summary>
/// Serializes an object to XML string.
/// </summary>
/// <param name="obj">Object to be serialized.</param>
/// <returns>Serialized XML string.</returns>
public static string SerializeObjectToXML(object obj)
{
    return SerializeObjectToXML(obj, null);
}

/// <summary>
/// Serializes an object to XML string.
/// </summary>
/// <param name="obj">Object to be serialized.</param>
/// <param name="serializer">An XmlSerializer instance to be used. If null, a new instance will be used.</param>
/// <returns>Serialized XML string.</returns>
public static string SerializeObjectToXML(object obj, XmlSerializer serializer)
{
    string xmlString;

    using (StringWriter stringWriter = new StringWriter())
    {
        if (serializer == null)
            serializer = new XmlSerializer(obj.GetType());

        serializer.Serialize(stringWriter, obj);
        stringWriter.Flush();
        xmlString = stringWriter.ToString();
    }

    return xmlString;
}

/// <summary>
/// Deserializes XML string to an object.
/// </summary>
/// <param name="xmlString">Serialized XML string.</param>
/// <param name="objType">Type of object to deserialize.</param>
/// <returns>Object deserialized from XML.</returns>
public static object DeserializeXMLToObject(string xmlString, Type objType)
{
    return DeserializeXMLToObject(xmlString, objType, null);
}

/// <summary>
/// Deserializes XML string to an object.
/// </summary>
/// <param name="xmlString">Serialized XML string.</param>
/// <param name="objType">Type of object to deserialize.</param>
/// <param name="serializer">An XmlSerializer instance to be used. If null, a new instance will be used.</param>
/// <returns>Object deserialized from XML.</returns>
public static object DeserializeXMLToObject(string xmlString, Type objType, XmlSerializer serializer)
{
    object obj = null;

    using (StringReader stringReader = new StringReader(xmlString))
    {
        if (serializer == null)
            serializer = new XmlSerializer(objType);

        obj = serializer.Deserialize(stringReader);
        stringReader.Close();
    }

    return obj;
}


If you find this post helpful, would you buy me a coffee?


Thursday, September 12, 2013

Regasm COM Interop Assembly With .Net Installer Project

COM Interop allows COM developers to access managed objects as easily as they access other COM objects. COM Interop provides a specialized utility (RegAsm.exe) that exports the managed types into a type library and registers the managed component as a traditional COM component.

To manually register a COM Interop assembly using RegAsm, we typically would do the following (assuming MyCom.COM.dll is an assembly that implements COM Interop for the managed MyCom.dll assembly):
  • Copy MyCom.COM.dll and MyCom.dll (and any other local assemblies) to a folder.
  • Run in VS command prompt: regasm [yourpath]\MyCom.COM.dll /tlb /codebase

However, it's also possible to include the Regasm process into a .Net Installer. Obviously this is the most ideal solution for easy distribution.

To implement this, I've largely benefited from Leon Mayne's blog Regasm /codebase for an installer project.

Below is a direct extract from the blog article about things to take note:

  • Making sure you do not use project references when adding your files to the installer. Use direct file references instead
  • Do the same for referenced files that are included automatically, as they do not get dropped in the obj folder and cause problems. Just exclude them from the project and then add them manually to the installer output
  • Make sure you have a type library for your dll and that this is included in the installer and set to register for COM
  • Make sure your dll is set to register for COM (duh)

Note: The first and second points may differ for some situations, as from my experience, I could use project references without any problem.

And another direct extract from the blog article that clearly stated the steps to perform:

  1. In your main project (the one containing the class you want to register), right click the project file and select Add / New Item and select Installer Class. Call it something like clsRegisterDll.cs
  2. In the designer that appears, click 'Click here to switch to code view' or right click the clsRegisterDll.cs file in solution explorer and select View Code
  3. Replace the code in the window with the code listed below, and make sure you change 'YourNamespace'. This has to match with the namespace in the clsRegisterDll.Designer.cs file.
  4. Compile your project
  5. In your installer, make sure you have added your dll to the Application Folder, and then right-click the installer project and select View / Custom Actions
  6. Right-click Install, and then click Add Custom Action
  7. Double click on Application Folder, and then on your dll
  8. Do the same for the Commit action
  9. Build and test your installer

Ok, now for the clsRegisterDll.cs class. In my implementation, I've renamed this class to RegasmTlbInstaller.cs, made some code changes, and also added overrides for Rollback and Uninstall.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Collections;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;

namespace YourNamespace
{
    /// <summary>
    /// An installer class to run regasm /tlb /codebase on the COM DLL (register and un-register).
    /// </summary>
    [RunInstaller(true)]
    public partial class RegasmTlbInstaller : Installer
    {
        public RegasmTlbInstaller()
        {
            InitializeComponent();
        }

        [SecurityPermission(SecurityAction.Demand)]
        public override void Install(IDictionary savedState)
        {
            base.Install(savedState);
        }

        /// <summary>
        /// Runs regasm /tlb /codebase to create and register the TLB file.
        /// </summary>
        [SecurityPermission(SecurityAction.Demand)]
        public override void Commit(IDictionary savedState)
        {
            base.Commit(savedState);

            // Get the location of regasm.
            string regasmPath       = RuntimeEnvironment.GetRuntimeDirectory() + @"regasm.exe";

            // Get the location of our DLL.
            string componentPath    = typeof(RegasmTlbInstaller).Assembly.Location;

            // Execute regasm.
            Process p               = new Process();
            p.StartInfo.FileName    = regasmPath;
            p.StartInfo.Arguments   = "\"" + componentPath + "\" /tlb /codebase";
            p.StartInfo.Verb        = "runas";          // To run as administrator.
            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            p.Start();
            p.WaitForExit();
        }

        [SecurityPermission(SecurityAction.Demand)]
        public override void Rollback(IDictionary savedState)
        {
            base.Rollback(savedState);
        }

        /// <summary>
        /// Runs regasm /u /tlb /codebase to un-register and delete the TLB file.
        /// </summary>
        [SecurityPermission(SecurityAction.Demand)]
        public override void Uninstall(IDictionary savedState)
        {
            // Get the location of regasm.
            string regasmPath       = RuntimeEnvironment.GetRuntimeDirectory() + @"regasm.exe";

            // Get the location of our DLL.
            string componentPath    = typeof(RegasmTlbInstaller).Assembly.Location;

            // Execute regasm.
            Process p               = new Process();

            p.StartInfo.FileName    = regasmPath;
            p.StartInfo.Arguments   = "/u \"" + componentPath + "\" /tlb /codebase";
            p.StartInfo.Verb        = "runas";          // To run as administrator.
            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            p.Start();
            p.WaitForExit();

            // Delete the TLB file.
            File.Delete(Path.Combine(Path.GetDirectoryName(componentPath), Path.GetFileNameWithoutExtension(componentPath) + ".tlb"));

            base.Uninstall(savedState);
        }
    }
}

Since this class also implements Rollback and Uninstall overloads, we need to add Custom Action for Rollback and Uninstall in the installer project as well (as in steps 6 and 7 mentioned above).

Additionally, the installer project properties should typically set as follows:
  • Project properties:
    • DetectNewerInstallation: True
    • InstallAllUsers: True
    • RemovePreviousVersions: True
  • Primary Output properties:
    • Register: vsdrpCOM
    • SharedLegacy: True
    • (User's Desktop and User's Programs Menu leave empty)

Finally, after building the installer project, please make sure the TLB file generated (e.g. MyCom.COM.tlb) is excluded from the "Detected Dependencies" folder.


Additional Resources:
C# COM Interop And Events


If you find this post helpful, would you buy me a coffee?


Thursday, September 5, 2013

C# COM Interop And Events

By using COM Interop, it is possible to expose the events from your managed component to COM environment. I've used the sample in Exposing Com Events for making my own implementation as below.

This example shows how to create a project named MyCom.COM to allow COM Interop for MyCom.dll with Events:
  • MyCom is the original .Net managed assembly which consist of:
    • a public property SerialNo;
    • a method ReadData();
    • two events Progress and Error.
  • MyComHelper is a wrapper class for the original MyCom functions and events.
  • Interface classes IMyComHelper and IMyComHelperEvents are required by MyComHelper.

Steps to create:

1. In your project, add reference to System.EnterpriseServices.dll.

2. Add a class named as MyComHelper. Implementations of MyComHelper class and interfaces:
using System.Runtime.InteropServices;
using System.EnterpriseServices;

namespace MyCom.COM
{
    #region Interfaces

    #region IMyComHelper

    [ComVisible(true)]                                          // Exposed.
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]      // Our managed interface will be IDispatch.
    public interface IMyComHelper
    {
        string SerialNo { get; set; }
        bool ReadData();
    }

    #endregion

    #region IMyComHelperEvents

    [ComVisible(true)]                                          // Exposed.
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]      // Our managed interface will be IDispatch.
    public interface IMyComHelperEvents
    {
        void Progress(string data, decimal percent);
        void Error(string description, string source);
    }

    #endregion

    #endregion

    /// <summary>
    /// A COM wrapper class for the MyCom functions and events.
    /// </summary>
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IMyComHelperEvents))]                       // Our event source is IMyComHelperEvents interface.
    [ProgId("MyCom_COM.MyComHelper")]
    public class MyComHelper : ServicedComponent, IMyComHelper
    {
        #region Declare

        MyCom _myCom = new MyCom();                                         // The MyCom class from MyCom.dll.

        #region Declare Events

        [ComVisible(false)]
        public delegate void ProgressDel(string data, decimal percent);     // No need to expose this delegate.
        public event ProgressDel Progress;

        [ComVisible(false)]
        public delegate void ErrorDel(string description, string source);
        public event ErrorDel Error;

        #endregion

        #region Declare Properties

        public string SerialNo
        {
            get { return _myCom.SerialNo; }
            set { _myCom.SerialNo = value; }
        }

        #endregion

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the MyComHelper object.
        /// </summary>
        public MyComHelper()
        {
            _myCom.Progress += new MyCom.ProgressEventHandler(_myCom_Progress);
            _myCom.Error    += new MyCom.ErrorEventHandler(_myCom_Error);
        }

        #endregion

        #region Methods

        public bool ReadData()
        {
            return _myCom.ReadData();   // In this example, data is returned in the Progress event.
        }

        #endregion

        #region Events

        void _myCom_Progress(string data, decimal percent)
        {
            Progress(data, percent);
        }

        void _myCom_Error(object sender, MyCom.ErrorEventArgs e)
        {
            Error(e.Description, e.Source);
        }

        #endregion
    }
}
3. In the project Properties > Application > Assembly Information > Make assembly COM-Visible > Check. Build the project.

4. Register the assembly using RegAsm:
  • Copy MyCom.COM.dll and MyCom.dll (and any other local assemblies) to a folder.
  • Run in VS command prompt: regasm [yourpath]\MyCom.COM.dll /tlb /codebase

5. Now you can add reference to MyCom.COM.dll from your unmanaged code, and will be able to access it's events.


Additional Resources:
Regasm COM Interop Assembly With .Net Installer Project


If you find this post helpful, would you buy me a coffee?


Sunday, September 1, 2013

Creating Events in C#

In C#, creating and consuming events are easy and definitely not a rocket science. However, from time to time, I find myself keep having trouble remembering how to implement it.

So, this is more a "note-to-self" post on creating... events.


Method 1:

public class MyClass
{
    /// <summary>
    /// MyClass event for getting data progress.
    /// </summary>
    public event GetDataProgressEventHandler GetDataProgress;

    /// <summary>
    /// MyClass event handler for getting data progress.
    /// </summary>
    /// <param name="percentage">Progress percentage.</param>
    /// <param name="message">Progress message.</param>
    public delegate void GetDataProgressEventHandler(decimal percentage, string message);

    ...
    
    public void GetData()
    {
        if (GetDataProgress!= null)
            GetDataProgress(((decimal)count / (decimal)max) * 100, statusMessage);    // Trigger the get data progress event.
    }
}

Usage Example:

MyClass myClass          = new MyClass();
myClass.GetDataProgress += new MyClass.GetDataProgressEventHandler(myClass_GetDataProgress);

...

void myClass_GetDataProgress(decimal progress, string message)
{
    ...
}


Method 2 (using Event Data):

public class MyClass
{
    /// <summary>
    /// Occurs when MyClass getting data giving progress.
    /// </summary>
    public event EventHandler<GetDataProgressEventArgs> GetDataProgress;

    /// <summary>
    /// Raises the GetDataProgress event of MyClass.
    /// </summary>
    /// <param name="e">The event argument.</param>
    protected virtual void OnGetDataProgress(GetDataProgressEventArgs e)
    {
        EventHandler<GetDataProgressEventArgs> handler = GetDataProgress;

        if (handler != null)
        {
            handler(this, e);
        }
    }

    ...
    
    public void GetData()
    {
        ...

        GetDataProgressEventArgs getDataProgressEventArgs = new GetDataProgressEventArgs();
        getDataProgressEventArgs.Percentage = ((decimal)count / (decimal)max) * 100;
        getDataProgressEventArgs.Message    = statusMessage;

        OnGetDataProgress(getDataProgressEventArgs);    // Trigger the get data progress event.
    }
}

For the event data, you need to define a class for GetDataProgressEventArgs:

/// <summary>
/// A derived EventArgs class used by MyClass to pass the GetDataProgress event data.
/// </summary>
public class GetDataProgressEventArgs : EventArgs
{
    private decimal _percentage = 0;

    /// <summary>
    /// Progress percentage.
    /// </summary>
    public decimal Percentage 
    { 
        get { return _percentage;  }
        set { _percentage = value; } 
    }

    private string _message = null;

    /// <summary>
    /// Progress status message.
    /// </summary>
    public string Message
    {
        get { return _message;  }
        set { _message = value; }
    }
}

Usage Example:

MyClass myClass          = new MyClass();
myClass.GetDataProgress += new EventHandler<GetDataProgressEventArgs>(myClass_GetDataProgress);

...

void myClass_GetDataProgress(object sender, GetDataProgressEventArgs e)
{
    ...
}


Additional Resources:
Handling and Raising Events



If you find this post helpful, would you buy me a coffee?


Thursday, August 29, 2013

Regex Tool For Testing Regex Patterns

This is a simple utility to match a Regex pattern which returns a Match (Match Single) or a MatchCollection (Match All), optionally with the RegexOptions specified.

The matched group(s) are displayed in the "Result" box at the bottom.

Please note that the first matched group (Group 0) of each Match is always the entire matched value.


Source Code:
The project source code is available as a public Mercurial repository hosted on BitBucket. You are free to download/clone/fork this project using the URL below:
https://bitbucket.org/simpcode/weizh.regextool

Executable:
Alternatively, you can download the executable from this link.


Screenshot:




If you find this post helpful, would you buy me a coffee?


Wednesday, August 28, 2013

Get Key Value From String

There are times when we need to store a key-value pair string (in a variable, fields, etc.), and then later read back the values from there.

For example, we may store a key-value pair in this format:
[key=value]
For multiple key-value pairs, we could store them in this way:
[key1=value1][key2=value2][key3=value3]

So, to read back the key-value pair string values, we can use the following methods:
/// <summary>
/// Gets value from a key-value pair string. In format [key=value].
/// </summary>
/// <param name="source">The source string to check.</param>
/// <param name="key">The key to find.</param>
/// <returns>The key's value.</returns>
public static string GetKeyValueFromString(string source, string key)
{
    return GetKeyValueFromString(source, key, "[", "]", "=");
}

/// <summary>
/// Gets value from a key-value pair string, in format with the signs provided. E.g. [key=value].
/// </summary>
/// <param name="source">The source string to check.</param>
/// <param name="key">The key to find.</param>
/// <param name="encloseLeftSign">Left enclosure sign.</param>
/// <param name="encloseRightSign">Right enclosure sign.</param>
/// <param name="equalSign">Equal sign.</param>
/// <returns>The key's value.</returns>
public static string GetKeyValueFromString(string source, string key, string encloseLeftSign, string encloseRightSign, string equalSign)
{
    if (String.IsNullOrEmpty(encloseLeftSign) || String.IsNullOrEmpty(encloseRightSign) || String.IsNullOrEmpty(equalSign))
        throw new ArgumentException("encloseLeftSign, encloseRightSign, equalSign arguments cannot be null or empty.");     // Validate signs.

    if (String.IsNullOrEmpty(source) || String.IsNullOrEmpty(key))
        return "";

    key = encloseLeftSign + key + equalSign;

    if (source.IndexOf(key) < 0)
        return "";

    string value = "";

    value = source.Substring(source.IndexOf(key) + key.Length);
    value = value.Substring(0, value.IndexOf(encloseRightSign));

    return value;
}

Note:
The first overload reads the value by using the default enclosure and equal signs (as in [key=value]), whereas the second overload reads the value by using the specified enclosures and equal signs (e.g. {key:value}, <key#value>, etc.).


Example:

// Store 3 key-value pairs into a "data" variable.
string data = "[code=3305][name=Peter Ross][url=www.example.com]";

// Read back the key values from the "data" variable.
string code = GetKeyValueFromString(data, "code");    // Value is "3305".
string name = GetKeyValueFromString(data, "name");    // Value is "Peter Ross".
string url  = GetKeyValueFromString(data, "url");     // Value is "www.example.com".



If you find this post helpful, would you buy me a coffee?


Monday, August 26, 2013

Properly Deleting A Directory, Optionally Using Regex Patterns

This method performs a deletion of a directory, and it's subdirectories and files.

Additionally, it will try to set all files to non-readonly before they're being deleted.

Optionally, it also allows to specify Regex patterns to match the names of the subdirectories or files to be deleted or excluded from the deletion (Regex options used are IgnoreCase and Singleline).

/// <summary>
/// Deletes the specified directory, subdirectories and files. 
/// <para>All files will be set to non-readonly before deleting.</para>
/// <para>_</para>
/// <para>Optionally, indicate the Regex patterns to match the names of the subdirectories or files to be deleted or excluded from deletion.</para>
/// <para>Note: Regex options used are IgnoreCase and Singleline.</para>
/// </summary>
/// <param name="path">The name of the directory to remove.</param>
/// <param name="subdirRegexPattern">Regex pattern for the name of the subdirectories to be deleted. Set null or empty for no checking.</param>
/// <param name="subdirExcludeRegexPattern">Regex pattern for the name of the subdirectories to be excluded from deletion. Set null or empty for no checking.</param>
/// <param name="fileRegexPattern">Regex pattern for the name of the files to be deleted. Set null or empty for no checking.</param>
/// <param name="fileExcludeRegexPattern">Regex pattern for the name of the files to be excluded from deletion. Set null or empty for no checking.</param>
public static void DeleteDirectory(string path, 
    string subdirRegexPattern, string subdirExcludeRegexPattern,
    string fileRegexPattern, string fileExcludeRegexPattern)
{
    if (!Directory.Exists(path))
        throw new DirectoryNotFoundException();

    DirectoryInfo dirInfo = new DirectoryInfo(path);

    #region Delete the files

    foreach (FileInfo fileInfo in dirInfo.GetFiles())
    {
        bool doDelete = true;

        #region Check regex patterns if any

        // Note: The static Regex.IsMatch() will take advantage of regex cache (default to last 15).

        if (!String.IsNullOrEmpty(fileRegexPattern) &&
            !Regex.IsMatch(fileInfo.Name, fileRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;

        if (!String.IsNullOrEmpty(fileExcludeRegexPattern) &&
            Regex.IsMatch(fileInfo.Name, fileExcludeRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;                   // Note: Exclude will take precedence.

        #endregion

        if (doDelete)
        {
            fileInfo.IsReadOnly = false;        // Make sure is not ReadOnly before delete.
            fileInfo.Delete();
        }
    }

    #endregion

    #region Delete the subdirectories

    foreach (DirectoryInfo subDirInfo in dirInfo.GetDirectories())
    {
        bool doDelete = true;

        #region Check regex patterns if any

        // Note: The static Regex.IsMatch() will take advantage of regex cache (default to last 15).

        if (!String.IsNullOrEmpty(subdirRegexPattern) &&
            !Regex.IsMatch(subDirInfo.Name, subdirRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;

        if (!String.IsNullOrEmpty(subdirExcludeRegexPattern) &&
            Regex.IsMatch(subDirInfo.Name, subdirExcludeRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline))
            doDelete = false;                   // Note: Exclude will take precedence.

        #endregion

        if (doDelete)
        {
            DeleteDirectory(subDirInfo.FullName, 
                subdirRegexPattern, subdirExcludeRegexPattern,
                fileRegexPattern, fileExcludeRegexPattern);     // Recursive.
        }
    }

    #endregion

    #region Delete the directory itself

    if (dirInfo.GetFiles().Length <= 0 && dirInfo.GetDirectories().Length <= 0)
    {
        dirInfo.Delete();                       // Delete only if no content.

        #region Make sure the directory is deleted before continue

        #region Note
        /*
         * To solve the intermittent file/directory deletion lagging issue, use retry if directory still exist.
         * Reference: http://stackoverflow.com/questions/9370012/waiting-for-system-to-delete-file
        */
        #endregion

        int retry = 0;
        while (dirInfo.Exists && retry <= 4)
        {
            retry += 1;
            System.Threading.Thread.Sleep(500); // Delay for half-second.

            // Note: If done all retries and still exist, do nothing and let the caller to handle (any delete exception will still be thrown).
        }

        #endregion
    }

    #endregion
}

Usage Examples:

To completely delete a directory and all it's contents (without Regex):
DeleteDirectory(@"D:\MyData\MyFolder", null, null, null, null);

To completely delete a directory and all it's contents, excluding subdirectories named "KeepDir" and files named "KeepFile.txt" (based on the Regex patterns specified):
DeleteDirectory(@"D:\MyData\MyFolder", 
    null, @"\bKeepDir\b",
    null, @"\bKeepFile.txt\b");



If you find this post helpful, would you buy me a coffee?