July 29, 2010

StringCollection Class Extender

A helper class for the StringCollection class.
public static class StringCollectionExtender
{
    // Convert a string collection to a multiline string where
    // each entry in the collection becomes a line in 
    // multi-line string
    public static string ToMultilineString(
        this StringCollection sc)
    {
        StringBuilder res = new StringBuilder();
        foreach (string str in sc)
        {
            res.AppendLine(str);
        }
        return res.ToString();
    }

    public static string[] ToStringArray(
        this StringCollection sc)
    {
        string[] res = new string[sc.Count];
        sc.CopyTo(res, 0);
        return res;
    }

    // Convert a multiline string to a string collection with each line
    // of the string becoming a new entry in the collection
    public static StringCollection MultilineStringToStringCollection(
        string str)
    {
        string[] lines = str.Split('\r');
        StringCollection res = new StringCollection();
        foreach (string line in lines)
        {
            res.Add(line.Trim('\n'));
        }
        return res;
    }
}

Snippets

Snippet Designer, very useful
SnippetManager

July 28, 2010

Assembly Version Incrementor

Find and increment the assembly version info in an Assembly info file
public class AssemblyVersionIncrementor
{
    public void IncrementAssemblyVersionString(FileSystemInfo fileInfo)
    {
        string[] lines = File.ReadAllLines(fileInfo.FullName);
        lines = IncrementAssemblyVersionString(lines);
        File.WriteAllLines(fileInfo.FullName, lines);
    }

    internal string[] IncrementAssemblyVersionString(string[] lines)
    {
        string[] res = new string[lines.Length];
        int ix = 0;
        foreach (string line in lines)
        {
            res[ix++] = IncrementAssemblyVersionString(line);
        }
        return res;
    }

    // Could be modified to find other types of Version Info by
    // making the "AssemblyVersion" a parameter
    internal string IncrementAssemblyVersionString(string line)
    {
        string res = line;
        if (line.Contains("AssemblyVersion"))
        {
            string newLine = string.Empty;
            string str = line;
            // Check for comments
            int commentStart = line.IndexOf("//");
            // TODO /* ... */ style comments
            if (commentStart >= 0)
            {
                str = line.Substring(0, commentStart);
            }
            if (str.Contains("AssemblyVersion"))
            {
                int start = str.IndexOf('"') + 1;
                int end = str.IndexOf('"', start);
                string assemblyVerStr = str.Substring(start, end - start);
                Version av = new Version(assemblyVerStr);
                Version newVer = av.IncrementRevision();
                newLine = line.Substring(0, start) + newVer.ToString() + 
                    line.Substring(end, line.Length - end);
            }

            res = newLine.Length > 0 ? newLine : line;
        }

        return res;
    }
}
uses the following Version extension class:
internal static class VersionExtender
{
    public static Version Increment(this Version version)
    {
        return Increment(version, 0x1L);
    }

    public static Version IncrementMajor(this Version version)
    {
        return Increment(version, 0x1000000000000L);
    }

    public static Version IncrementMinor(this Version version)
    {
        return Increment(version, 0x100000000L);
    }

    public static Version IncrementBuild(this Version version)
    {
        return Increment(version, 0x10000L);
    }

    public static Version IncrementRevision(this Version version)
    {
        return Increment(version, 0x1L);
    }


    // Good demo of using >> and << operators as well
    private static Version Increment(this Version version, ulong inc)
    {
        ulong versnNum = (((ulong)version.Major & 0xFFFF) << 48) +
            (((ulong)version.Minor & 0xFFFF) << 32) +
            (((ulong)version.Build & 0xFFFF) << 16) +
            ((ulong)version.Revision & 0xFFFF);

        versnNum += inc;

        UInt16 major = (UInt16)(versnNum >> 48);
        UInt16 minor = (UInt16)((versnNum >> 32) & 0xFFFF);
        UInt16 build = (UInt16)((versnNum >> 16) & 0xFFFF);
        UInt16 revision = (UInt16)(versnNum & 0xFFFF);

        return new Version(major, minor, build, revision);
    }
}

July 21, 2010

Quickly Add Xml To XmlDocument

This line loads an Xml document, looks for the presence of a particular line and if it is not there, adds it to the xml document. This is useful for updating a config file with a particular line of xml. Uses XPath to find the interesting element of the xml file.
public static void UpdateAnXmlNode(string xmlFileName)
{
  XmlDocument xDoc = new XmlDocument();
  try
  {
    // load the configuration file
    xDoc.Load(xmlFileName);

    // find the node of interest from the key
    XmlNode theNode =
    xDoc.SelectSingleNode(
@"/configuration/xxxConfiguration/moduleGroups/add[@trustName = 'Default']/modules");

    bool customXmlLinePresent = false;
    if (theNode != null)
    {
      customXmlLinePresent = theNode.InnerXml.Contains(
             "XXX.YYY.Sandbox.DebugTree.Module");
    }
    string extraXml = string.Empty;
    if (!customXmlLinePresent)
        extraXml += @"<add moduleType=""XXX.YYY.Sandbox.DebugTree.Module, XXX.YYY.Sandbox"" />" + Environment.NewLine;

    if (extraXml.Length > 0)
    {
      theNode.InnerXml += extraXml;
      xDoc.PreserveWhitespace = true;
      xDoc.Save(xmlFileName);
    }
  }
  catch (XmlException ex)
  {
    DoSomethingWithError(ex);
  }
}

July 16, 2010

FileSystemInfoExtender Class

Extend the FileSystemInfo class with enumerators to iterate through subdirectories, ditto with the DirectoryInfo class.
Now merged into this blog entry

FileAttributesExtender

Use Flags based enumerated types extender class to extend file attributes and give it Set/group theory like methods
public static class FileAttributesExtender
{
    // Return lhs flags plus rhs flags
    public static FileAttributes Union(this FileAttributes lhs, FileAttributes rhs)
    {
        return lhs | rhs;
    }

    // Return flags common to lhs and rhs
    public static FileAttributes Intersection(this FileAttributes lhs, FileAttributes rhs)
    {
        return lhs & rhs;
    }

    // Return lhs flags minus rhs flags
    public static FileAttributes Difference(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        int res = (int)lhs - (int)common;
        return (FileAttributes)(res);
    }

    // Return true if lhs contains ALL the flags within rhs
    public static bool Contains(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        return (common == rhs);
    }

    // Return true if lhs contains any of the flags within rhs
    public static bool ContainsAnyOf(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        return ((int)common > 0);
    }

    // Return true if lhs contains none of the flags within rhs
    public static bool ContainsNoneOf(this FileAttributes lhs, FileAttributes rhs)
    {
        FileAttributes common = Intersection(lhs, rhs);
        return ((int)common == 0);
    }

    // NON-extension methods here
    public static FileAttributes FromString(string source)
    {
        FileAttributes res = (FileAttributes)Enum.Parse(typeof(FileAttributes), source, true);
        return res;
    }

}

July 9, 2010

Close or Kill a Process

This routine will try and close a process by sending a close message to it's main window. If the process has no GUI or a timeout period is exceeded then the 'Process.Kill()' method will be used to close it.
// Try and close a process. Kill it if the Close takes 
// too long.
// exeLessExtension - Process name (without extension)
// msToWaitForCloseMainWindow - Time in milliseconds to
// wait for 'CloseMainWindow()' before just killing the 
// process, Set negative to not kill at all, set to 0 to 
// kill without calling CloseMainWindow
public static void ForceProcessClose(
  string exeLessExtension, 
  int msToWaitForCloseMainWindow)
{
  Process[] processes = Process.GetProcessesByName(exeLessExtension);

  if (msToWaitForCloseMainWindow != 0)
  {
    foreach (Process p in processes)
    {
      if (!p.CloseMainWindow())
      {   // Process does not have a GUI, use 'Kill()' instead
         msToWaitForCloseMainWindow = 0;
      }
    }
  }
  if (msToWaitForCloseMainWindow >= 0)
  {
    Thread.Sleep(msToWaitForCloseMainWindow);
    processes = Process.GetProcessesByName(exeLessExtension);
    foreach (Process p in processes)
    {
       p.Kill();
    }
  }
}

July 1, 2010

Using Images in WPF

Using project resources in WPF
  1. Create sub-directory for resources called say "Resources"
  2. Add your resources (images, icons etc.) to it.
  3. Copy the folder structure into the VS project and add the resource files to it.
  4. Check the Properties of your resources in VS project file to ensure they have a Build Action of 'Resource'
Creating an image in WPF: In XAML:
<Image Source="Resources/P4.ico" />
Adding an image to a window's resources is very easy:
<Window.Resources>
        <Image x:Key="P4Icon" Source="Resources/P4.ico" />
</Window.Resources>
In code:
Image myImage = new Image();
myImage.Source = new BitmapImage(new Uri("Icons/MyImage.png", UriKind.Relative));
Inserting an image in a button is very easy in WPF:
<Button HorizontalAlignment="Left" Margin="5,8,0,10" Name="p4But" Width="34" Click="p4But_Click">
   <Image Source="Resources/P4.ico" Width="24" />
</Button>

Start a Process and Capture the Output in c#

Simple sample for running a process synchronously and capturing the output
public string GetEnvironmentVars()
{
    Process process = new Process();
    // UseShellExecute must be 'false' when redirecting the output
    process.StartInfo.UseShellExecute = false;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.FileName = "p4";
    process.StartInfo.Arguments = " set ";
    //process.StartInfo.WorkingDirectory = dir;
    process.Start();
    string output = process.StandardOutput.ReadToEnd();
    //string error = process.StandardError.ReadToEnd();
    // Wait a reasonable ammount of time for it to finish
    bool res = process.WaitForExit(5000); 
    return res ? output : "Command \"" + 
        process.StartInfo.FileName + process.StartInfo.Arguments +
        "\" failed to exit:" + Environment.NewLine + output;
}

Editing Context Menus of Visual Studio 2008

Can add external tools to context menu.
  1. Create your external tool using the "Tools=>External Tools" menu option. Your new external tool will be called "External Command n" where 'n' is the external menu item tool location in the external menu items group of the Tools menu
  2. Open "Tools=>Customize" dialog.
  3. Select "Context Menus" in the "Toolbars" tab.
  4. The context menus will appear on the toolbar. Select a context menu from one on the toolbar. Some useful ones are:
    • "Project and Solution Context Menus=>Item" - This is the file context menu in the solutions folder
    • "Project and Solution Context Menus=>Project" - This is the project context menu in the solutions folder
    • "Other Context Menus=>Easy MDI Document Window" - Not obvious. This is the context menu for the tab at the top of a file editor window
  5. Goto "Command" tab and select "Tools" in the categories tab. From the "Commands:" list select the appropriate "External Command n"
Perforce menu items are defined in "External Tools" as follows:
Title: P4 Checkout
Command: C:\Program Files (x86)\Perforce\p4.exe
Arguments: edit $(ItemPath)
Initial Directory: $(ItemDir)
Select "Use Output window"
As shown here:

Title: P4 Revert
Command: C:\Program Files (x86)\Perforce\p4.exe
Arguments: revert $(ItemPath)
Initial Directory: $(ItemDir)
Select "Use Output window"