RSS

Monthly Archives: January 2011

Speed Development With Custom Application Blocks For Enterprise Library

Code download available at: AppBlocks2006_07.exe (315 KB)
Browse the Code Online

Enterprise Library for Microsoft .NET Framework 2.0 is a library of application blocks, which are modular components designed to help developers deal with common development challenges. It provides an extensible framework for building robust, scalable applications. And while Enterprise Library contains several useful application blocks, you can also integrate your own reusable components. In this article, I will demonstrate how to build a sample application block that integrates with Enterprise Library.
The Microsoft patterns & practices team, which is the creator of Enterprise Library, coined the term “application block” to describe a library that is more than just a DLL file. Although an application block is indeed a DLL, it has further unique characteristics.
Application blocks are configuration-driven, meaning that configuration is used to define their behavior. This configuration can be defined in a normal .NET configuration file or it can be created and injected into the application block at run time. Some of the configuration will be typical configuration values, such as numbers defining conditions for certain behavior. But in regards to application blocks, configuration is also used to define extensions to the basic block.
Application blocks are also modular and extensible. The extensibility is usually achieved through a provider model; the block delegates some of its work to a provider, which implements a base class or interface that the block uses. Although a block typically comes with a few pre-packaged providers (for example, one that lets you log to the Windows® event log or one that lets you read and write data to SQL Server™), other providers can be developed and replaced by defining the provider type in the block’s configuration.
When would you want to create an application block instead of a normal library? If you have no need for configuration of the library and you don’t want to allow others to extend it, you do not need to create an application block. But if these capabilities sound useful, then you should consider creating an application block. And since Enterprise Library already offers a lot of configuration management infrastructure that you can make use of, you may benefit from creating an application block instead of a standalone, configurable library.
An application block adds complexity to the library, as well as a dependency on other blocks in Enterprise Library. In return, however, you gain a common configuration management system and you may be able to use other parts of Enterprise Library. For instance, data access is a very common need. If you integrate your application block with Enterprise Library, you can write an optional provider for your block that uses the Data Access Application Block. When done correctly, this creates an optional dependency on the Data Access Application Block; in cases where you do not need data access, you can avoid this dependency.

 

Creating an Application Block
In this article, I will show you how to build an application block that can be used for managing plug-ins for an application. Note that although I will cover many aspects of application block development, I will cut some corners in the part of the implementation that does not apply to Enterprise Library. As such, the solution I will create in this article is simplistic and not at all secure. For a thorough treatment on how to load plug-ins in a secure and robust fashion, see Shawn Farkas’s MSDN®Magazine article “Do You Trust It? Discover Techniques for Safely Hosting Untrusted Add-Ins with the .NET Framework 2.0“. Because the Plug-In Loader application block I’m creating here is extensible, it’s possible to develop a new provider that utilizes the methods described in Shawn Farkas’s article and add it to this application block.
Now that the disclaimer is out of the way, let’s get started. First I will define the end goal of the application block. Since its purpose is to load plug-ins, I think a reasonable goal is to enable developers to write code like this:

PlugInManager<MyPlugIn> mgr = 
    PlugInFactory.GetManager<MyPlugIn>();
IList<MyPlugIn> plugIns = mgr.GetPlugIns();
// Do something with the plug-ins...
The static PlugInFactory class creates a new PlugInManager<T>, which can be used to acquire the plug-ins. In this article, GetPlugIns is the only member of PlugInManager<T>. In a more complete implementation, other functionality—the ability to enable or disable each plug-in at run-time, for example—would likely be added. PlugInManager<T> delegates the real work to a provider specified in the application block’s configuration. This provider then loads and returns the plug-ins as defined in its implementation.
An application block consists of the run-time component and an optional design-time component. I’ll show you how to develop both of these. The run-time component is where the library’s logic is implemented. This is the file that you need to reference in your project when you want to utilize the application block. Developing the run-time component involves several steps in itself.
The design-time component is another library assembly that you can create and include. Just as the run-time component integrates with the Enterprise Library run-time framework, the design-time component integrates with Enterprise Library’s design-time tools. This design-time functionality exists primarily to make the developer’s job easier when using the application block. But it can also be used by system administrators or other users to provide a graphical tool for editing the configuration of an application.
An application block is a complex collection of code that involves a lot of interacting classes. As you read through this step-by-step guide, you may wonder why it is necessary to have so many moving parts. In part, this is because Enterprise Library provides a lot of flexibility regarding how you implement your application block. In many steps, Enterprise Library uses abstractions, such as interfaces and abstract classes, to accomplish certain tasks. For most such scenarios, an extendable default implementation is included, but you are rarely forced to use that default implementation. If you prefer, you can create your own implementation.
Each time Enterprise Library accomplishes a task—like reading configuration data from a persistent store or instantiating a concrete type when an abstract type is requested—you can extend or deviate from the default behavior. For each extensibility point in Enterprise Library there are typically a few interacting classes involved. Since Enterprise Library has a lot of extensibility points, there are many interacting classes—a fact you’ll soon discover for yourself.

 

Factories for Objects
Enterprise Library creates objects using factories. It has its own attribute-based approach in which you attribute classes with the type of their factory and then implement the factory by deriving from base classes that are provided by Enterprise Library. Figure 1 provides an overview of the typical object-creation process. As you can see, this is a complex process, but you must keep in mind that it accomplishes more than just creating an arbitrary instance of a type. You request an abstract type and, based on configuration data, you get the correct implementation of that type, appropriately configured and instrumented.
Figure 1 Object-Creation Process
Step Procedure Notes
1 Inspect the type to be created for the presence of a CustomFactory attribute. This attribute identifies a type that must be used as a custom factory for the class. If this attribute is not present, an exception is thrown.
2 Instantiate the custom factory identified in Step 1. This factory must implement ICustomFactory.
3 Call the custom factory’s CreateObject method to create an instance of the requested type. The default implementation of ICustomFactory is the abstract AssemblerBasedCus-tomFactory<TObject, TConfiguration> class. If this class is used as a base for implementing ICustomFactory, Enterprise Library continues with the following steps.
4 Call the AssemblerBasedCustomFactory<TObject, TConfiguration>.GetConfiguration method. This configuration data is used to create the requested instance and identifies the implementation type to be created.
5 Inspect the implementation type for the presence of the ConfigurationElementType attribute. This attribute identifies the type that contains configuration data for the implementation type. If this attribute is not present, an exception is thrown.
6 Instantiate the configuration type. The configuration type is populated with configuration data from the configuration source.
7 Inspect the configuration data class for the requested type, looking for the presence of the Assembler attribute. This attribute identifies a type that must be used to create the requested type from its configuration data—a so-called assembler. If this attribute is not present, an exception is thrown.
8 Instantiate the assembler. The assembler identified in Step 7 must implement IAssembler<TObject, TConfiguration> or an exception is thrown.
9 The assembler creates the requested object. The configuration data defines the parameters for the object.
This object creation framework is highly extensible and you should use it as an object creation mechanism for your own application block, but you will soon find yourself creating a lot of factory classes. Figure 2 provides an overview of the interaction between these classes. When using the Plug-In Loader application block, the static PlugInFactory class is typically the entry point. This class is just a convenience class, which uses PlugInManagerFactory<T> to create instances of PlugInManager<T>. Most Enterprise Library application blocks provide such a static factory class as an entry point to the application block. Implementing a static factory class is optional when creating an Enterprise Library application block, but I’ve decided to include such a class for Plug-In Loader since it makes sense here.

Figure 2 Interaction of Object Factory Classes
Enterprise Library takes a factory-based approach to creating configurable objects, and although that framework is very flexible, it does not support generics since types are unknown at compile time. For that reason, the generic Plug-In Loader classes delegate most of their work to non-generic classes, which can be created and managed by Enterprise Library. PlugInManager<T> wraps the abstract PlugInProvider class, and PlugInManagerFactory<T> wraps PlugInProviderFactory. If your application block does not need to support generics, you don’t have to implement classes like PlugInManager<T> and PlugInManagerFactory<T>.
PlugInProviderFactory, which creates PlugInProvider instances, derives from a class provided by Enterprise Library. Using the CustomFactory attribute described in Step 1 of Figure 1, PlugInProvider identifies PlugInProviderCustomFactory, which Enterprise Library uses together with configuration classes to create instances of PlugInProvider.
This may seem complex, but Enterprise Library does most of the heavy lifting. You need to implement quite a few classes, but all of them are quite simple. You may be asking yourself why all this complexity is required. As I mentioned earlier, you are doing more than just newing up an instance of PlugInProvider. For starters, you cannot instantiate PlugInProvider, since it is an abstract class. When you request an instance of PlugInProvider, the application block must really serve up an implementation of that abstract class, based on configuration data. Enterprise Library uses this mechanism to identify what to create and how to create it.

 

Creating Factories
If you look at the intended target code I showed earlier, you can see that the first classes I need to implement are PlugInManager<T> and PlugInFactory. Both of these classes delegate most of their work to other classes. PlugInManager<T> uses PlugInProvider to perform the real work, as shown in Figure 3. Since the Enterprise Library object creation mechanism does not support generics, PlugInProvider is not a generic class, so to return IList<T>, the IList returned by PlugInProvider must be converted to the appropriate type-safe collection.
Figure 3 Getting a PlugInProvider
public class PlugInManager<T>
{
    private PlugInProvider provider_;

    internal PlugInManager(PlugInProvider provider)
    {
        this.provider_ = provider;
    }

    public IList<T> GetPlugIns()
    {
        IList plugIns = this.provider_.GetPlugIns();
        List<T> returnList = new List<T>(plugIns.Count);
        foreach (T item in plugIns) returnList.Add(item);
        return returnList;
    }
}
PlugInProvider itself is the simple class shown in Figure 4. Since it is abstract, it provides the extensibility point for Plug-In Loader. If you want to extend Plug-In Loader, you can derive a new class from PlugInProvider and implement your custom logic there. The most notable feature of PlugInProvider is the CustomFactory attribute, which instructs Enterprise Library how to create a new instance of a class that derives from PlugInProvider. Also note the abstract GetPlugIns method, which is the method that inheritors have to implement.
Figure 4 Implementation of PlugInProvider
[CustomFactory(typeof(PlugInProviderCustomFactory))]
public abstract class PlugInProvider
{
    private Type plugInType_;

    protected PlugInProvider(Type plugInType)
    {
        this.plugInType_ = plugInType;
    }

    public abstract IList GetPlugIns();

    protected Type PlugInType
    {
        get { return this.plugInType_; }
    }
}
PlugInProviderCustomFactory derives from AssemblerBased-CustomFactory<TObject, TConfiguration>, which is an abstract class provided by Enterprise Library. The only thing you have to do when deriving from this class is implement the GetConfiguration method. A couple of new classes, PlugInLoaderSettings and PlugInProviderData, make their first appearance here, as you can see in the following code:

public class PlugInProviderCustomFactory :
    AssemblerBasedCustomFactory<PlugInProvider, PlugInProviderData>
{
    protected override PlugInProviderData GetConfiguration(
        string name, IConfigurationSource configurationSource)
    {
        PlugInLoaderSettings settings =
            (PlugInLoaderSettings)configurationSource.
                GetSection(PlugInLoaderSettings.SectionName);
        return settings.PlugInProviders.Get(name);
    }
}

These classes are configuration classes and I will discuss them in more detail in the next section.

For now, the most important thing to notice is that the GetConfiguration method returns the appropriate configuration data, enabling Enterprise Library to construct a new PlugInProvider object, as described in Figure 1. With this custom factory in place, I can create a factory class that I can then use to create PlugInProvider instances, as shown here:

public class PlugInProviderFactory : 
    NameTypeFactoryBase<PlugInProvider>
{
    public PlugInProviderFactory(IConfigurationSource configSource) :
        base(configSource) { }
}

Although this is yet another factory class, all I need to do is derive from the NameTypeFactoryBase<T> class and provide a public constructor. PlugInManagerFactory<T> simply wraps PlugInProviderFactory. PlugInFactory creates and maintains a dictionary of these factories, and delegates the work to the appropriate factory, as shown in the code in Figure 5.

Figure 5 Creating Factories and Sending Them Work
public class PlugInManagerFactory<T>
{
    private PlugInProviderFactory factory_;

    public PlugInManagerFactory(IConfigurationSource 
            configurationSource)
    {
        this.factory_ = new PlugInProviderFactory(configurationSource);
    }

    public PlugInManager<T> Create()
    {
        string name = typeof(T).AssemblyQualifiedName;
        return new PlugInManager<T>(this.factory_.Create(name));
    }
}

public static class PlugInFactory
{
    private readonly static Dictionary<Type, object> factories_ =
        new Dictionary<Type, object>();

    public static PlugInManager<T> GetManager<T>()
    {
        if (!PlugInFactory.factories_.ContainsKey(typeof(T)))
        {
            IConfigurationSource configSource =
                ConfigurationSourceFactory.Create();
            PlugInManagerFactory<T> factory =
                new PlugInManagerFactory<T>(configSource);
            PlugInFactory.factories_.Add(typeof(T), factory);
        }

        object piFactory = PlugInFactory.factories_[typeof(T)];
        return ((PlugInManagerFactory<T>)piFactory).Create();
    }
}
One item of note here is a naming convention that is specific to Plug-In Loader. Enterprise Library polymorphic collections are keyed by their items’ names, so each name must be unique within the collection. For Plug-In Loader, the type of plug-in would have been the most intuitive key. However, that would have required me to create my own collection class keyed on type, and thus I would not have been able to reuse the classes provided by Enterprise Library.
As the design-time component requires a unique name in any case, the convention for Plug-In Loader is for each PlugInProvider to be named by the assembly-qualified name of the plug-in type, which is what you see in effect in Figure 5. It is a bit of a hack, but the user will never notice, because I will also deal with this convention in the design-time component. On the other hand, if you feel like editing the raw XML, everything is a string in that world in any case.
That’s it for the first step of creating an application block. If you refer back to Figure 1, you may wonder why I have not defined any assembler classes. This is because assemblers are tied to implementations of PlugInProvider, not the abstract PlugInProvider class itself. Now I will describe how to define configuration classes, and then I will describe how to implement a PlugInProvider, which will include a discussion of creating the appropriate assembler.

 

Defining Plug-In Configuration
The configuration framework of Enterprise Library builds on top of System.Configuration and works very similarly. To define the configuration section for Plug-In Loader, I create the PlugInLoaderSettings class as shown in Figure 6. But instead of deriving directly from System.Configuration.ConfigurationSection, an application block’s configuration section should derive from Microsoft.Practices.EnterpriseLibrary.Common.Configuration.SerializableConfigurationSection. This adds Enterprise Library functionality to the class; among other things, this lets you store the configuration section somewhere other than the application configuration file.
Figure 6 PlugInLoaderSettings
public class PlugInLoaderSettings : SerializableConfigurationSection
{
    public const string SectionName = "plugInConfiguration";

    private const string providersProperty_ = "plugInProviders";

    public PlugInLoaderSettings() : base() {}

    [ConfigurationProperty(PlugInLoaderSettings.providersProperty_)]
    public NameTypeConfigurationElementCollection<PlugInProviderData>
        PlugInProviders
    {
        get
        {
            return (NameTypeConfigurationElementCollection
                    <PlugInProviderData>)
                    this[PlugInLoaderSettings.providersProperty_]; 
        }
    }
}
The PlugInLoaderSettings class contains only a collection of PlugInProviderData classes. The PlugInProviderData class contains the data used to configure an instance of PlugInProvider, as shown in the following:

public class PlugInProviderData : NameTypeConfigurationElement
{
    public PlugInProviderData() : base() { }
    public PlugInProviderData(string name, Type type) :
        base(name, type) { }
}
This class represents a configuration element and derives indirectly from System.Configuration.ConfigurationElement. Had I wanted to create a simple configuration element, I could have derived PlugInProviderData directly from ConfigurationElement, but Enterprise Library gives me a couple of other options. One is the NamedConfigurationElement class, the other is NameTypeConfigurationElement. The former adds a name to the configuration element, which is useful when implementing the design-time features of the application block. This name also acts as the unique key in the generic configuration collection classes provided by Enterprise Library.
NameTypeConfigurationElement adds an additional Type property to the configuration element, and this is used to support polymorphic collections, which is exactly what I want in this case—to specify different plug-in providers, each with unique configuration settings, for different plug-in types. Where the name of the configuration element acts as a key to that element, the Type property identifies the type that the element configures. In the case of Plug-In Loader, the Type property identifies a type that implements PlugInProvider. Recall that, by convention, Plug-In Loader uses the Name property to store the assembly-qualified name of the type of plug-in. It is easy to mix up these two types, but the name identifies which plug-in type should be served by the provider, whereas the Type property identifies the type of the provider. Since the name is the key, you can only have a type of plug-in defined once, but many different plug-in types can be served up by the same provider type; in fact, most often they will probably all be served up by the same provider.
Due to the way Enterprise Library constructs these configuration element classes, it is not possible to make the PlugInProviderData class abstract, but you should think about it as being abstract. Notice that it does not really do anything, so in this particular implementation, I could have omitted it and created my configuration elements for the different plug-in providers by deriving them directly from NameTypeConfigurationElement. However, the abstract PlugInProvider class does contain some implementation, and it is easier to understand the structure of the application block’s code if there is a one-to-one relationship between providers and their configuration elements.

 

Implementing PlugInProvider
At this point, the abstract framework of the run-time component is complete, but there is still no functionality. It is now to time to create an implementation of PlugInProvider. This is a naïve implementation that is not secure and does not support plug-ins that can be unloaded from memory. For that reason, I will call it NaivePlugInProvider.
Shown in Figure 7, the NaivePlugInProvider class derives from PlugInProvider. Its main functionality is implemented in the GetPlugIns method, which simply loads and reflects over all types in all assemblies located in the configured folder. If a type implements the desired plug-in type, a new instance of that type is created and added to the list of plug-ins to return. Note that this implementation requires all plug-ins to have a default constructor; a more robust implementation might use a more sophisticated approach.
Figure 7 Implementing the Actual Plug-In Provider
[ConfigurationElementType(typeof(NaivePlugInProviderData))]
public class NaivePlugInProvider : PlugInProvider
{
    private string plugInFolder_;

    public NaivePlugInProvider(Type plugInType, string plugInFolder)
        : base(plugInType)
    {
        this.plugInFolder_ = plugInFolder;
    }

    public override IList GetPlugIns()
    {
        ArrayList plugIns = new ArrayList();

        string[] assemblyFileCandidates =
            Directory.GetFiles(this.plugInFolder_, "*.dll");
        foreach (string assemblyFileCandidate in assemblyFileCandidates)
        {
            Assembly a = Assembly.LoadFrom(assemblyFileCandidate);
            foreach (Type t in a.GetTypes())
            {
                if((t.IsPublic) && (this.PlugInType.IsAssignableFrom(t)))
                {
                    plugIns.Add(Activator.CreateInstance(t));
                }
            }
        }

        return plugIns;
    }
}
NaivePlugInProvider has two other traits that are not quite so apparent, but that are interesting in the context of creating an Enterprise Library application block: the use of the ConfigurationElementType attribute, and the lack of a default constructor.
When configuring the Plug-In Loader application block, you should only need to be concerned about which PlugInProvider you want to use, not about which class provides configuration data for that provider. The ConfigurationElementType attribute contains that information, which means that the configuration data only contains information about which PlugInProvider to create, and the Enterprise Library infrastructure figures out which class contains configuration data for that provider. In this case, it is the NaivePlugInProviderData class shown in Figure 8. This class derives from PlugInProviderData and provides an extra configuration property that allows you to specify the folder that will contain the plug-in assemblies.
Figure 8 NaivePlugInProviderData and NaivePlugInProviderAssembler
[Assembler(typeof(NaivePlugInProviderAssembler))]
public class NaivePlugInProviderData : PlugInProviderData
{
    private const string folderProperty_ = "plugInFolder";

    public NaivePlugInProviderData() : base()
    {
        this.Type = typeof(NaivePlugInProvider);
    }

    public NaivePlugInProviderData(string name) :
        this(name, "PlugIns")
    {
    }

    public NaivePlugInProviderData(string name, string plugInFolder) :
        base(name, typeof(NaivePlugInProvider))
    {
        this.PlugInFolder = plugInFolder;
    }

    [ConfigurationProperty(NaivePlugInProviderData.folderProperty_,
        IsRequired = true)]
    public string PlugInFolder
    {
        get
        {
            return (string)this[NaivePlugInProviderData.folderProperty_];
        }
        set { this[NaivePlugInProviderData.folderProperty_] = value; }
    }
}

public class NaivePlugInProviderAssembler :
    IAssembler<PlugInProvider, PlugInProviderData>
{
    public PlugInProvider Assemble(IBuilderContext context,
        PlugInProviderData objectConfiguration,
        IConfigurationSource configurationSource,
        ConfigurationReflectionCache reflectionCache)
    {
        NaivePlugInProviderData naiveConfiguration =
            objectConfiguration as NaivePlugInProviderData;
        Type plugInType = Type.GetType(naiveConfiguration.Name);
        string folder = naiveConfiguration.PlugInFolder;
        return new NaivePlugInProvider(plugInType, folder);
    }
}
The other interesting thing about NaivePlugInProvider is its lack of a default constructor. How does Enterprise Library create a new instance of NaivePlugInProvider when it has no default constructor? NaivePlugInProviderData is outfitted with an Assembler attribute. This attribute identifies a type that can create a NaivePlugInProvider instance from a NaivePlugInProviderData instance.
The NaivePlugInProviderAssembler class is also shown in Figure 8. An assembler class must implement IAssembler<TObject, TConfiguration>, which contains the single Assemble method. It uses the supplied configuration data to pull out the relevant information and create a new NaivePlugInProvider instance.
At this point, Plug-In Loader contains a fully functional, albeit simplistic, implementation and is ready for use. You can now go and create more providers to create different plug-in discovery behaviors for the application block. An obvious extension is a provider that follows secure practices for discovering and loading plug-ins. Another possible extension is one that pulls plug-ins from blobs in a SQL Server table, possibly creating an optional dependency on the data access application block.
If you do not mind having to write the entire configuration in XML by hand, you can stop right there and ship your application block. Otherwise, you can create a design-time component that takes care of that work for you.

 

