Click here to Skip to main content
Click here to Skip to main content

The 'dynamic' keyword for higher productivity: Windows Firewall API example

, 23 May 2010
Rate this:
Please Sign up or sign in to vote.
This dynamic keyword can be put to good use when doing COM. And in turn, you could be more productive when you need to complete tasks regarding the Windows API (that expose COM functionality).

Introduction

The .NET 4.0 framework introduces a new keyword, dynamic. On MSDN, the dynamic keyword is described as follows:

The type is a static type, but an object of type dynamic bypasses static type checking. In most cases, it functions like it has a type object. At compile time, an element that is typed as dynamic is assumed to support any operation. Therefore, you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from Reflection, or from somewhere else in the program. However, if the code is not valid, errors are caught at run time.

Scott has a great post on the dynamic keyword, with a few good examples: http://www.hanselman.com/blog/ C4AndTheDynamicKeywordWhirlwindTourAroundNET4AndVisualStudio2010Beta1.aspx.

In short, you can write anything you want for dynamic objects and your application will always compile. What you wrote will be evaluated at run time.

How it was

For one of my projects, I was required to add exceptions in the Windows Firewall.

MSDN did provide me with some examples of how to do this: http://msdn.microsoft.com/en-us/library/aa366415(v=VS.85).aspx. The downside: these examples only cover C/C++/VBScript.

VBScript

Ah yes, VBScript. Look at this script to handle the Windows Firewall API:

' Create the firewall manager object.
Dim fwMgr
Set fwMgr = CreateObject("HNetCfg.FwMgr")
 
' Get the current profile for the local firewall policy.
Dim profile
Set profile = fwMgr.LocalPolicy.CurrentProfile

That's it, easy. No references, no mapping with COM objects... You just look at the MSDN reference and you write the code. That's being productive.

But I don't say that this is perfect. The downside of this is that you are missing the strength of the compiler. If you would make an error and write LocalLopicy instead of LocalPolicy, well, you'd only find out when running that line of code.

C/C++

HRESULT hr = S_OK;
INetFwMgr* fwMgr = NULL;
INetFwPolicy* fwPolicy = NULL;

_ASSERT(fwProfile != NULL);

*fwProfile = NULL;

// Create an instance of the firewall settings manager.
hr = CoCreateInstance(
        __uuidof(NetFwMgr),
        NULL,
        CLSCTX_INPROC_SERVER,
        __uuidof(INetFwMgr),
        (void**)&fwMgr
        );
if (FAILED(hr))
{
    printf("CoCreateInstance failed: 0x%08lx\n", hr);
    goto error;
}

// Retrieve the local firewall policy.
hr = fwMgr->get_LocalPolicy(&fwPolicy);
if (FAILED(hr))
{
    printf("get_LocalPolicy failed: 0x%08lx\n", hr);
    goto error;
}

// Retrieve the firewall profile currently in effect.
hr = fwPolicy->get_CurrentProfile(fwProfile);
if (FAILED(hr))
{
    printf("get_CurrentProfile failed: 0x%08lx\n", hr);
    goto error;
}

Having to write that much code is not productive... enough said.

C# with ComImport

Jon Cole wrote a nice article on using ComImport in C# to talk to the Windows Firewall API:

