Latest Event Updates

System.Reflection – Working with the Assembly Class

Posted on Updated on

The objects within the System.Reflection namespace of the .NET Framework provide us a means to get assembly and type information at runtime. Reflection is useful in situations where you need to introspect assemblies and their types, either to gain information or to make use of their functionality dynamically without knowing its specifics in advance. Sounds pretty cool, right? Well it is!

This article will walk you through the basics of Reflection and the Assembly class and will be followed by additional articles that dive much more deeply into the objects within the System.Reflection namespace.

The System.Reflection.Assembly Class

The Assembly class is the primary class within the System.Reflection namespace. This will be our starting point as we take a look at Reflection.

First, we will create a simple Visual Studio solution that we will call ‘ReflectionWalkthroughSolution’.

In this solution, we will add a C# Class Library project which we will call ‘MyClassLibrary’.

Next, we will add a Console Application which we will call ‘MyConsoleApplication’. These names are nonsensical, but this solution is merely for demonstration purposes :).

So, after doing these things, our Solution Explorer should look like this:

.NET Reflection - Solution Explorer Getting Started

MyConsoleApplication is bolded because I have set it as the startup project. This will allow us to hit F5 to debug the app and be able to use the console application as our entry point.

Next, expand MyConsoleApplication and Add a Reference to the MyClassLibrary project. Once this is done we are ready to start writing a little code.

Assembly.GetExecutingAssembly() Method

The first thing we will look at is how to get a reference to the assembly that is currently executing. To do this we will add some simple code in the Main() method of our console application as shown below. After writing this code, place a breakpoint on the line, then hover the thisAssembly object and take a look at the properties of the assembly and their values.

static void Main(string[] args)
{
    //get the currently executing assembly
    Assembly thisAssembly = Assembly.GetExecutingAssembly();

}

With the code running, when the breakpoint is hit, we can right-click and Add a Watch to the thisAssembly object. When we hit F10 to step over the line, the Watch window is populated with information pertaining to the ‘thisAssembly’ object. See the illustration below.

Hint: Click the image to view it full-size.

.NET Reflection - Assembly.GetExecutingAssembly() Watch Window
Properties of the ‘thisAssembly’ Assembly object

 

Let’s discuss some of the properties that are shown in the Watch window above.

The CodeBase property returns the full path at which the assembly resides. In other words, the execution location for the assembly, including the name of the assembly.dll file itself.

The CustomAttributes property is an IEnumerable<CustomAttributeData> type that displays all of the custom attributes that exist at the assembly level. If you take a look you will see that many of these come directly from the AssemblyInfo.cs file that is found under the Properties folder of the project.

Assembly.CustomAttributes Illustration
The Assembly.CustomAttributes property expanded.

We will dive into custom attributes in a later post. Attributes can be applied to assemblies, classes, properties, methods, and more and through Reflection can be retrieved and read using the appropriate objects. These attributes exist at the assembly level. For now, just be aware that these attributes can be retrieved in this way.

The DefinedTypes property is an IEnumerable<TypeInfo> type that displays all of the types (classes) defined within the assembly. In our simple little console application we only have one defined type. See below.

The Assembly.DefinedTypes property expanded
The Assembly.DefinedTypes property expanded

Looking ahead to later posts, once you have a reference to an Assembly, knowing the types defined in the assembly allow you to dive into the assembly and then into each type as needed.

The FullName property is a string value that displays the full name of the assembly. This is important when discussing a topic such as strong naming.

The Assembly.FullName property
The Assembly.FullName property

There are other properties of the Assembly object that are noteworthy for this introduction. They are the GlobalAssemblyCache property which tells you whether or not the assembly is GAC’ed, the ImageRuntimeVersion which tells you the full version of the .NET Framework against which the assembly is compiled, and the MainfestModule which gives specific information about the assembly itself.

Getting Assemblies loaded in the Current AppDomain (AppDomain.Current.GetAssemblies() Method)

Now that we’ve taken a quick look at the properties of an Assembly and how to get a reference to the currently executing assembly, let’s branch out a little and retrieve all of the assemblies referenced and loaded into the current AppDomain. An AppDomain is an isolated environment in which an application executes. The AppDomain class belongs to the System namespace. To retrieve the AppDomain for the currently executing application, we will the AppDomain.Current property.

If we return to the Main() method of the Program class of our little console application and remove the code we wrote originally and replace it with this code:

static void Main(string[] args)
{
    Assembly thisAssembly = Assembly.GetExecutingAssembly();

    //Get assemblies loaded in the current AppDomain
    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

    //create a StringBuilder to hold assembly information
    StringBuilder sb = new StringBuilder();

    //iterate through the array and write info for each assembly
    foreach (Assembly assembly in assemblies)
    {
        //let's create some rudimentary formatted output to show property values
        //for each assembly
        sb.AppendLine("================================================================");
        sb.AppendLine(String.Format("Assembly: {0}", assembly.FullName));
        sb.AppendLine("================================================================");

        sb.AppendLine(String.Format("CodeBase: {0}", assembly.CodeBase));
        sb.AppendLine(String.Format("Location: {0}", assembly.Location));
        sb.AppendLine(String.Format("Number of Types: {0}", assembly.DefinedTypes.Count().ToString()));
        sb.AppendLine(String.Format("Number of Custom Attributes:  {0}", assembly.CustomAttributes.Count().ToString()));
        sb.AppendLine(String.Format(".NET Runtime Version: {0}", assembly.ImageRuntimeVersion));

        //you can add more stuff here to see more properties...
    }

    string output = sb.ToString();
}

When we view the formatted output from our StringBuilder, it should look something like this:

================================================================
Assembly: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.NET/Framework/v4.0.30319/mscorlib.dll
Location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll
Number of Types: 3029
Number of Custom Attributes:  36
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.VisualStudio.HostingProcess.Utilities, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.HostingProcess.Utilities/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.HostingProcess.Utilities.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.dll
Number of Types: 12
Number of Custom Attributes:  17
.NET Runtime Version: v2.0.50727
================================================================
Assembly: System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
Number of Types: 2273
Number of Custom Attributes:  27
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
Number of Types: 303
Number of Custom Attributes:  25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
Number of Types: 2278
Number of Custom Attributes:  29
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.HostingProcess.Utilities.Sync/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities.Sync\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll
Number of Types: 5
Number of Custom Attributes:  18
.NET Runtime Version: v2.0.50727
================================================================
Assembly: Microsoft.VisualStudio.Debugger.Runtime, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/assembly/GAC_MSIL/Microsoft.VisualStudio.Debugger.Runtime/11.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Debugger.Runtime.dll
Location: C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll
Number of Types: 10
Number of Custom Attributes:  17
.NET Runtime Version: v2.0.50727
================================================================
Assembly: vshost32, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Temp/ReflectionWalkthroughSolution/MyConsoleApplication/bin/Debug/MyConsoleApplication.vshost.exe
Location: C:\Temp\ReflectionWalkthroughSolution\MyConsoleApplication\bin\Debug\MyConsoleApplication.vshost.exe
Number of Types: 1
Number of Custom Attributes:  19
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Core/v4.0_4.0.0.0__b77a5c561934e089/System.Core.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
Number of Types: 929
Number of Custom Attributes:  25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml.Linq/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.Linq.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll
Number of Types: 86
Number of Custom Attributes:  22
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Data.DataSetExtensions/v4.0_4.0.0.0__b77a5c561934e089/System.Data.DataSetExtensions.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Data.DataSetExtensions\v4.0_4.0.0.0__b77a5c561934e089\System.Data.DataSetExtensions.dll
Number of Types: 25
Number of Custom Attributes:  24
.NET Runtime Version: v4.0.30319
================================================================
Assembly: Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Microsoft.CSharp/v4.0_4.0.0.0__b03f5f7f11d50a3a/Microsoft.CSharp.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.CSharp\v4.0_4.0.0.0__b03f5f7f11d50a3a\Microsoft.CSharp.dll
Number of Types: 316
Number of Custom Attributes:  23
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_32/System.Data/v4.0_4.0.0.0__b77a5c561934e089/System.Data.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_32\System.Data\v4.0_4.0.0.0__b77a5c561934e089\System.Data.dll
Number of Types: 1049
Number of Custom Attributes:  21
.NET Runtime Version: v4.0.30319
================================================================
Assembly: System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
================================================================
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Xml/v4.0_4.0.0.0__b77a5c561934e089/System.Xml.dll
Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll
Number of Types: 1229
Number of Custom Attributes:  25
.NET Runtime Version: v4.0.30319
================================================================
Assembly: MyConsoleApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
================================================================
CodeBase: file:///C:/Temp/ReflectionWalkthroughSolution/MyConsoleApplication/bin/Debug/MyConsoleApplication.EXE
Location: C:\Temp\ReflectionWalkthroughSolution\MyConsoleApplication\bin\Debug\MyConsoleApplication.exe
Number of Types: 1
Number of Custom Attributes:  14
.NET Runtime Version: v4.0.30319

Pretty simple, right? Now let’s dig into the Assembly a little deeper.

Getting Types within an Assembly (Assembly.GetTypes() Method)

Every Assembly can contain one of more Types. We can retrieve all of these types via the Assembly.GetTypes() method. To try this out, we replace the code in our console application’s Program class’s Main() method with this code:

static void Main(string[] args)
{
    Assembly thisAssembly = Assembly.GetExecutingAssembly();

    //get all of the Types defined in this Assembly
    Type[] types = thisAssembly.GetTypes();

    //robust code always checks for null FIRST
    if (types != null && types.Length > 0)
    {
        //we'll create a StringBuilder for our formatted output
        StringBuilder sb = new StringBuilder();

        //iterate through the Type[] array
        foreach (Type type in types)
        {
            sb.AppendLine("===============================================================");
            sb.AppendLine(String.Format("Type Name: {0}", type.Name));
            sb.AppendLine("===============================================================");

            sb.AppendLine(String.Format("Type FullName: {0}", type.FullName));
            sb.AppendLine(String.Format("Namespace: {0}", type.Namespace));

            sb.AppendLine(String.Format("Is it a Class?: {0}", type.IsClass.ToString()));
            sb.AppendLine(String.Format("Is it an Interface?: {0}", type.IsInterface.ToString()));
            sb.AppendLine(String.Format("Is it Generic?: {0}", type.IsGenericType.ToString()));
            sb.AppendLine(String.Format("Is it Public?: {0}", type.IsPublic.ToString()));
            sb.AppendLine(String.Format("Is it Sealed?: {0}", type.IsSealed.ToString()));

            sb.AppendLine(String.Format("Qualified Name: {0}", type.AssemblyQualifiedName));

            if (type.BaseType != null && !String.IsNullOrEmpty(type.BaseType.Name))
            {
                sb.AppendLine(String.Format("Base Type: {0}", type.BaseType.Name));
            }

            //there are many, many more properties that an be shown...
        }

        string output = sb.ToString();
    }
}

In our console application, there is only one type, the Program class. When we execute the code above we see information for that single type.

===============================================================
Type Name: Program
===============================================================
Type FullName: MyConsoleApplication.Program
Namespace: MyConsoleApplication
Is it a Class?: True
Is it an Interface?: False
Is it Generic?: False
Is it Public?: False
Is it Sealed?: False
Qualified Name: MyConsoleApplication.Program, MyConsoleApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Base Type: Object

Now that we’ve moved down a level and looked at Types within an assembly, let’s move down one more level.

The next post discusses the System.Reflection.Type class and its properties and methods.

Using C# to Manage IIS – Microsoft.Web.Administration Namespace

Posted on Updated on

Introduction

If you have ever attempted to programmatically manipulate IIS in version 6 or earlier, you discovered that it is not a trivial endeavor! You could use Windows Management Instrumentation (WMI), ADSI, or write code to manually manipulate XML. It just wasn’t a task for the faint of heart! I have done it several times and remember it to be quite a headache.

Beginning with IIS 7, programmatic management of IIS has become much simpler with the Microsoft.Web.Administration objects. Using this API model, you can create websites, application pools, applications, virtual directories, etc. with just a few lines of code! Seriously!

The top-level objects of the API are illustrated below.

mwa_overview

This post will walk you through some of the features of the Microsoft.Web.Administration namespace objects and provide some real-world examples of how to implement this really cool API.

Getting Started

If you have IIS installed on your development machine, you will be able to find the .dll in Windows\system32\inetsrv folder or you can use the Nuget package. Either way, you will need the Microsoft.Web.Administration.dll file as a reference in your project.

Once you add the necessary reference to your project, you are ready to go!

Working with the ServerManager

To get started, we must first create an instance of the ServerManager class. We do this exactly as you might think:

ServerManager server = new ServerManager();

NOTE: It is important to note that an instance of the ServerManager assumes that the machine on which the application is running is the web server. It is possible to connect to a remote server using the ServerManager, and we will look at that later in this post.

Getting Sites Associated with the Server

Let’s get a list of all of the websites within our ServerManager. To do this, we access the Sites property of the ServerManager instance. The code block iterates each Site object.

 ServerManager server = new ServerManager();

SiteCollection sites = server.Sites;
foreach (Site site in sites)
{
    ApplicationDefaults defaults = site.ApplicationDefaults;

    //get the name of the ApplicationPool under which the Site runs
    string appPoolName = defaults.ApplicationPoolName;

    ConfigurationAttributeCollection attributes =  defaults.Attributes;
    foreach (ConfigurationAttribute configAttribute in attributes)
    {
        //put code here to work with each ConfigurationAttribute
    }

    ConfigurationAttributeCollection attributesCollection = site.Attributes;
    foreach (ConfigurationAttribute attribute in attributesCollection)
    {
        //put code here to work with each ConfigurationAttribute
    }

    //Get the Binding objects for this Site
    BindingCollection bindings = site.Bindings;
    foreach (Microsoft.Web.Administration.Binding binding in bindings)
    {
        //put code here to work with each Binding
    }

    //retrieve the State of the Site
    ObjectState siteState = site.State;

    //Get the list of all Applications for this Site
    ApplicationCollection applications = site.Applications;
    foreach (Microsoft.Web.Administration.Application application in applications)
    {
        //put code here to work with each Application
    }
}

For more information regarding all of the properties and methods of the Site class, click here.

Getting Applications Associated with each Site

The applications that run under a Site are represented through the Applications property of the Site class. The Applications property is an ApplicationCollection object. We can iterate each Application object with the following code:

ApplicationCollection applications = site.Applications;
foreach (Microsoft.Web.Administration.Application application in applications)
{
    //get the name of the ApplicationPool
    string applicationPoolName = application.ApplicationPoolName;

    VirtualDirectoryCollection directories = application.VirtualDirectories;
    foreach (VirtualDirectory directory in directories)
    {
        //put code here to work with each VirtualDirectory
    }
}

 Getting Virtual Directories Associated with each Application

The virtual directories associated with each Application are accessible via the VirtualDirectories property of the Application class. We can iterate each VirtualDirectory with the following code:

VirtualDirectoryCollection directories = application.VirtualDirectories;
foreach (VirtualDirectory directory in directories)
{
    ConfigurationAttributeCollection attribues = directory.Attributes;
    foreach (ConfigurationAttribute attribute in attributes)
    {
        //put code here to work with each attribute
    }

    ConfigurationChildElementCollection childElements = directory.ChildElements;
    foreach (ConfigurationElement element in childElements)
    {
        //put code here to work with each ConfigurationElement
    }

    //get the directory.Path
    string path = directory.Path;

    //get the physical path
    string physicalPath = directory.PhysicalPath;
}

Getting Application Pools Associated with a Server

The ServerManager.ApplicationPools property is an ApplicationPoolCollection object that contains all ApplicationPool objects associated with the server. Remember that this is a server-level property and that an ApplicationPool can be shared by multiple Sites and Applications.

ServerManager server = new ServerManager();

ApplicationPoolCollection applicationPools = server.ApplicationPools;
foreach (ApplicationPool pool in applicationPools)
{
    //get the AutoStart boolean value
    bool autoStart = pool.AutoStart;

    //get the name of the ManagedRuntimeVersion
    string runtime = pool.ManagedRuntimeVersion;

    //get the name of the ApplicationPool
    string appPoolName = pool.Name;

    //get the identity type
    ProcessModelIdentityType identityType = pool.ProcessModel.IdentityType;

    //get the username for the identity under which the pool runs
    string userName = pool.ProcessModel.UserName;

    //get the password for the identity under which the pool runs
    string password = pool.ProcessModel.Password;
}

Now that we have taken a quick look at the object hierarchy and iterated through some of the child collections, let’s switch our view to how to create and manipulate objects.

Creating a New Application Pool

To create a new ApplicationPool, we create a ServerManager instance, then check the ApplicationPools collection to see if our ApplicationPool already exists. If it does, we update a few of its properties, if not we create the new ApplicationPool. Regardless of the whether we are creating or updating, we use the ServerManager.CommitChanges() method.

ServerManager server = new ServerManager();

ApplicationPool myApplicationPool = null;

//we will create a new ApplicationPool named 'MyApplicationPool'
//we will first check to make sure that this pool does not already exist
//since the ApplicationPools property is a collection, we can use the Linq FirstOrDefault method
//to check for its existence by name
if (server.ApplicationPools != null && server.ApplicationPools.Count > 0)
{
    if (server.ApplicationPools.FirstOrDefault(p => p.Name == "MyApplicationPool") == null)
    {
        //if we find the pool already there, we will get a referecne to it for update
        myApplicationPool = server.ApplicationPools.FirstOrDefault(p => p.Name == "MyApplicationPool");
    }
    else
    {
        //if the pool is not already there we will create it
        myApplicationPool = server.ApplicationPools.Add("MyApplicationPool");
    }
}
else
{
    //if the pool is not already there we will create it
    myApplicationPool = server.ApplicationPools.Add("MyApplicationPool");
}

if (myApplicationPool != null)
{
    //for this sample, we will set the pool to run under the NetworkService identity
    myApplicationPool.ProcessModel.IdentityType = ProcessModelIdentityType.NetworkService;

    //we set the runtime version
    myApplicationPool.ManagedRuntimeVersion = "v4.0";

    //we save our new ApplicationPool!
    server.CommitChanges();
}

In the previous example, we set the ProcessModel.IdentityType to NetworkService. It’s a more common scenario to have a pre-defined service account that is most likely a domain account reserved specifically for this purpose. In this case, we can make a small modification to our code to implement this change. See below.

if (myApplicationPool != null)
{
    //for this sample, we will set the pool to run under the identity of a specific user
    myApplicationPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
    myApplicationPool.ProcessModel.UserName = UserName;
    myApplicationPool.ProcessModel.Password = Password;

    //we set the runtime version
    myApplicationPool.ManagedRuntimeVersion = "v4.0";

    //we save our new ApplicationPool!
    server.CommitChanges();
}

The UserName and Password values are strings that we would provide to our code when creating the ApplicationPool.

Creating a New Site

Creating a new Site is just as easy as creating a new ApplicationPool!

ServerManager server = new ServerManager();

if (server.Sites != null && server.Sites.Count > 0)
{
    //we will first check to make sure that the site isn't already there
    if (server.Sites.FirstOrDefault(s => s.Name == "MySite") == null)
    {
        //we will just pick an arbitrary location for the site
        string path = @"c:\MySiteFolder\";

        //we must specify the Binding information
        string ip = "*";
        string port = "80";
        string hostName = "*";

        string bindingInfo = string.Format(@"{0}:{1}:{2}", ip, port, hostName);

        //add the new Site to the Sites collection
        Site site = server.Sites.Add("MySite", "http", bindingInfo, path);

        //set the ApplicationPool for the new Site
        site.ApplicationDefaults.ApplicationPoolName = myApplicationPool.Name;

        //save the new Site!
        server.CommitChanges();
    }
}

We have only touched on some of the functionality provided to us by the Microsoft.Web.Administration namespace objects. For more information, this MSDN resource is a great place to start. Remember that this API is only valid for IIS version 7.0 and higher and that if you seek to automate a prior version your plan of attack will be quite different.

How to Find the Public Key Token for a Strongly-Named Assembly

Posted on Updated on

In another post, we discussed why you should strongly name assemblies and how to do it via the Strong Name Tool (sn.exe). In this quick post we will consider another scenario – how to get the public key token string value of a strongly named assembly (dll).

This is easily accomplished by using the Strong Name Tool as well by simply using the -T switch and specifying the path and name of the dll for which to retrieve the public key token. It can also be done in code using the System.Reflection.Assembly class. We will take a quick look at each approach.

Using the Strong Name Tool

To use the Strong Name Tool (sn.exe), simply open the Microsoft Visual Studio Developer Command Prompt and use the following syntax:

sn.exe -T <assembly path and name>

For this example, we will retrieve the public key token for an assembly named SampleClassLibrary.dll that is located in the c:\Temp folder.

Get Public Key Token of Strongly Named Assembly using Strong Name Tool

For this example, the public key token is ba27e13c07659075.

Using the System.Reflection.Assembly Class – Getting the Public Key Token in Code

The manner in which we get the public key token for an assembly depends upon our point of reference. For example, we can do it for the currently executing assembly, do it for a specific assembly based on a file path, or do it for one or all assemblies that have been loaded within the current AppDomain.

Let’s consider two of these scenarios:

Getting the Public Key Token for the Currently Executing Assembly

To demonstrate this approach, I have created a simple method in a sample class:

public void GetPublicKeyTokenForThisAssembly()
{
    byte[] token = Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken();
}

Now, if we execute this code, and place a breakpoint on the line that gets the byte array, we can use Visual Studio debugging to view the results of our token retrieval.

PublicKeyTokenDebuggerView

Since the return type is a byte array, the results are exactly as we would expect. So how do we convert that to the same string that we saw when we did it through the Strong Name Tool?

We write a little bit of C# code to do this as shown below:

byte[] token = Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken();

StringBuilder sb = new StringBuilder();

for (int i = 0; i < token.GetLength(0); i++)
{
     sb.Append(String.Format("{0:x2}", token[i]));
}

string publicKeyToken = sb.ToString();

The result of this is the public key token that we retrieved using the Strong Name Tool above.

If you are wondering what the “{0:x2}” formatter does, this is something known as Composite Formatting. The hexadecimal format specifier (X) converts a number to a string of hexadecimal digits. The “2” instructs the runtime to produce a string with a length of 2.

Getting the Public Key Token for Assemblies in the Current AppDomain

Though the point of this post is not to discuss Reflection or how to use the methods of the Assembly class, it is beneficial to take a look at how this same logic could be applied to all assemblies loaded within the current AppDomain.

To do this, we merely have to change our code to get the assemblies and iterate through them to retrieve the public key tokens.

byte[] token;

StringBuilder publicKeyTokens = new StringBuilder();
Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

foreach (Assembly assembly in loadedAssemblies)
{
    token = assembly.GetName().GetPublicKeyToken();
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < token.GetLength(0); i++)
    {
        sb.Append(String.Format("{0:x2}", token[i]));
    }

    publicKeyTokens.AppendLine("The public key token for assembly " + assembly.FullName + " is:  " + sb.ToString());
}

string allPublicKeyTokens = publicKeyTokens.ToString();

The output of our StringBuilder is as follows:

The public key token for assembly mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly Microsoft.VisualStudio.HostingProcess.Utilities, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly Microsoft.VisualStudio.Debugger.Runtime, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly vshost32, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly System.Data.DataSetExtensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly System.Deployment, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a is:  b03f5f7f11d50a3a
The public key token for assembly System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 is:  b77a5c561934e089
The public key token for assembly WindowsFormsApplication2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null is:
The public key token for assembly SampleClassLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ba27e13c08759075 is:  ba27e13c08759075

If you notice the assembly for my sample WinForms assembly shows a PublicKeyToken that is null. Why is this? Because I did not strongly name that assembly! Also, notice that in our code we extracted the FullName value for each assembly. This is an important point when talking about strong naming because the FullName shows us the name, version, public key token, and culture information.

In this simple little sample, I utilized a solution with two projects: a WinForms project and a class library that is referenced by the WinForms project. In this sample, the class library is strongly named and the WinForms project is not. This is okay, but the opposite scenario is not. Suppose that we strongly name the WinForms assembly but remove the strong name from the class library. The compiler will bark at us then we try to build the solution because as we said before, a strongly named assembly requires all referenced assemblies to be strongly named as well. The output below is generated when we try to build under the second scenario:

strongnamedcompilererror

 

 

Strong-Named Assemblies – Why and How

Posted on Updated on

Overview

In this short post, I will discuss strong-named assemblies in .NET and cover why and how you should use strong naming within your .NET projects.

First, there seems to be a misconception among many developers with whom I’ve worked that strong naming is a security measure. That is not the intent of strong naming! Instead, strong naming provides a means of uniquely identifying assemblies and providing evidence that the assembly to be loaded is indeed the one intended. A strong name consists of the following things:

  • The assembly’s simple text name
  • The assembly’s full version number
  • The assembly’s culture information
  • A Public Key
  • A Digital Signature

Why Use Strong-Named Assemblies?

Based on your scenario, there are some distinct reasons why assemblies should be strongly named.

  • If you plan to deploy your assembly to the Global Assembly Cache (GAC), the assembly MUST be strongly named. This is necessary because the GAC allows multiple versions of the same assembly to coexist simultaneously. Anyone who has been around long enough to remember Windows development in the past, especially prior to .NET remember the term “DLL Hell”. I know this term all too well and learned very quickly a long time ago what a pain it can be. Those are bad memories, so I will move on! I will discuss the Global Assembly Cache in another article.
  • Strong names protect the version lineage of an assembly. If an assembly is strongly named, the assemblies it references must also be strongly named.

Strong naming relies upon asymmetric cryptography. Although the purpose of this article is strong naming, a very quick discussion of asymmetric cryptography is warranted. I will only take a shallow dive into asymmetric cryptography because this purpose of this article is to briefly discuss strong-named assemblies and not encryption.

Asymmetric Cryptography (Public Key Cryptography)

Public Key Cryptography (PKC) is a form of cryptography that requires two “keys” – a public key and a private key. The public key is known to all and the private key is only known to the individual. In a very simple example of PKC, if I decide to send an encrypted message to one of my friends, I use my friend’s public key to encrypt the message, then he uses his private key to decrypt it. If he then decides to send me an encrypted message, he uses my public key to encrypt it then I use my private key to decrypt. Makes sense, right? It is important to note that with PKC, only the public key can be used to encrypt, and only the corresponding private key can be used to decrypt.

How to Create a Strong Name Key Pair

Before strongly naming an assembly, you must first have a public/private key pair file. The key pair is used by the compiler to create the strong-named assembly.

Creating a strong name key pair is made very simple by Microsoft through use of the Strong Name Tool (sn.exe). To create a key pair using this tool, simply follow these steps:

  1. From the Start menu, select your version of Visual Studio in the All Programs menu, click Visual Studio Tools, then select the Visual Studio Developer Command Prompt menu. See the following image.
  2. When the Visual Studio Command Prompt window opens, invoke the Strong Name Tool using the following syntax: sn -k KeyFileName.snk. The Strong Name Tool has numerous switches that can be used with its invocation. The -k switch instructs the tool to create the public/private key pair with the name that you specify. You can specify the path to which the .snk file should be created. In the example below, I create a key pair named MyKeyPair and I write it to the c:\Temp folder.sn_result
  3. When the key pair has been created, the next step is to use it to strongly-name an assembly. This is easily accomplished within Visual Studio by right-clicking the project to be signed and selecting the Properties menu. The example below shows the Properties window for a sample class library that I have created.SigningTabBrowse
  4. Browse to the .snk file and click Ok. The .snk is then associated with the project and when built, the resulting assembly will be strongly-named.

That’s it! Pretty simple. In the next article we take a look at how to extract the PublicKeyToken from a strongly-named assembly.

 

New Features in WCF 4.5

Posted on Updated on

With the release of Windows Communication Foundation (WCF) in version 3.0 of the .NET Framework in 2006, Microsoft set the stage for a unified framework for developing service-oriented applications in .NET. Since then, I have personally become quite a fan of WCF and have created countless services and service clients. WCF is flexible and robust, and with .NET 4.5 has become even better!

Though we won’t dive into detail, I would like to take a quick moment to briefly discuss some of the highlights of the new features in WCF 4.5. These are the new features that I have found to be most exciting:

Generated Configuration Files have been Simplified

When we Add a Service Reference via Visual Studio or use the svcutil.exe, a client config file is generated for us. In earlier versions, this file contained every binding present, including those that just had default values. Now the config files only contain those with non-default bindings.

Configuration Validation

When we build our project(s) via Visual Studio, the attributes that we implement within our project classes are validated against the values in the config files. This allows us to discover issues at built-time without having to wait for run-time.

ASP.NET Compabibility Mode Default

Through use of the aspNetCompatibilityEnabled attribute in the <serviceHostingEnvironment> node, WCF now gives developers access to the functionality of the HTTP pipeline when writing WCF services. Setting this to true enables us to access the HttpContext.

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

Support for Asynchronous Streaming

WCF now truly supports asynchronous streaming – this is a significant scalability enhancement!

IIS HTTPS Simplification

A new HTTPS protocol mapping which greatly simplifies implementing a WCF server over HTTPS when using IIS. I remember this being a slight pain in the past.

<system.serviceModel>
    <services>
         <service name="SampleService">
               <endpoint address=""
                     binding="basicHttpsBinding"
                     contract="ISampleService">
              </endpoint>
         </service>
    </services>
 </system.serviceModel>

Prior to WCF 4.5, we would have had a <basicHttpBinding> node and a <security> node with the mode attribute set to “Transport”. Now we just set a basicHttpsBinding attribute within the endpoint. Pretty cool!

Easier Configuration of WCF Services in Code

In the pre-4.5 days, we had to create a ServiceHostFactory that created the ServiceHost. in WCF 4.5, you merely create a public static method named Configure that accepts a ServiceConfiguration object.

public class MyService : IMyService
{
    public static void Configure(ServiceConfiguration config)
    {
        ServiceEndpoint serviceEndpoint = new ServiceEndpoint(new ContractDescription(&quot;IMyService1&quot;),
                new BasicHttpBinding(), new EndpointAddress(&quot;basic&quot;));
        serviceEndpoint.Behaviors.Add(new MyEndpointBehavior());
        config.AddServiceEndpoint(serviceEndpoint);

        ServiceDebugBehavior sdb = new ServiceDebugBehavior();
        sdb.HttpGetEnabled = true;
        config.Description.Behaviors.Add(sdb);

        ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
        smb.IncludeExceptionDetailInDefaults = true;
        config.Description.Behaviors.Add(smb);
    }
}

Multiple Authentication Types via a Single Binding

You read that right! Beginning with WCF 4.5, multiple authentication types can be used via a single endpoint if you are using HTTP with Transport security. Because IIS allows multiple authentication methods on a virtual directory, these same authentication types can be used by WCF for the virtual directory in which the web service is hosted.

WebSocket Support via Two New Bindings

Two new bindings, NetHttpBinding and NetHttpsBinding provide TCP-like performance over the standard HTTP and HTTPS ports (80 and 443) bidirectionally. The HTTP paradigm that we all know and love has been built based on the concepts of requests and responses. The WebSocket specification defines socket connections between a web browser and a server over HTTP. This is a wonderful enhancement because what we have is a connection between the client and the server and both parties can start sending information any time. A discussion of WebSockets is beyond the scope of this short feature overview, but it is a topic worth discussing at a later date.

UDP Support

Anyone familiar with UDP knows that it is a great way to very quickly send messages in one direction. WCF 4.5 now supports UDP!

Conclusion

Though there are other exciting enhancements in WCF 4.5, these are my favorites and in my mind some of the most interesting.

8004E00F – COM+ was unable to talk to Microsoft Distributed Transaction Coordinator

Posted on Updated on

Problem Overview

When you open the Windows Component Services Configuration Console (dcomcnfg), you see a red, downward-pointing arrow on the My Computer icon under Component Services as shown below.

MSDTC 8004E00F - COM+ was unable to talk to the Microsoft Distributed Transaction Coordinator

This error is commonly caused by the MSDTC service being set to run under the local system account instead of NT Authority\NetworkService.

If you check the MSDTC service and discover that this is indeed the problem, follow these steps to change the identity:

  1. Open the Registry Editor (regedit.exe).
  2. Find the following registry key: HKEY_LOCAL_MACHINE\Software\Microsoft\MSDTC
  3. Right-click TurnOffRpcSecurity, then select Modify. Add a value of 1.
  4. Open the Services Console and stop the MSDTC service.
  5. Right-click the service and select Properties on the popup menu.
  6. When the Properties dialog opens, click the Log On tab and change the identity to NT Authority\NetworkService (the password is blank).
  7. Click Apply then click OK.
  8. Return to the Services management console and restart the MSDTC service.

If the problem persists, check the Application Log within the Event Viewer and look for errors (red icons). The Event Viewer is a great resource for finding out what is really going on.

One possible solution is to stop the service, uninstall and then reinstall MSDTC. To do this, follow these steps:

  1. Stop the MSDTC service.
  2. Open a command prompt (cmd).
  3. Type “msdtc -uninstall”.
  4. Type “msdtc -install”
  5. Close the command prompt.