Design-Time Behavior
The design-time component plugs into the Enterprise Library configuration application. This is an extensible Windows Forms application that allows you to edit application configuration files using a rich UI, instead of having to write raw XML. The component has three responsibilities: it must provide behavior for the application UI itself, it must allow the application to serialize the user’s settings, and it must be able to deserialize configuration data when the user opens an existing application configuration file.
Since application configuration files are based on XML, they are hierarchical in nature. The configuration application models this as a tree consisting of nodes. Each configuration element in the run-time component must be represented by a design-time node class, which provides additional design-time behavior to the configuration class. Figure 9 illustrates this relationship for the Plug-In Loader application block.

Figure 9 Mapping Run-Time and Design-Time Classes
To integrate with the Enterprise Library configuration application, the design-time component must register with it. The assembly and its dependencies must be placed in the same directory as the configuration application itself, and it must be marked with the ConfigurationDesignManager attribute, like this:

[assembly: ConfigurationDesignManager(
    typeof(PlugInLoaderConfigurationDesignManager))]
This assembly-level attribute registers the PlugInLoaderConfigurationDesignManager with the configuration application. This class derives from the abstract ConfigurationDesignManager class.
Defining the design-time behavior involves specifying which actions are possible in which contexts. This is done by overriding ConfigurationDesignManager’s Register method:

public override void Register(IServiceProvider serviceProvider)
{
    PlugInLoaderCommandRegistrar cmdRegistrar =
        new PlugInLoaderCommandRegistrar(serviceProvider);
    cmdRegistrar.Register();

    // Node map code goes here...
}
PlugInLoaderCommandRegistrar derives from the abstract CommandRegistrar class; its purpose is to register design-time actions with the configuration application. The first action I need to implement is the command to add the application block to an application configuration file. When the Plug-In Loader application block is added to an application configuration, a PlugInLoaderSettingsNode, along with its child Plu gInProviderCollectionNode, must be added to the hierarchy.
First, these node classes must be defined, as such:

public class PlugInLoaderSettingsNode : ConfigurationNode
{
    public PlugInLoaderSettingsNode() :
        base("Plug-In Loader Application Block") {}

    [ReadOnly(true)]
    public override string Name
    {
        get { return base.Name; } set { base.Name = value; }
    }
}
PlugInProviderCollectionNode is almost identical, since PlugInLoaderSettingsNode does not contain other properties than the collection of PlugInProviders. While you may think I could have used a common class for both nodes, this is not the case. Both nodes occupy different places in the hierarchy, and I am going to attach different actions to each of them. If you wonder why I have overridden the Name property, it is merely so I can mark it with the Read-only attribute. This will make the nodes read-only in the configuration application.
When the command to add the Plug-In Loader application block to an application configuration file is invoked by the user, these two nodes must be added to the hierarchy. To accomplish this, I create the AddPlugInLoaderSettingsNodeCommand class, shown in Figure 10. It derives from AddChildNodeCommand and overrides the ExecuteCore method to implement the desired logic. The command class must be associated with a node class so that the base class knows that it should create an instance of PlugInLoaderSettingsNode and add it to the hierarchy. This is already done after the call to the base implementation of ExecuteCore, so all I need to do is create a new PlugInProviderCollectionNode and add it to the settings node.
Figure 10 Adding Nodes to the Hierarchy
public class AddPlugInLoaderSettingsNodeCommand : AddChildNodeCommand
{
    public AddPlugInLoaderSettingsNodeCommand(
        IServiceProvider serviceProvider) : base(serviceProvider, 
            typeof(PlugInLoaderSettingsNode)) {}

    protected override void ExecuteCore(ConfigurationNode node)
    {
        base.ExecuteCore(node);

        PlugInLoaderSettingsNode settingsNode =
            this.ChildNode as PlugInLoaderSettingsNode;
        if (settingsNode == null) return;

        PlugInProviderCollectionNode providersNode =
            new PlugInProviderCollectionNode();
        settingsNode.AddNode(providersNode);
    }
}
The AddPlugInLoaderSettingsNodeCommand class defines what happens when the user invokes the command. But I still need to define when and where this command is available. It should only be available when the user has selected the application configuration root node, and it should only be possible to invoke the command once. I will accomplish this from the PlugInLoaderCommandRegistrar class by overriding the abstract Register method:

public override void Register()
{
    this.AddPlugInLoaderCommand();
    // Add other commands here...
}

The AddPlugInLoaderCommand method only contains three statements, as shown in the following:

private void AddPlugInLoaderCommand()
{
    ConfigurationUICommand cmd =
        ConfigurationUICommand.CreateSingleUICommand(
        this.ServiceProvider, "Plug-In Loader Application Block",
        "Add the Plug-In Loader Application Block",
        new AddPlugInLoaderSettingsNodeCommand(this.ServiceProvider),
        typeof(PlugInLoaderSettingsNode));
    this.AddUICommand(cmd, typeof(ConfigurationApplicationNode));
    this.AddDefaultCommands(typeof(PlugInLoaderSettingsNode));
}
By calling CreateSingleUICommand, I specify that this command can only be invoked once. In this method call, I also provide display texts and an instance of AddPlugInLoaderSettingsNodeCommand, which is invoked if the user selects to perform this action. With the call to AddUICommand I associate this command with the ConfigurationApplicatonNode type, which is the type of the application configuration root node. The AddDefaultCommands method adds default commands, such as Add and Delete, to the newly created PlugInLoaderSettingsNode.

 

Provider Nodes
PlugInProviderData must be enhanced by PlugInProviderNode, and NaivePlugInProviderData by NaivePlugInProviderNode, as illustrated in Figure 9. The abstract PlugInProviderNode in Figure 11 provides design-time functionality to PlugInProviderData. Several attributes from the System.ComponentModel namespace are in play here: Category, Editor, and ReadOnly. These provide the same functionality as in the Visual Studio® property grid.
Figure 11 Design-Time Support for PlugInProviderData
public abstract class PlugInProviderNode : ConfigurationNode
{
    private PlugInProviderData configurationData_;
    private Type plugInType_;

    protected PlugInProviderNode(PlugInProviderData configurationData) :
        base(configurationData == null ? null : configurationData.Name)
    {
        if (configurationData == null)
        {
            throw new ArgumentNullException("configurationData");
        }
        this.configurationData_ = configurationData;
    }

    [ReadOnly(true)]
    public override string Name
    {
        get { return base.Name; } set { base.Name = value; }
    }

    [BaseType(typeof(object), TypeSelectorIncludes.AbstractTypes |
        TypeSelectorIncludes.BaseType |
        TypeSelectorIncludes.Interfaces)]
    [Category("Plug-In Loader")]
    [Editor(typeof(TypeSelectorEditor), typeof(UITypeEditor))]
    public Type PlugInType
    {
        get { return this.plugInType_; }
        set
        {
            this.plugInType_ = value;
            if (value != null) this.Name = value.AssemblyQualifiedName;
        }
    }

    public abstract string Provider { get; }

    protected override void OnRenamed(
        ConfigurationNodeChangedEventArgs e)
    {
        base.OnRenamed(e);
        this.PlugInProviderData.Name = e.Node.Name;
    }

    internal PlugInProviderData PlugInProviderData
    {
        get { return this.configurationData_; }
    }
}

Figure 9 Mapping Run-Time and Design-Time Classes
PlugInProviderNode wraps a PlugInProviderData instance, which provides and stores all the configuration data except the plug-in type. Plug-In Loader uses a special naming convention in which the configured name of the PlugInProvider is the assembly-qualified name of the plug-in type. Since there’s no guarantee that the Name property can be resolved to a type, PlugInProviderNode stores the plug-in type separately in a member variable.
The PlugInType property also contains the BaseType attribute, which is used by the TypeSelectorEditor identified in the Editor attribute to filter the available types. When this editor shows available types, only those that are abstract, base types, or interfaces are listed. When selecting a plug-in type, these are the only types worth listing since you cannot base a plug-in on a sealed type.
Another notable feature of PlugInProviderNode is the read-only Provider property. I find it is always nice to be able to inspect which provider is configured, and this provides the most direct way of informing the user. Otherwise, this can be really difficult to tell when using the configuration application.
The last thing to note about PlugInProviderNode is that I use OnRenamed to keep the name of the node and the name of the underlying data synchronized.
A NaivePlugInProviderNode class extends PlugInProviderNode by providing the PlugInFolder property. Adding a NaivePlugInProvider command to the configuration application is a bit simpler than adding the application block itself, since this command does not need to create additional sub-nodes under a NaivePlugInProviderNode. So I don’t need to create a separate command class for this action. Instead I can just let PlugInLoaderCommandRegistrar handle all of the command registration:

this.AddMultipleChildNodeCommand(
    "Naive Plug-In Provider", "Add a new naive Plug-In Provider",
    typeof(NaivePlugInProviderNode),
    typeof(PlugInProviderCollectionNode));
this.AddDefaultCommands(typeof(NaivePlugInProviderNode));
By calling AddMultipleChildNodeCommand, I specify that this command can be invoked an arbitrary number of times to create new NaivePlugInProviderNodes as child nodes of a PlugInProviderCollectionNode. Other PlugInProvider node types can be added using a similar mechanism.

 

Serializing and Deserializing XML
When you work with the configuration application, settings are retained only in memory until you choose to save the changes. Once you save changes, the data configured in each node must be serialized to XML. Fortunately, Enterprise Library and System.Configuration take care of serializing configuration elements to and from XML. The only thing you need to do is specify how to map from nodes to configuration classes.
This is done by overriding PlugInLoaderConfigurationDesignManager’s GetConfigurationSectionInfo method. This implementation acquires the PlugInLoaderSettingsNode from the hierarchy, but delegates the real work to the PlugInLoaderSettingsBuilder class shown in Figure 12. This internal class creates a new, empty PlugInLoaderSettings instance and then uses the node hierarchy to add configuration data to that instance.
Figure 12 Preparing Settings for Serialization
internal class PlugInLoaderSettingsBuilder
{
    private PlugInLoaderSettingsNode settingsNode_;
    private IConfigurationUIHierarchy hierarchy_;

    internal PlugInLoaderSettingsBuilder(
        IServiceProvider serviceProvider,
        PlugInLoaderSettingsNode settingsNode)
    {
        this.settingsNode_ = settingsNode;
        this.hierarchy_ = 
            ServiceHelper.GetCurrentHierarchy(serviceProvider);
    }

    internal PlugInLoaderSettings Build()
    {
        PlugInLoaderSettings settings = new PlugInLoaderSettings();
        this.BuildPlugInProviders(settings);
        return settings;
    }

    private void BuildPlugInProviders(PlugInLoaderSettings settings)
    {
        PlugInProviderCollectionNode providersNode =
            this.hierarchy_.FindNodeByType(this.settingsNode_,
                typeof(PlugInProviderCollectionNode))
                    as PlugInProviderCollectionNode;

        if (providersNode != null)
        {
            foreach (PlugInProviderNode pipNode in providersNode.Nodes)
            {
                settings.PlugInProviders.Add(pipNode.PlugInProviderData);
            }
        }
    }
}
Since Plug-In Loader’s configuration hierarchy is so shallow, this only involves looping through all PlugInProviders and adding the configuration data from their nodes. If the hierarchy had been deeper, this operation would have entailed traversing the node hierarchy and building up the equivalent hierarchy of configuration classes.
Just as Enterprise Library and System.Configuration take care of serializing to XML, the framework also performs the work of deserializing from XML to configuration class instances. However, you must provide the code that maps from configuration classes to a hierarchy of nodes. The initial step is to override PlugInLoaderConfigurationDesignManager’s OpenCore method:

protected override void OpenCore(IServiceProvider serviceProvider,
    ConfigurationApplicationNode rootNode, ConfigurationSection section)
{
    if (section != null)
    {
        PlugInLoaderSettingsNodeBuilder builder =
            new PlugInLoaderSettingsNodeBuilder(serviceProvider,
                (PlugInLoaderSettings)section);
        rootNode.AddNode(builder.Build());
     }
}
Like when serializing to XML, here I delegate the real work to another class: PlugInLoaderSettingsNodeBuilder. This class derives from NodeBuilder, which provides some utility methods for mapping between configuration classes and nodes. The idea is that the root node and all collection nodes have associated node builder classes, so a node builder class only contains code to create its own type of node, and delegates the work of creating the rest of the node hierarchy to other node builder classes. This is also the case for PlugInLoaderSettingsNodeBuilder, which creates a new PlugInLoaderSettingsNode and delegates the job of creating the collection of PlugInProviders to another class. The code download for this article shows how this is done.
This code uses a NodeCreationService to create a new PlugInProviderNode from the configuration data, and adds this node to the collection node. To enable the NodeCreationService to perform the mapping, a node map must be registered by PlugInLoaderConfigurationDesignManager in the Register method.
In order to create and register the node map, I create the PlugInLoaderNodeMapRegistrar class (which derives from NodeMapRegistrar) and override its abstract Register method. Here, I simply create the map between the configuration data class and the corresponding node class by calling the inherited AddMultipleNodeMap method:

this.AddMultipleNodeMap("Naive Plug-In Provider",
    typeof(NaivePlugInProviderNode), typeof(NaivePlugInProviderData));
Using the mechanism of node builder classes, I can build a hierarchy of nodes to an almost arbitrary depth and complexity while keeping the implementation of each node builder relatively simple. This approach may seem overly intricate for the very simple example shown here, but it scales very well to more complex configuration schemes where it becomes far more useful.

 

Conclusion
Creating an Enterprise Library application block is not trivial, but for the right kind of project, it may prove to be more than worth the effort. The core of creating an Enterprise Library application block, or turning a normal configuration-driven library into an application block, lies in developing the run-time component. But the optional extras are equally vital. I have described the design-time component, but this is only the first step among several.
Packaging the application block in a Microsoft® Installer (MSI) package is not difficult to do, and makes the block much easier to download and install. You should also consider creating comprehensive documentation—not only API documentation generated from XML comments, but also a quick introduction, an architectural overview, guidance on how to get started, and so on.
Most developers learn by example, so a few good QuickStart sample applications will make adoption of the application block easier. Even if your audience is only internal to your organization, samples may very well be worth the investment.

 

Mark Seemann works for Microsoft Consulting Services in Copenhagen, Denmark, where he helps Microsoft customers and partners architect, design and develop enterprise-level applications. He thanks Tom Hollander for providing valuable help for this article. Mark can be reached via his blog at blogs.msdn.com/ploeh.
 
Leave a comment

Posted by on January 28, 2011 in C#

 

Basic Client/Server Chat Application in C#

Welcome to the Basic Client/Server Chat Application in C#. In this tutorial I will provide the basics for a simple chat application in C# utilizing TCPClient, StreamReader, and the StreamWriter Classes in the .Net Framework.

In this application you have 3 components, the server (a class file), the communication component (a class file) and the client application. We will look at all 3 of these components individually, and how the can combine to create your basic chat application. The first component, the chat server, is where the messages are sent back and forth between the client and the server. Before writing any methods you need to add the following references to your class.

1 using System.IO;
2 using System.Net;
3 using System;
4 using System.Threading;
5 using Chat = System.Net;
6 using System.Collections;

I know some of you are going to look at the 5th reference and ask questions regarding Chat = System.Net. When adding references in C# you are allowed to add aliases to your references, thus allowing you to have multiple uses of the same Namespace at the same time, acting as 2 different objects.

NOTE: To use Aliases for the Namespace reference it has to be in conjunction with the Using Statement.

The first thing we do in our Server class is create 3 global variables, 2 are Hashtable variables, and the third is a TCPListener variable, which is used to listen for connections from TCP Clients.

1 System.Net.Sockets.TcpListener chatServer;
2 public static Hashtable nickName;
3 public static Hashtable nickNameByConnect;

These three variables will be used throughout our ChatServer.cs class file. Next, is the Public ChatServer() method, this is where we start the chat server and connect. We will then use our TCPListener object to check if there are any pending connection requests. If there are pending requests we then create a new connection, let the user know they’re connected, then create our DoCommunication Object.

We’ll get to the DoCommunication object later in this tutorial. Here is the code for this method

01 public ChatServer()
02 {
03 //create our nickname and nickname by connection variables
04 nickName = new Hashtable(100);
05 nickNameByConnect = new Hashtable(100);
06 //create our TCPListener object
07 chatServer = new System.Net.Sockets.TcpListener(4296);
08 //check to see if the server is running
09 //while (true) do the commands
10 while (true)
11 {
12 //start the chat server
13 chatServer.Start();
14 //check if there are any pending connection requests
15 if (chatServer.Pending())
16 {
17 //if there are pending requests create a new connection
18 Chat.Sockets.TcpClient chatConnection = chatServer.AcceptTcpClient();
19 //display a message letting the user know they're connected
20 Console.WriteLine("You are now connected");
21 //create a new DoCommunicate Object
22 DoCommunicate comm = new DoCommunicate(chatConnection);
23 }
24 }
25 }

Next, since this is a basic chat application, we need a method for sending our messages to all that are connected. Here we create a StreamWriter object, used to write our messages to the chat window, a TcpClient Array, to hold all the TcpClients for all connected users, then we copy the users nickname to the chat server window. After that we create a loop, looping through all the TcpClients, we check if the message eing sent is empty or that index of our TcpClient array is empty. From there we send our message to the chat window, and flush to make sure the buffer is empty.

In your Catch, of our Try…Catch block, is where we handle the Exception that is caused when a user leaves or disconnects. We display a message letting the users know that that person has disconnected, we remove that nickname from the list, then dispose of that users TcpClient instance. Here is the code for this method

01 public static void SendMsgToAll(string nick, string msg)
02 {
03 //create a StreamWriter Object
04 StreamWriter writer;
05 ArrayList ToRemove = new ArrayList(0);
06 //create a new TCPClient Array
07 Chat.Sockets.TcpClient[] tcpClient = new Chat.Sockets.TcpClient[ChatServer.nickName.Count];
08 //copy the users nickname to the CHatServer values
09 ChatServer.nickName.Values.CopyTo(tcpClient, 0);
10 //loop through and write any messages to the window
11 for (int cnt = 0; cnt < tcpClient.Length; cnt++)
12 {
13 try
14 {
15 //check if the message is empty, of the particular
16 //index of out array is null, if it is then continue
17 if (msg.Trim() == "" || tcpClient[cnt] == null)
18 continue;
19 //Use the GetStream method to get the current memory
20 //stream for this index of our TCPClient array
21 writer = new StreamWriter(tcpClient[cnt].GetStream());
22 //white our message to the window
23 writer.WriteLine(nick + ": " + msg);
24 //make sure all bytes are written
25 writer.Flush();
26 //dispose of the writer object until needed again
27 writer = null;
28 }
29 //here we catch an exception that happens
30 //when the user leaves the chatroow
31 catch (Exception e44)
32 {
33 e44 = e44;
34 string str = (string)ChatServer.nickNameByConnect[tcpClient[cnt]];
35 //send the message that the user has left
36 ChatServer.SendSysMsg("** " + str + " ** Has Left The Room.");
37 //remove the nickname from the list
38 ChatServer.nickName.Remove(str);
39 //remove that index of the array, thus freeing it up
40 //for another user
41 ChatServer.nickNameByConnect.Remove(tcpClient[cnt]);
42 }
43 }
44 }

The next method we introduce is a way to send a system message, this method is almost identical to the SendMsgToAll method, except here we dont dispose of the TcpClient instance, since the message is being sent by the system, not a user.

01 public static void SendSystemMessage(string msg)
02 {
03 //create our StreamWriter object
04 StreamWriter writer;
05 ArrayList ToRemove = new ArrayList(0);
06 //create our TcpClient array
07 Chat.Sockets.TcpClient[] tcpClient = new Chat.Sockets.TcpClient[ChatServer.nickName.Count];
08 //copy the nickname value to the chat servers list
09 ChatServer.nickName.Values.CopyTo(tcpClient, 0);
10 //loop through and write any messages to the window
11 for (int i = 0; i < tcpClient.Length; i++)
12 {
13 try
14 {
15 //check if the message is empty, of the particular
16 //index of out array is null, if it is then continue
17 if (msg.Trim() == "" || tcpClient[i] == null)
18 continue;
19 //Use the GetStream method to get the current memory
20 //stream for this index of our TCPClient array
21 writer = new StreamWriter(tcpClient[i].GetStream());
22 //send our message
23 writer.WriteLine(msg);
24 //make sure the buffer is empty
25 writer.Flush();
26 //dispose of our writer
27 writer = null;
28 }
29 catch (Exception e44)
30 {
31 e44 = e44;
32 ChatServer.nickName.Remove(ChatServer.nickNameByConnect[tcpClient[i]]);
33 ChatServer.nickNameByConnect.Remove(tcpClient[i]);
34 }
35 }
36 }