[ComImport, ComVisible(false), Guid("F7898AF5-CAC4-4632-A2EC-DA06E5111AF2"), 
System.Runtime.InteropServices.InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface INetFwMgr
{
     INetFwPolicy LocalPolicy { get; }
     FirewallProfileType CurrentProfileType { get; }
  
     void RestoreDefaults();
     void IsPortAllowed(string imageFileName, IPVersion ipVersion, 
          long portNumber, string localAddress, IPProtocol ipProtocol, 
          [Out] out bool allowed, [Out] out bool restricted);
     void IsIcmpTypeAllowed(IPVersion ipVersion, string localAddress, 
          byte type, [Out] out bool allowed, [Out] out bool restricted);
}

For me, this is a semi-clean solution. But it involves a lot of trial and error, and also research... And it's a lot of code too...

George Mamaladze also implemented a few functionalities using FirewallAPI.dll.

How it is since .NET 4.0

The dynamic keyword can be put to good use when doing COM. And in turn, you could be more productive when you need to complete tasks regarding the Windows API (that expose COM functionality). Now I can access COM objects as if I were working in VBScript:

Type fwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr");
dynamic fwMgr = Activator.CreateInstance(fwMgrType);

bool isFwEnabled = fwMgr.LocalPolicy.CurrentProfile.FirewallEnabled;

How it works:

  • Get the type of the COM object you want to initialize. In this case, I decided to use the ProgID (unique 'name'). Why? Because it was given to me in the MSDN example: http://msdn.microsoft.com/en-us/library/aa366423(v=VS.85).aspx
  • Create an instance of this type using Activator, and put it in a 'dynamic object'.
  • And after that, I can write whatever code I think is right...

This gives you the possibility to just take existing VBScript code, modify it a bit, and you'll have compliable .NET code. All those things that were easy to do in VBScript are now easy to do in .NET

And with the modern unit testing tools, I think the missing compiler support isn't such a big issue after all.

Windows Firewall API using 'dynamic'

And now for our example. I've written some classes that implement a few interesting parts of the Windows Firewall API. To be honest, I only tested this on my local machine (Windows Server 2008 R2), so please let me know if you encounter any issues while using the code.

I've done three things:

  • Map a few enums
  • Map a few objects
  • Created two 'worker classes'

Mapping the enums

/// <summary>
/// Msdn reference: <a href="http://msdn.microsoft.com/en-us/library/aa366282(v=VS.85).aspx">http://msdn.microsoft.com/en-us/library/aa366282(v=VS.85).aspx</a>
/// </summary>
public enum NetFwAction
{
    Block = 0,
    Allow = 1,
    Max = 2
}

Nothing fancy about this. This is just a mapping with the information found on MSDN. Current enums:

  • NetFwAction
  • NetFwIpProtocol
  • NetFwIpVersion
  • NetFwProfile
  • NetFwRuleDirection
  • NetFwScope

Mapping the objects

I also needed the following objects: NetFwAuthorizedApplication and NetFwRule.

First I tried it like this:

/// <summary>
/// Msdn reference: <a href="http://msdn.microsoft.com/en-us/library/aa365100(v=VS.85).aspx%20///">http://msdn.microsoft.com/en-us/library/aa365100(v=VS.85).aspx
///</a> </summary>
public class NetFwAuthorizedApplication
{
    ...
   
    /// <summary>
    /// Initialize the application object.
    /// </summary>
    /// <param name="comObj"></param>
    internal NetFwAuthorizedApplication(dynamic comObj)
    {
        this.Name = comObj.Name;
        this.RemoteAddress = comObj.RemoteAddress;
        this.Enabled = comObj.Enabled;
        ...
    }
}

Again, this shows the power of the dynamic keyword. Just by looking at the reference (http://msdn.microsoft.com/en-us/library/aa365100(v=VS.85).aspx), I can access the properties without any extra code. If I want the RemoteAddress of the object, I just write comObj.RemoteAddress.

For me, this is a clean solution, but maybe too much work. If we have to do the mapping for each new object we want, this is not so productive. That's why I also wrote a base class that handles the mapping automatically. It uses Reflection so you might get a (small) performance hit doing it this way.

public class DynamicComMapper
{ 
     /// <summary>
     /// Map all the properties of the object to the properties of the COM object.
     /// </summary>
     /// <param name="comObj"></param>
     protected void Map(dynamic comObj)
     {
         foreach (PropertyInfo property in this.GetType().GetProperties())
         {
             try
             {
                 // Retrieve the value of a property of the COM object.
                 object comPropertyValue = typeof(Object).InvokeMember(
                   property.Name, BindingFlags.GetProperty, null, comObj, null);
    
                 // Set value in our managed object.
                 property.SetValue(this, comPropertyValue, null);
             }
             catch (COMException)
             {
                 // You could skip errors for unknown properties.
                 // if (ex.ErrorCode != -2147352570)
                 //    throw ex;
             }
         }
     }
}

This base class for "object mappings" tries to get the value from the COM object's property matching our current object's property. Meaning, all properties of my NetFwAuthorizedApplication object will be populated automatically.

Creating the worker classes

Since we're working with COM, we also have to think about cleaning up resources. That's why we'll use the DynamicComBase as the base class for our worker classes. This class implements IDisposable for cleaning up things automatically (since the dynamic object comObj is included in this base class).

It also provides functionality to clean up temporary COM objects that will be used in our "mapped objects".

/// <summary>
/// Release a COM object so that it can be cleaned up.
/// </summary>
/// <param name="comObj"></param>
protected void ReleaseCom(dynamic comObj)
{
    Marshal.ReleaseComObject(comObj);
}
  
/// <summary>
/// Clean up all released COM objects.
/// </summary>
protected void CleanAllReleased()
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

Now that we have a base class, we can create the NetFwMgr:

public class NetFwMgr : DynamicComBase
{
    /// <summary>
    /// Default constructor.
    /// </summary>
    public NetFwMgr()
    { 
        // Initialize the COM object.
        Type fwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr");
        base.comObj = Activator.CreateInstance(fwMgrType);
    }
  
    /// <summary>
    /// Set the port in the firewall.
    /// </summary>
    /// <param name="profile"></param>
    /// <param name="name"></param>
    /// <param name="protocol"></param>
    /// <param name="port"></param>
    /// <param name="scope"></param>
    /// <param name="enabled"></param>
    public void SetPort(NetFwProfile profile, string name, NetFwIpProtocol protocol, 
                        int port, NetFwScope scope, bool enabled)
    {
         if (profile == NetFwProfile.All)
             throw new ArgumentException("The profile 'All' is not allowed here.");
  
         // Create a port object.
         Type fwPortType = Type.GetTypeFromProgID("HNetCfg.FwOpenPort");
         dynamic fwPort = Activator.CreateInstance(fwPortType);
  
         // Configure the port.
         fwPort.Name = name;
         fwPort.Protocol = (int)protocol;
         fwPort.Port = port;
         fwPort.Scope = (int)scope;
         fwPort.Enabled = enabled;
  
         // Add to profile.
         comObj.LocalPolicy.GetProfileByType((int)profile).GloballyOpenPorts.Add(fwPort);
    }
  
    /// <summary>
    /// Verify if the firewall is enabled.
    /// Note, this will fail if the service is stopped.
    /// </summary>
    public bool IsEnabled
    { 
         get
         {
             return comObj.LocalPolicy.CurrentProfile.FirewallEnabled;
         }
    }
  
    /// <summary>
    /// Get a list of authorized applications.
    /// </summary>
    public List<NetFwAuthorizedApplication> GetAuthorizedApplications()
    {
         // Create a new list.
         List<NetFwAuthorizedApplication> applications = 
             new List<NetFwAuthorizedApplication>();
  
         // Get all applications and create typed objects.
         foreach (dynamic application in 
            comObj.LocalPolicy.CurrentProfile.AuthorizedApplications)
         {
             applications.Add(new NetFwAuthorizedApplication(application));
  
             // Clean current COM object.
             ReleaseCom(application);
         }
  
         // Clean up all released COM objects.
         CleanAllReleased();
  
         // Done.
         return applications;
    }
}

How does this class work?

  • First, we get the type of the object we want to create, based on the ProgID
  • We create an instance of this type using the Activator and put it in a dynamic object (residing in the base class)
  • Next, we use our mapped objects (e.g.: NetFwAuthorizedApplication) and the dynamic method calls (e.g.: comObj.LocalPolicy.CurrentProfile.AuthorizedApplications)
  • Clean up temporary objects using ReleaseCom and CleanAllReleased

The project also includes a test application that uses the classes, and this is the result:

demoappwinfirewall.png

What's next?

Currently, the classes I've written allow you to:

  • Enable/Disable a port on the firewall
  • Verify if the firewall is active for the current profile
  • Get a list of authorized applications for the current profile
  • Get a list of all rules in the firewall

But actually, I've done nothing more than just modify the existing VBScript examples available on MSDN: http://msdn.microsoft.com/en-us/library/aa366415(v=VS.85).aspx. Using the base classes, you can easily add your own code to handle other common tasks, and you can start by looking at the VBScript code.

On the other hand, you can also use these base classes to 'port' other VBScripts to .NET. Just take a look here: http://gallery.technet.microsoft.com/ScriptCenter/en-us and you'll be able to access things like backups, Windows update, ... in no time.

The only thing not to forget is writing unit tests! Since we don't have help from the compiler for tracing errors in dynamic objects, our unit tests are the only line of defense...

I hope this article showed you a few of the strengths of the new dynamic keyword and you'll start using it too..

Enjoy!

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Sandrino Di Mattia
Technical Lead RealDolmen
Belgium Belgium
I'm a Technical Consultant at RealDolmen, one of the largest players on the Belgian IT market: http://www.realdolmen.com
 
All posts also appear on my blogs: http://blog.sandrinodimattia.net and http://blog.fabriccontroller.net
Follow on   Twitter

Comments and Discussions

 
QuestionBrilliant! Pinmemberthund3rstruck14-Jun-12 15:56 
GeneralThanks! Pinmemberjakub8086-Jun-10 11:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 24 May 2010
Article Copyright 2010 by Sandrino Di Mattia
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid