Click here to Skip to main content
15,881,559 members
Articles / Programming Languages / C#

Generic Directory Watcher Service

Rate me:
Please Sign up or sign in to vote.
4.76/5 (29 votes)
8 Feb 2007GPL314 min read 195.4K   2.8K   148  
This service watches for filesystem events in directories and runs specified programs in response to those events.
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.Configuration.Internal;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using Microsoft.VisualBasic;

namespace DirectoryWatcher
{
    /// <summary>
    /// A snippet of runtime-compiled code that we are to run in response to a filesystem event.
    /// </summary>
    public class ProgramCode : ConfigurationElement
    {
        /// <summary>
        /// The text of the actual code that we are to compile.
        /// </summary>
        private string text = null;
        /// <summary>
        /// Line number in the configuration file for this element (used for possible exception 
        /// messages).
        /// </summary>
        private int lineNumber = 0;
        /// <summary>
        /// Path to the configuration file in which this element resides (used for possible
        /// exception messages).
        /// </summary>
        private string fileName = "";
        /// <summary>
        /// Assembly that results when we compile the code.
        /// </summary>
        private Assembly assembly = null;

        /// <summary>
        /// The language for this code snippet.
        /// </summary>
        [ConfigurationProperty("language", IsRequired = true)]
        public ProgramLanguage Language
        {
            get
            {
                return (ProgramLanguage)base["language"];
            }
        }

        /// <summary>
        /// Text representing the actual code.
        /// </summary>
        public string Text
        {
            get
            {
                return text;
            }
        }

        /// <summary>
        /// Collection of referenced assemblies for this snippet of code.
        /// </summary>
        [ConfigurationProperty("referencedAssemblies")]
        public ReferencedAssemblyCollection ReferencedAssemblies
        {
            get
            {
                return (ReferencedAssemblyCollection)base["referencedAssemblies"];
            }
        }

        /// <summary>
        /// Assembly representing the compiled results of the code.
        /// </summary>
        public Assembly Assembly
        {
            get
            {
                // If the code has not already been compiled, do so now
                if (assembly == null)
                {
                    CodeDomProvider codeProvider = null;

                    // Get the proper code provider based on the code's language
                    if (Language == ProgramLanguage.CSharp)
                        codeProvider = new CSharpCodeProvider();

                    else if (Language == ProgramLanguage.VisualBasic)
                        codeProvider = new VBCodeProvider();

                    CompilerParameters compilerParameters = new CompilerParameters();

                    // Set the compiler options so that we don't generate an assembly on disk and
                    // we create a library assembly that does not contain debug information
                    compilerParameters.GenerateExecutable = false;
                    compilerParameters.GenerateInMemory = true;
                    compilerParameters.IncludeDebugInformation = false;
                    compilerParameters.CompilerOptions = "/target:library /optimize";
                    compilerParameters.ReferencedAssemblies.Add("System.dll");
                    compilerParameters.ReferencedAssemblies.Add(AppDomain.CurrentDomain.BaseDirectory + "\\DirectoryWatcher.exe");

                    // Add any assembly references (besides System.dll and DirectoryWatcher.exe, 
                    // which everyone gets) specified for this code
                    foreach (ReferencedAssembly referencedAssembly in ReferencedAssemblies)
                        compilerParameters.ReferencedAssemblies.Add(referencedAssembly.Name);

                    // Generate the assembly
                    CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParameters, text);

                    // Check the return code and throw an exception if the compilation failed
                    if (results.NativeCompilerReturnValue != 0)
                        throw new CompilationException(results.Errors);

                    assembly = results.CompiledAssembly;
                }

                return assembly;
            }
        }

        /// <summary>
        /// Handler for the case where we encounter an unrecognized element while attempting to 
        /// deserialize the class from XML; deals with "custom" properties, specifically the Text 
        /// property, whose value is set in an element node instead of an attribute.
        /// </summary>
        /// <param name="elementName">
        /// Name of the unrecognized element.
        /// </param>
        /// <param name="reader">
        /// Reader object that is involved in the deserialization.
        /// </param>
        /// <returns>
        /// True if we actually recognize the element, false otherwise.
        /// </returns>
        protected override bool OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
        {
            if (elementName == "text")
            {
                text = reader.ReadString();
                reader.Read();

                return true;
            }

            return base.OnDeserializeUnrecognizedElement(elementName, reader);
        }

        /// <summary>
        /// Instantiates the object using data stored in XML; records the filename and line number
        /// (for use later in possible exceptions) and then calls the base method.
        /// </summary>
        /// <param name="reader">
        /// Reader object that is involved in the deserialization.
        /// </param>
        /// <param name="serializeCollectionKey">
        /// True to serialize only the collection key properties, false otherwise.
        /// </param>
        protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
        {
            lineNumber = ((IConfigErrorInfo)reader).LineNumber;
            fileName = ((IConfigErrorInfo)reader).Filename;

            base.DeserializeElement(reader, serializeCollectionKey);
        }

        /// <summary>
        /// Called after the deserialization process is complete; validates the object's data.
        /// </summary>
        protected override void PostDeserialize()
        {
            if (text == null)
                throw new ConfigurationErrorsException("\"text\" is a required element.", fileName, lineNumber);

            base.PostDeserialize();
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer (Senior) AOC Solutions
United States United States
I'm a software architect in the truest sense of the word: I love writing code, designing systems, and solving tough problems in elegant ways. I got into this industry not for the money, but because I honestly can't imagine myself doing anything else. Over my career, I've worked on just about every major Microsoft web technology, running the gamut from SQL Server 7.0 to 2008, from .NET 1.1 to 4.0, and many others. I've made a name for myself and have risen to my current position by being able to visualize and code complex systems, all while keeping the code performant and extensible.

Both inside and outside of work, I love researching new technologies and using them sensibly to solve problems, not simply trying to force the newest square peg into the round hole. From emerging Microsoft projects like AppFabric to third party or open source efforts like MongoDB, nServiceBus, or ZeroMQ, I'm constantly trying to find the next technology that can eliminate single points of failure or help scale my data tier.

Outside of work, I'm a rabid DC sports fan and I love the outdoors, especially when I get a chance to hike or kayak.

Comments and Discussions