Believe it or not, thats the entirety of the ChatServer Class, simple isnt it. Working with Tcp objects can be fun, as you can do so much with them. In this simple application you could add the functionality to send files back and forth between users, and more. That may be the end of the ChatServer Class, but its not the end of creating our application.

The next component to look at is the DoCommunicate Class. This is the component that does the work for our server. For a chat application to work efficiently, and work as people expect a chat application to work, it needs to be a multi-threaded application. Meaning each user is running in their own thread, which allows for the messages to be sent and received in real time. Multi threading gives the illusion that multiple activities are happening at the same time.

The main purpose of multi threading is to improve performance. With each user in the chat application operating on their own thread, users don’t have to wait for one user to be finished to send their message, they’re able to send them simultaneously. C# has some powerful items in the System.Threading Namespace, which is used for, you guessed it, running multiple threads and synchronizing them.

For our DoCommunicate.cs class file we need the following references

1 using System.IO;
2 using System.Net;
3 using System;
4 using System.Threading;
5 using Chat = System.Net;
6 using System.Collections;
7 using PC;

Once again we add an alias to an instance of the System.Net Namespace reference, this prevents namespace collisions in our class. Like the ChatServer class, the first thing we do in our class is create some global variables, 4 of them:

In this method is where the new Thread is created and started, allowing this user to react in real time in the application.

1 public DoCommunicate(System.Net.Sockets.TcpClient tcpClient)
2 {
3 //create our TcpClient
4 client = tcpClient;
5 //create a new thread
6 Thread chatThread = new Thread(new ThreadStart(startChat));
7 //start the new thread
8 chatThread.Start();
9 }

Notice when we create our new Thread we pass it a method called startChat. We’ll get to this method momentarily, but first we need to do a couple things that startChat relies on. Once the thread is created and
started, we need to get the nickname the user wishes to use. For this we use the GetNick method we created. Here we simply ask the user what their nickname is, then return that value to the startChat method.

1 private string GetNick()
2 {
3 //ask the user what nickname they want to use
4 writer.WriteLine("What is your nickname? ");
5 //ensure the buffer is empty
6 writer.Flush();
7 //return the value the user provided
8 return reader.ReadLine();
9 }

Now lets look at the aforementioned startChat method. Here we create our StreamReader and StreamWriter objects and set the global string variable nickName to the value returned from the GetNick method. Next thing we do is check to ensure that the nickname provided by the user doesn’t already exist, if it does we prompt them for a nickname until we find one thats not already in use.

Once they provide a valid nickname we add their nickname to the server, preventing another user from using it, then we send a system message letting the other users know there is a new user. From there we create a new Thread, which calls the runChat method. Lets first look at the startChat method

01 private void startChat()
02 {
03 //create our StreamReader object to read the current stream
04 reader = new System.IO.StreamReader(client.GetStream());
05 //create our StreamWriter objec to write to the current stream
06 writer = new System.IO.StreamWriter(client.GetStream());
07 writer.WriteLine("Welcome to PCChat!");
08 //retrieve the users nickname they provided
09 nickName = GetNick();
10 //check is the nickname is already in session
11 //prompt the user until they provide a nickname not in use
12 while (PC.ChatServer.nickName.Contains(nickName))
13 {
14 //since the nickname is in use we display that message,
15 //then prompt them again for a nickname
16 writer.WriteLine("ERROR - Nickname already exists! Please try a new one");
17 nickName = GetNick();
18 }
19 //add their nickname to the chat server
20 PC.ChatServer.nickName.Add(nickName, client);
21 PC.ChatServer.nickNameByConnect.Add(client, nickName);
22 //send a system message letting the other user
23 //know that a new user has joined the chat
24 PC.ChatServer.SendSystemMessage("** " + nickName + " ** Has joined the room");
25 writer.WriteLine("Now Talking.....\r\n-------------------------------");
26 //ensure the buffer is empty
27 writer.Flush();
28 //create a new thread for this user
29 Thread chatThread = new Thread(new ThreadStart(runChat));
30 //start the thread
31 chatThread.Start();
32 }

The last method in our DoCommunicate.cs Class is the runChat method called by the new thread in startChat. This is simply for reading the current stream and sending our messages to the chat window.

01 private void runChat()
02 //use a try...catch to catch any exceptions
03 {
04 try
05 {
06 //set out line variable to an empty string
07 string line = "";
08 while (true)
09 {
10 //read the curent line
11 line = reader.ReadLine();
12 //send our message
13 PC.ChatServer.SendMsgToAll(nickName, line);
14 }
15 }
16 catch (Exception e44)
17 {
18 Console.WriteLine(e44);
19 }
20 }

That is the end of our DoCommunicate class. So far you have seen how to create a chat server, a class to handle the work of the chat application. You have learned about TcpClients, TcpListeners, StreamReaders, StreamWriters, and Threads. We discussed the purpose of a multi threaded application, and how to create one, and you have learned about adding an alias to your reference to prevent namespace collision in your application.

Now that we have our chat server completely defined, we need a client application to chat with. In this application I have a single form, ChatClient, but I did this a little differently. I didn’t add any controls via drag and drop, I added them at runtime, personally I wouldn’t recommend this for new programmers.

First thing i our client application is a Windows API call, the reference we need is the ExitProcess function. That looks like this

1 [DllImport("kernel32.dll")]
2 private static extern void ExitProcess(int a);

In void Main is where I create a new form, add my controls, set the properties of the window, WindowState, Text, and my TcpClient and call the Connect method of the System.Net.Sockets.TcpClient Class.

With the Connect method you provide the IP address, or host name, along with the port number to connect to, then it connects you to that information. Since this is a basic application, that information is hard coded into the application, with a real application you would have an area to give the user the option to specify which chat server they wish to connect to. Aside from the Main method we have three more methods:

  • ChatClient_Closing: This handles what needs to be done once the user closes the application. This all happens as the form is closing.
  • key_up: This is what sends our message to the chat window. Since I do it on the key up event, they will see what you’re typing as you trype. For an actual application this functionality would be added to a Send button, or when the user hits Enter.
  • Run: This is the running of the chat application, reading the current stream and appending it to the current contents of the chat window, and placing the cursor at the end of the text already in the textbox you’re typing your message into

How I’m appending the text to the current contents of the chat window is by using the AppendText Method of the TextBox Class.

First lets look at the code for the Closing Event of the form.

1 private static void ChatClient_Closing(object s, CancelEventArgs e)
2 {
3 e.Cancel = false;
4 //exit the application
5 Application.Exit();
6 //call the ExitProcess API
7 ExitProcess(0);
8 }

When the form closes, it calls the Application.Exit Method, then the call to the ExitProcess Function.

Next we have the code for the Control.KeyUp Event, which is what sends our messages to the chat window. In this method, we create a StreamWriter for writing to the current stream. To do this we call the GetStream Method of the System.Net.Sockets.TcpClient class. GetStream retrieves the current NetworkStream, used for sending and receiving messages across a network.

01 private static void key_up(object s, KeyEventArgs e)
02 {
03 //create our textbox value variable
04 TextBox txtChat = (TextBox)s;
05 //check to make sure the length of the text
06 //in the TextBox is greater than 1 (meaning it has text in it)
07 if (txtChat.Lines.Length > 1)
08 {
09 //create a StreamWriter based on the current NetworkStream
10 StreamWriter writer = new StreamWriter(tcpClient.GetStream());
11 //write our message
12 writer.WriteLine(txtChat.Text);
13 //ensure the buffer is empty
14 writer.Flush();
15 //clear the textbox for our next message
16 txtChat.Text = "";
17 txtChat.Lines = null;
18 }
19 }

Next we have the code for our run method. This creates a StreamReader Object, using GetStream to retrieve the current NetworkStream, this will be used for reading the messages in the stream. We then append the value in the current stream, line by line, to the chat window.

01 private static void run()
02 {
03 //create our StreamReader Object, based on the current NetworkStream
04 StreamReader reader = new StreamReader(tcpClient.GetStream());
05 while (true)
06 {
07 //call DoEvents so other processes can process
08 //simultaneously
09 Application.DoEvents();
10 //create a TextBox reference
11 TextBox txtChat = (TextBox)client.Controls[0];
12 //append the current value in the
13 //current NetworkStream to the chat window
14 txtChat.AppendText(reader.ReadLine() + "\r\n");
15 //place the cursor at the end of the
16 //text in the textbox for typing our messages
17 txtChat.Selectionstart = txtChat.Text.Length;
18 }
19 }

That is the end of the tutorial Basic Client/Server Chat Application in C#. I am enclosing all three files with this tutorial. They are under the Public GNU License which means you can modify the code to suit your needs, but you need to provide a reference to the original creator of the code. Also, you are not allowed to remove the license header at the beginning of all the files in this solution.

I hope you enjoyed this tutorial, and found it useful. I will next write a tutorial for an advanced client/server chat application, to show what can be done with the techniques we learned in this tutorial.

Thank you so much for reading :)

NOTE: You’re going to want to take the ChatServer Class and possibly make an application out of that as well. I have it as a class file as I’m using a different implementation of the server.

Attached File Basic_ClientServer_Chat.zip (132.21K)

 

Referenced by: http://www.dreamincode.net/forums/topic/33396-basic-clientserver-chat-application-in-c%23/

http://www.codeproject.com/KB/IP/dotnettcp.aspx

http://www.codeproject.com/KB/IP/chatserver.aspx

 
6 Comments

Posted by on January 26, 2011 in C#, Window Application

 

Introduction to Building a Plug-In Architecture Using C#

Part I. Overview

Using this technique, we will use the standard GOF State Pattern by having a host application expose a piece of functionality as interface. The host will then load different implementations based on some criteria that we’ll be choosing. Once we have a class that implements this interface, it can be “plugged” into the host application by dropping the containing dll into a specified folder which provides the host application with a “pluggable” implementation of the exposed interface.

For this article we’ll be using a the following very simple interface that will allow us to build plug-in components that the host can use to perform a calculation on two integers and expose the symbol representing the calculation. (Of course, in a real application the interface for the plug-in would probably be much more complex but we’ll keep it simple here to keep the focus on the technique.)

public interface ICalculator
{
int Calculate(int a, int b);
char GetSymbol();
}

We could have multiple implementations of this interface in our main project or in seperate assemblies that would all look similiar to this class with slightly different implementations:

class Divider:ICalculator
{
#region ICalculator Members

public int Calculate(int a, int b)
{
return a / b;
}

public char GetSymbol()
{
return ‘/’;
}

#endregion
}

Injecting our implementation of the ICalculator interface with the constructor, we could provide a default behavior (division) for a host class while at the same time allowing for other implementations to be injected into our host class. This pattern also makes the host class easier to unit test.

public class CalculatorHost
{
public CalculatorHost(ICalculator calculator)
{
m_calculator = calculator;
}

public CalculatorHost() : this(new Divider()) { }

private int m_x, m_y;
private ICalculator m_calculator;

public int X
{
get { return m_x; }
set { m_x = value; }
}

public int Y
{
get { return m_y; }
set { m_y = value; }
}

public int Calculate()
{
return m_calculator.Calculate(m_x, m_y);
}

public override string ToString()
{
return string.Format(“{0} {1} {2} = {3}”,
m_x.ToString(),
m_calculator.GetSymbol(),
m_y.ToString(),
m_calculator.Calculate(m_x, m_y));
}

}

Part II. Late Binding

What we want to be able to do at the end of the day, is drop a new dll implementing ICalculator into a folder and have the application be able to consume and use the new functionality through a late binding mechanism. For this particular implementation we’ll be placing all the plug-in dlls in a folder called “Plugins” which will a sub directory where the main application’s assembly lives. (Note: Because this is just a sample app and not bullet-proof, when you unzip and build the samples, you may have to manually add the “Plugins” sub-folder to avoid a runtime exception. Of course, you’ll also have to place the plug-in dlls into this folder for the host application to load them.).

Because our late-binding mechanism uses reflection we will be taking a perf hit. One way to minimize the impact is to try to cache the results of this operation. In order to do this, we’ll use a static class to hold the results of our binding. One approach would be to perform our late binding when the application starts. In this particular implementation we’ll use the lazy-load approach and create a loader method that will populate a list of CalculatorHost objects and so we will take the hit the first time the application requests the plugins (which will probably be early in the life of the application).

public static class CalculatorHostProvider
{

private static List<CalculatorHost> m_calculators;

public static List<CalculatorHost> Calculators
{
get
{
if (null == m_calculators)
Reload();

return m_calculators;
}
}
}

When Reload() is called for the first time, we will create the new list. We also may want to reload the plugins while our application is running. For instance, if we have dropped a new implementation in the “Plugings” subdirectory and can’t afford to restart the application. In this case, we’ll clear the existing calculators list.

Next we’ll load all the assemblies in the “Plugins” subdirectory and iterate through them to locate the ones that we can use for creating a new CalculatorHost object.

public static void Reload()
{

if (null == m_calculators)
m_calculators =
new List<CalculatorHost>();
else
m_calculators.Clear();

m_calculators.Add(new CalculatorHost()); // load the default
List<Assembly> plugInAssemblies = LoadPlugInAssemblies();
List<ICalculator> plugIns = GetPlugIns(plugInAssemblies);

foreach (ICalculator calc in plugIns)
{
m_calculators.Add(
new CalculatorHost(calc));
}
}

Part III. Attributes

While not absolutely required for this technique, it is a good idea to explicitly declare our plugins to ensure that the intent of the interface implementation is actually for a plug in component for our host application. In order to do this we’ll use a custom attribute decorator for any class that will be implementing ICalculator and is supposed to function as a plugin for our host. There may be cases where we would only want one implementation of a particular plug in instead of this articles approach of having multiple plugins or maybe we would want to have priorities assigned to each plug-in. In order to do that, we could put some identifier in the attribute class by which we could sort and filter the plugins to get the one(s) we want. We can also look at the assembly versions. It is probably something we would run into at some point using this technique and using attributes for providing metadata on the plugins is a pretty good solution.

[AttributeUsage(AttributeTargets.Class)]
public class CalculationPlugInAttribute : Attribute
{
public CalculationPlugInAttribute(string description)
{
m_description = description;
}

private string m_description;

public string Description
{
get { return m_description; }
set { m_description = value; }
}
}

So now, when we build a plug-in, we’ll make sure to decorate it to explicitly declare the intent of the implementation. This is especially helpful if there are other developers building plugins for our application. In a seperate project we have an implementation of the ICalculator used for adding two numbers together. We’ll build this solution, take the resulting dll and drop it in the “Plugins” folder of the host application.

[CalculationPlugInAttribute(“This plug-in will add two numbers together”)]
class Adder: ICalculator
{
#region ICalculator Members

public int Calculate(int a, int b)
{
return a + b;
}

public char GetSymbol()
{
return ‘+’;
}

#endregion

}

Part IV. The Guts

Loading the assemblies from the “Plugins” folder is a straightforward process. We’ll find all the dlls in the folder and use Assembly.LoadFile() to add them to a list.

private static List<Assembly> LoadPlugInAssemblies()
{
DirectoryInfo dInfo = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, “Plugins”));
FileInfo[] files = dInfo.GetFiles(“*.dll”);
List<Assembly> plugInAssemblyList = new List<Assembly>();

if (null != files)
{
foreach (FileInfo file in files)
{
plugInAssemblyList.Add(
Assembly.LoadFile(file.FullName));
}
}

return plugInAssemblyList;

}

Next, we’ll take the resulting list and get all the types that implement our ICalculator interface and have the CalculationPlugInAttribute declared. We’ll use the Activator class to instantiate an instance of each class we found and return the resulting list of instantiated calculators.

static List<ICalculator> GetPlugIns(List<Assembly> assemblies)
{
List<Type> availableTypes = new List<Type>();

foreach (Assembly currentAssembly in assemblies)
availableTypes.AddRange(currentAssembly.GetTypes());

// get a list of objects that implement the ICalculator interface AND
// have the CalculationPlugInAttribute
List<Type> calculatorList = availableTypes.FindAll(delegate(Type t)
{
List<Type> interfaceTypes = new List<Type>(t.GetInterfaces());
object[] arr = t.GetCustomAttributes(typeof(CalculationPlugInAttribute), true);
return !(arr == null || arr.Length == 0) && interfaceTypes.Contains(typeof(ICalculator));
});

// convert the list of Objects to an instantiated list of ICalculators
return calculatorList.ConvertAll<ICalculator>(delegate(Type t) { return Activator.CreateInstance(t) as ICalculator; });

}

Part V. Using Our Plug-In Architecture

I have two projects (a console app and a windows app) implementing the plug in architecture in the code accompanying this project.

The console app is simple and consists of less than a dozen lines:

static void Main(string[] args)
{

int
x = 34,
y = 56;

Console.WriteLine(String.Format(“x={0} y={1}”, x.ToString(), y.ToString()));

foreach (CalculatorHost calculator in CalculatorHostProvider.Calculators)
{
calculator.X = x;
calculator.Y = y;
Console.WriteLine(calculator.ToString());
}
Console.ReadLine();

}

The windows application uses our plugins for the contents of a drop down (bound when the form loads) and is also fairly simple:

private void Form1_Load(object sender, EventArgs e)
{
m_cbCalculation.DisplayMember =
“Operator”;
m_cbCalculation.DataSource =
CalculatorHostProvider.Calculators;
}

Remember… both of these projects need a subdirectory called “Plugins” to run and you will have to manually drop the implementation of each plugin into this directory for the host application to consume it. Also, nither of these projects are bullet-proof by any means and are demonstration purposes only. We would have to add error handling before they would be production ready. Also, we would probably want to add enough unit tests to our projects to ensure everything is working properly before a distribution build.

Anyways… the important thing is the technique. We could adapt this approach for many differerent types of applications such as creating windows or web services with pluggable functionality or building a mission-critical application architecture where we can plan for future version deployment such that a customer would just have to drop a new dll in a folder in order to avoid application down-time as a result of a new installation process.

I hope you found this article useful.

Until next time,
Happy coding

 

Referenced by: http://www.c-sharpcorner.com/uploadfile/rmcochran/plug_in_architecture09092007111353am/plug_in_architecture.aspx

 
Leave a comment

Posted by on January 10, 2011 in C#, Window Application

 

How to Setup a Dedicated Web Server for Free

All great websites have a great server behind them. In this tutorial, I’ll show you how to set up a dedicated web server (with Apache, MySQL, and PHP) using that old computer you have lying around the house and some free software.

 


1. A Quick Overview

In this tutorial, we are aiming to accomplish several things:

  • We’re going to install the Ubuntu Server operating system. I commonly use Ubuntu because of its ease of use and
    simple administration. It also has a rather large and extremely active community behind it, which makes getting
    support a breeze.
  • We’re going to install an OpenSSH server. This allows you to administer your server from remote computers.
  • A LAMP (Linux, Apache, MySQL, and PHP) stack is going to be installed. This provides the backbone that will run
    your web site. Apache is the industry standard web server on Unix-based operating systems; it’s what most web hosts
    use (NETTUTS is using it right now!) and it’s what we’re going to use.
  • We’re going to install a firewall to protect your server from unauthorized access.

In order to follow this tutorial, you’re going to need a few items:

  • A computer to use as your server. It doesn’t need to be powerful; as long as it’s not ancient, it’ll work fine. Please
    don’t do this on your desktop PC; Ubuntu will completely wipe your computer.
  • A CD burner and a blank CD. These are so that you can burn Ubuntu to a disk in order to install it.
  • Time. Seriously, this process is time-consuming, especially if you run into problems. Try to set aside an afternoon
    to follow this tutorial.

You may be asking why you’d want to have your own web server. There are several reasons, a few of them being: you can have your own testing
ground for your websites; with a little modification, you could host your own site; and, you will learn a lot about Linux/Unix as you go.
With that said, let’s get started!


2. Download Ubuntu Server

First and foremost, we’re going to need a CD with Ubuntu on it. Point your web browser to http://www.ubuntu.com/,
and click download from the menu to the left. You will now be presented with a box with two tabs: “Desktop Edition” and
“Server Edition”. Click the “Server Edition” tab, and select “Ubuntu 8.04 LTS”. Next, select a download location from the
drop-down box. Finally, hit the “Begin Download” button.

Now you need to burn the ISO (the file that you downloaded) to a blank CD. If you don’t know how to do this, there is an
excellent guide at https://help.ubuntu.com/community/BurningIsoHowto


3. Install Ubuntu Server

Now that you’ve downloaded and burned the ISO, let’s get Ubuntu installed on your server. Put the disk in the drive, and boot from the CD. In most modern computers, this will happen by default if a disk is in the drive when you turn it on. If it doesn’t, then you need to press a key on your keyboard right when you turn it on. For my laptop, it’s F12, and for my server, it’s F2. It just depends on your computer. You can find it by looking at the text on your screen right when you turn the computer on, during the BIOS. You’ll see something like “Press [KEY] to change boot order”. Press that key, and select your CD drive.

Still with me? Good. Now that you’ve booted up Ubuntu, you should see the following screen:

Select your language, and hit enter. Now you’ll see this screen:

Select “Install Ubuntu Server”, and away we go!

The installer will now ask you if you want it to detect your keyboard layout. Personally, I always choose no, because
it’s faster to select a standard american keyboard from the list than to have the installer detect it. Either option is fine,
just follow the on-screen instructions.

After you’ve done that, you’ll now see a bunch of loading screens saying things like “Detecting CD-ROM drives” and such.
These should pass quickly and without problems. However, during these screens, the installer will try to auto-configure your
network settings. For most cases, this will work without complaint. However, if it doesn’t work for you, just follow the
on-screen instructions to get it working.

After it’s done with all of that, it will ask you for a host name. You can usually set this to anything; I always set
mine to “web-server”.

The system will now want you to set the time zone for your clock. For me, it’s Pacific. Choose the one that applies to
you.

Now, the system will detect more hardware, and you’ll be prompted to “partion the disk(s)”. Select “Guided – use entire
disk”.

You will now need to select the disk you wish to partition. For most setups, only one disk will be available; however,
for more specialized systems, more options will be available here. Choose the one that applies to you.

It will ask you if you want to write the changes to the disk. Select “Yes” and hit enter. The installer will now proceed
to format the drive and set up the partitions.

Now the magic happens. The system will begin to install. While this happens, go get a cup of coffee. This can take anywhere
from 10 minutes to an hour. It just depends on your system. There might be times that it seems like it’s frozen; don’t worry,
it isn’t. Just let it do it’s thing. However, if it’s stuck on one thing for upwards of an hour, then yes, it is frozen.

Now that the system is installed, it needs to set up the account you are going to login with. First, give it your full
name and hit “Continue”.

Now give it your username. It will normally just set it as your first name,
but you can change it. One name you may not use is “root”.

You will now be asked to provide a password. It is ESSENTIAL that you choose a strong password, or your server will not
be secure at all. I recommend at LEAST a mixture of numbers, lowercase letters, and uppercase letters. However, for my servers
I use symbols, as well as a mixture of the above. DO NOT use a password shorter than 7 characters.

Then, re-enter your password to verify that you typed it correctly.

The system will now attempt to configure the “Package Manager” (we’ll get to what that is shortly). Provide it with your
proxy information, or leave it blank if you don’t use a proxy, and select “Continue”.

The system will now scan several servers looking for updates and configuration settings.

After that has completed, you will be presented with several options to install server software. Now, listen VERY carefully.
Select OpenSSH server, and press SPACE, NOT ENTER. If you hit enter, the install will proceed without installing the OpenSSH server.

You could install “LAMP server” as well, but I have no experience with this option, so we’re going to install it all with a different
command later on.

The system will now install your selected software, as well as other system components.

Finally, the install will finish. Remove the CD, and hit enter. The computer will reboot. If all goes well, you will be
presented with a screen that looks similar to the following:

Congratulations! You’ve just finished the hardest part. Ubuntu is now installed, and it is time to turn this computer into
a web server.


4. Update Your New Server

Before we go any further, we need to make sure your server is up-to-date. To do this, you need to login. First, type your username
(the one you chose earlier), press enter, and then type your password. As you’re typing your password, you’ll notice that nothing
seems to be happening. Don’t worry, that’s the way it was designed to work. After you’ve finished typing your password, hit enter,
and your screen should look similar to the one below if all went well:

Now, type:

sudo aptitude update && sudo aptitude dist-upgrade

It will ask you for you password, and again, you won’t see anything as you’re typing it. After you’ve done that, it will ask you if
you want to continue. Type “y” and press enter. Your screen will look similar to the following:

Your system will now download and install all the latest updates. This will take a while depending on your internet connection. After
it has finished, your computer will need to be rebooted. To do this, type:

sudo shutdown -r now

And let it reboot. Your server is now completely updated.


A Quick Note About “Sudo”

By now, you may have noticed that all of the commands you have typed have started with “sudo”. This is because they require
administrator privileges, and that’s what “sudo” does. It runs the command (i.e. “shutdown”) as an administrator, allowing it to work
properly. This is also why it asks you for your password. However, after you have typed “sudo” once and entered your password,
you do not have to enter your password again for five minutes. Not all commands require sudo, only ones that modify parts of the system.
Got all of that? Good.


5. Install Apache, MySQL, and PHP

It is now time to install some programs. In order to access your sites from the internet, we’re going to need to install a web server (Apache). In additon to the web server, we’ll
also want a database server (MySQL) and a server-side language (PHP) so that we can run popular applications such as WordPress. So,
let’s get to it!

Installing programs on Ubuntu is a lot different than installing programs on Windows or
OS X, in that Ubuntu will download and install the programs for you with a simple command. This is because Ubuntu has something called
a Package Manager, which manages nearly all the programs on your system. All we have to do is tell the package manager
(called “aptitude”) that we want it to install Apache, MySQL, and PHP. To do this, type the following command:

sudo aptitude install apache2 php5-mysql libapache2-mod-php5 mysql-server

And press enter. Aptitude will download and install of the programs you specified. It will also download and install any
dependencies.

During the install process, MySQL will ask you for a root password. You can set this to anything, just be sure you make it long and secure.
Whatever you do, DO NOT leave this blank.

After that has all finished, you now have a fully working web server. To test it out, first find your server’s IP by typing:

ifconfig | grep inet

It’s usually the first IP returned. In my case, it’s 192.168.177.129. Now that you know the IP, open your web browser and point it
to your server IP. If you see the “It works!” message, then congratulations, it works.

However, we’re not done yet. We don’t want Apache or PHP to disclose any information about themselves, as this information is not needed
by your users and could pose a security risk. First, back up the original Apache configuration file:

sudo cp /etc/apache2/apache2.conf /etc/apache2/apache2.conf.bak

Now open the configuration file:

sudo nano /etc/apache2/apache2.conf

Scroll down (down arrow) to where it says “ServerTokens Full” and change it to read “ServerTokens Prod”

Now, scroll down a little further and change “ServerSignature On” to “ServerSignature Off”

Finally, press Control-O followed by Control-X. That will save the file and exit the text editor.

Now, we need to do the same thing for PHP. First, back up the original PHP configuration file:

sudo cp /etc/php5/apache2/php.ini /etc/php5/apache2/php.ini.bak

Open the configuration file:

sudo nano /etc/php5/apache2/php.ini

Change “expose_php = On” to “expose_php = Off”

Again, press Control-O followed by Control-X. Now that the configuration files are updated, restart Apache:

sudo /etc/init.d/apache2 restart

You are done setting up Apache, MySQL, and PHP.


6. Install a Firewall

We now are going to lock down our server a bit more by installing Shorewall, a command-line firewall. To install it:

sudo aptitude install shorewall

By default, Shorewall is installed with no rules, allowing complete access. However, this is not the behavior we want.
Instead, we’re going to block all connections to anything other than port 80 (HTTP) and port 22 (SSH). First, copy the configuration
files to the Shorewall directory:

sudo cp /usr/share/doc/shorewall-common/examples/one-interface/* /etc/shorewall/

Now, open the “rules” file:

sudo nano /etc/shorewall/rules

Add these lines above where it says “#LAST LINE”

HTTP/ACCEPT	net		$FW
SSH/ACCEPT	net		$FW

Then press Control-O and Control-X. Your firewall is now configured to only accept HTTP and SSH traffic. The last thing we need to
do is tell Shorewall to start on boot. So, open up the main Shorewall configuration file:

sudo nano /etc/shorewall/shorewall.conf

Scroll down to “STARTUP_ENABLED=No” and set it to “STARTUP_ENABLED=Yes”

Press Control-O and Control-X. Now, open the Shorewall default configuration file:

sudo nano /etc/default/shorewall

And change “startup=0″ to “startup=1″. Press Control-O and Control-X. Finally, start your firewall:

sudo /etc/init.d/shorewall start

Congratulations! Your firewall is now set up and protecting your server.


7. Add Your Website to Your Web Server

Now that you’ve got everything all set up, you’d probably like to add a website to it. By default, all of the files Apache serves
up to the internet are located at “/var/www/”. However, you cannot write to this folder. Let’s make it so you can:

sudo usermod -g www-data [YOUR USERNAME]
sudo chown -R www-data:www-data /var/www
sudo chmod -R 775 /var/www

What happened there was you added yourself to the “www-data” group, and made the website folder writable to the members of the “www-data”
group.

Now, you’re going to log into your server using SFTP (not to be confused with FTPS). Some clients that support SFTP are:
WinSCP (Windows, Free), FileZilla (Windows, Linux, OS X, Free),
Cyberduck (OS X, Free), and, my personal favorite, Transmit
(OS X, $30)

Connect to your server using your username and password, and, if your client supports it, a default path of “/var/www” (if it doesn’t,
simply browse to /var/www once you have logged in): (Transmit pictured)

You may now add your files to this folder (/var/www) and they will show up on your server when you browse to it with your
web browser.

Now, you may wonder why we’re using SFTP instead of FTP. Mainly, because SFTP is already built into OpenSSH (which you installed
earlier). However, it is also a lot more secure than FTP, and makes it difficult (if not impossible) for malicious users to gain access
to your login credentials.


8. Make Your Server Accesible to the Internet

Most modern home networks are behind a router these days. Because of this, your web server will not be visible to the internet without
a little work. As I don’t have every router available to test with, I can only give you general directions in this area.

There are two ways to open your server up to the internet: a DMZ or Port Forwarding. The main difference you’ll notice is that with a DMZ, your server uses the
firewall we installed earlier to protect itself. However, with Port Forwarding, your server will be protected by your router’s firewall.

However, before we go on, you’re going to want to give your server a static LAN address. To do that, login to your router, and look for something
along the lines of “Static IPs” or “Static Routing”. After you have given your server a static LAN address, you can do these next parts. Remember,
Google is your friend.

To port foward, there is an excellent website, PortForward.com, that, while ugly, can
help you get the job done for almost any router. The ports that you want to forward are 22 and 80.

To create a DMZ, you need to login to your router and look for something like “DMZ settings”. Once you find it, add your server to
the DMZ, and you’ll be set. Again, Google is helpful in situations like this.

Now, find your public IP, and voila! You can access your server from anywhere as long
as your IP doesn’t change.


9. Managing Your Server Remotely

Beside allowing you to upload files, OpenSSH allows you to login to your server from anywhere as long as you know it’s IP. For Windows, you’ll
need an SSH client. I recommend Putty. For OS X, SSH is
already installed. Simply open up Terminal, and type “ssh you@yourip“. For Putty, choose SSH, and put in your IP, username, and password
when it asks for it. You’ll notice that, once you login, it looks exactly the same as the screen on the server:

You can do anything from here that you would do actually sitting at the server. To logout from the server, simply type “exit” and hit enter.


10. That’s It!

You now have a completely functioning web server. It makes for a great testing ground, and would even be suitable to host websites with fairly
low traffic. There is obviously a lot left to be learned, but hopefully you have gained a little insight into how web servers work.

If you’d like to read more on the topics I covered, here are some great guides:

 

Referenced by: http://net.tutsplus.com/tutorials/php/how-to-setup-a-dedicated-web-server-for-free/

 
1 Comment

Posted by on January 6, 2011 in General

 

.NET Based Add-in/Plug-in Framework with Dynamic Toolbars and Menus

.NET based Addiin Project Framework

Introduction

Building a software product which can meet the future needs of customers is always a challenging task. This is especially the case when the software enters into the enhancement / maintenance stage. At this stage, the team members who had originally created the software might have left the company, or they may be in other project teams. You will be lucky if you can find a team member who was originally in the development team to help you. If a programmer who is new to the product is involved in the enhancement of the product, then there is a greater chance that he will do some mistake and will break the existing functionality, which will be unidentified till it happens in the customer�s site. Worst is the case if the software is not designed well and proper documentation is not available. Also, the developmental cost of adding a new functionality to an existing product is also very high just because the entire system needs to be tested again and shipped as another version. This is where the Addin Project Framework wins. In my previous article, I had shown you how to write a plug-in framework with dynamic toolbars and menus in VC++, MFC, ATL, and COM. In this article, I will show you how to do the same thing using the .NET framework libraries.

In spite of the fact that Microsoft is pushing their customers to .NET technology, their product VS.NET 2005 Product Framework is still written in old Win32, MFC, and COM technologies. This forced me to think that there may be some problems with the current .NET framework libraries which forces Microsoft not to use it in their main products such as VS.NET 2005 (or is there any other reason why they didn�t use the .NET technology in their products?). This article is the outcome of this thinking. This article is just written out of curiosity to know how difficult it is to write a plug-in framework with dynamic toolbars and menus, in .NET. To my surprise, writing a .NET plug-in framework similar to the one which I wrote in VC++, MFC, and ATL is very easy, and it will take only half the time compared to VC++, MFC, and ATL. Apart from some small limitations like keyboard shortcut key customization in .NET (the .NET library provides only predefined short cut keys), I didn�t see any difference, and it is more efficient as far as the implementation is concerned.

In this article, I will explain this .NET based add-in project framework architecture, how the plug-in loads and its interaction with the framework, how to invoke the add-in functions from menus and toolbars etc. I will also discuss the pros and cons of both the approaches (the .NET based approach, and VC++, MFC, and ATL/ COM based approach) which I think will be useful in your future projects. There are other plug-in frameworks based on the .NET framework which are available in CodeProject. The advantage with this framework is that it is very simple and easily understandable compared to other frameworks. You can extend this framework to suit your needs, once you get an idea about how it works.

Background

This article assumes that you have some background in C# programming and are familiar with some common namespaces and libraries which are available, that you have some idea about building .NET components, XML, interfaces etc. The core of this framework depends on the System.Reflection namespace and a good idea about this namespace is assumed.

Brief Architecture

The brief architecture of the .NET plug-in framework model is shown below:

.NET based Addiin Project Framework

Fig. 1 .NET Plug-in Framework Architecture.

The architecture consists of an interface DLL called AddinInterfaces.dll which contains the necessary interfaces which every framework (IProjectFrameworkApp) and plug-in (IProjectFrameworkAddin) should implement. The basic interface definition of each interface is given below:

Collapse
public interface IProjectFrameworkAddin
{
  void InitializeAddin(ref IProjectFrameworkApp ref 
       ProjectFrameworkApp, long lSession,bool bFirstLoad); 
  void UnInitializeAddin(long lSession); 
}

This is the interface which every plug-in should implement so as to establish a connection between the plug-in and the framework. The functions in this interface is called by the framework at appropriate times.

Collapse
public interface IProjectFrameworkApp
{
  void AddCommandsInfo(string strXMLMenuInfo, long lSession, 
       object lInstanceHandle, object lToolbarInfo);
  void SendMessage(string strMessage); 
}

This interface is implemented by the framework which is used by the plug-ins so as to send information and messages to the framework.

You can add additional interfaces in this DLL which will be implemented by the framework or the add-in, depending on the needs.

XML Plug-in Menu Format

The framework uses the following XML structure to exchange information about the menu, toolbars, and other information in the add-ins.

Collapse
<ProjectFrameworkAddin>
 <AppVer>1</AppVer>
 <AddinName>Report Addin</AddinName>
 <ToobarButtonCount>1</ToobarButtonCount>
 <MainMenu>
  <Name>Bar Code</Name>
  <ShortcutKeyIndex>1</ShortcutKeyIndex>
  <SubMenu>
      <Name>Bar Code</Name>
      <ShortcutKeyIndex>1</ShortcutKeyIndex>
    <LeafMenu>
      <Name>Test Menu</Name>
      <FunctionName>AddinFunctionName</FunctionName>
      <HelpString>Some Status bar text</HelpString>
      <ToolTip>Some tool tip text</ToolTip>
      <ToolBarIndex>Addin2Settings.ico</ToolBarIndex>
      <ShortCutKey>Ctrl + H</ShortCutKey>
    </LeafMenu>
    <LeafMenu>
      ........
      ........
    </LeafMenu> 
  </SubMenu>
  <SubMenu>
    ..............
    ..............
  </SubMenu>
 </MainMenu>
 <MainMenu>
  ......
  ......
 </MainMenu>
</ProjectFrameworkAddin>

Program Flow

The basic program flow of this framework is in shown in fig 2 as a high level sequence diagram.

Project Framework Sequance Diagram

Fig. 2 Program Flow

Using the code

The sample source code contains four projects, vis. ProjectFramework, AddinInterfaces, Addin1, and Addin2. The ProjectFramework is the core app which loads all the add-ins which are found in the addin directory. AddinInterfaces is the DLL which is referenced by the framework and the add-ins, which contains all the interface definitions. Addin1 and Addin2 are two sample add-ins. Please see the source code for more details. The important blocks of code on which the application is built up are given below.

In the AddinProjectFramework constructor, please note the code in bold and see the high level program flow diagram (Fig. 2).

Collapse
public AddinProjectFramework()
{
    // // Required for Windows Form Designer support // 

    m_PluginManager= new PluginManager(); 
    m_FrameworkApp = new ProjectFrameworkApp(); 
    m_AddinToolbarArray= new ArrayList();
    
    System.Configuration.AppSettingsReader 
         configurationAppSettings = new 
         System.Configuration.AppSettingsReader(); 
    m_strPluginFile=((string)
         (configurationAppSettings.GetValue(
         "PluginFile", typeof(string))));

    InitializeComponent(); 
    m_PluginManager.ProjectFramework=this; 
    m_FrameworkApp.ProjectFramework=this; 
    //Set the IProjectFrameworkApp reference 

    m_PluginManager.m_FrameworkApp = 
      (IProjectFrameworkApp)m_FrameworkApp; 

    //Load all addins

    if(CheckAddinSettings()) 
    { 
        LoadAllAddins(m_bLoadAllAddins);
        m_PluginManager.LoadAddinMenus();
        m_PluginManager.LoadAddinToolbars();
        m_PluginManager.LoadAddinToolbarMenus();
        //Remove the about box from 

        //the Middle and add it towards the end.

        //Help menu is here

        mainProjectMenu.MenuItems.RemoveAt(3);
        mainProjectMenu.MenuItems.Add(menuItemHelp);
    }
}

The core of the framework depends on a class called PluginManager. The skeleton of the plug-in manager is given below:

Collapse
namespace ProjectFramework
{

/// This class handles all the plugin

/// related activities their 

///loading, saving etc 

public struct AddinCommadInfo
{
    //All Menus before coming to the leaf node 

    public ArrayList MenuStringsArray;
    public int iCommandID;
    public string strMenuString;
    public string strHelpString;
    public string strToolTip;
    public string strFunction;
    public string strShortCutKey;
    public string strToolbarIndexName;
    public int iSeparator;
    public MenuItem Menu;
    void Initialize()...
};
public struct AddinInfo 
{
    public string strAddinName;
    public string strAddinDllName;
    public string strAddinVersion;
    public object lInstanceHandle;
    public object lToobarRes;
    public bool bLoadAddin;
    public int lToolbarButtonCount;
    public Assembly AddinAssembly;
    public Type AddinType;
    public IProjectFrameworkAddin FrameworkAddin;
    public string strAddinInterfaceName;
    public ArrayList AddinCommadInfoArray;
    void Initialize() ....
};
public class PluginManager 
{
    public AddinInfo[] AddinInfoArray;
    public AddinProjectFramework ProjectFramework;
    public IProjectFrameworkApp m_FrameworkApp;
    public bool m_bLoadAddinsOnStartup;
    private string m_strXMLFileName;
    public PluginManager()....
    public bool LoadPluginDetailsFromXML(string 
                strXMLFileName)..
    public bool SavePluginDetailsToXML()...
    public bool LoadAddAddinAssemblyInfo(string 
                strAddinFolderName)...
    public bool UnloadAllAddins()...
    public bool InvokeAddinMember(int iAddinIndex, 
                string strFunctionName)...
    public string GetMainInterfaceName(string strDllName)...
    public int GetAddinIndex(string strDllName)...
    public bool InvokeMenu(ref object AddinMenuItem)...
    public bool UpdateAddinMenuStatus(int iAddinIndex, 
                bool bEnable)...
    public bool LoadAddinMenus()...
    public bool LoadAddinToolbars()...
    public bool LoadAddinToolbarMenus()...
    public void GetMainMenuItem(string strMenuName, 
                out MenuItem Item)....
    public void GetMenuItem(string strMenuName, 
                ref MenuItem ItemParent, 
                out MenuItem Item)...
}

Comparison Between .NET based and MFC, ATL, COM-Based Plug-in Frameworks

Feature .NET Based Plug-in Framework MFC, ATL, COM-Based Plug-in Framework
Ease of use Very easy to use. Difficult to use. Needs to know a lot about the intricacies of COM.
Flexibility For database applications, this plug-in framework is good. Not flexible for high-end applications like spread sheets and graphics packages. Good for all sorts of frameworks where a great deal of flexibility is needed.
Modularity .NET libraries are loosely coupled. So easy to design and develop pattern oriented applications. MFC libraries are tightly coupled. Difficult to design and develop pattern oriented applications.
Speed Once the IL is converted into machine code, performance is comparable to native code. Performance is the best. A lot of optimization techniques are available.
Memory Memory consumption is high compared to native code. Also, the CLR determines the lifetime of unused objects. Not deterministic as far as memory optimization is concerned. Has more control over memory. Can make very good memory optimizations.
Support .NET is a new technology. Can expect somewhat good support in the future. Very good support available till now. Time will only tell whether Microsoft will support MFC in the future.
Cost of development Low High.
Data type support Any type which .NET framework is supporting. Supports only COM specific data types, interfaces, and custom interfaces.

Points of Interest

In this version, the features implemented are:

  • Dynamic menus.
  • Dynamic toolbars.
  • Help string and tool tip support.
  • Invoking of methods in plug-ins from add-in menu and toolbar.
  • Automation support explained using a dialog box invoked from one of the plug-ins (Report Add-in -> Sales report -> Today’s Sales Report, Ctrl + B menu).
  • Keyboard accelerator support for plug-in.
  • Loading / unloading of add-ins.
  • Support for getting notification events from add-ins.

Links

History

  • Initial release: May 2, 2006.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

 

Referenced by: http://www.codeproject.com/KB/macros/Net_AddinProjFrmwork.aspx

 
Leave a comment

Posted by on January 6, 2011 in C#, Window Application