Community Coding Contest 2010 - Looking for Input and Prizes

4. January 2010

Now that it's 2010, and over a year since the first Community Coding Contest came to an end, I'm thinking about running the contest again in 2010.

Last time it ran for 3 months and we had 6 really great entries. This time around I'm thinking that it may be better to accept entries for 6 months, and have the voting run for a full month. This will give much more time for entries to be submitted, and for people to work on their entries before submitting and/or voting begins.

These are just some initial thoughts, and I'm going to need input on everything from YOU (the community) before I can make a final decision as to when and how to run the contest again. I'd really appreciate if you could fill out the following survey and tell us what you think.

Community Coding Contest 2010 Pre-Contest Survey

Also, we will need some prizes donated before the contest will be able to go on. If you or your company are interested in donating prizes or monetary support (web hosting fees, mailing expenses, etc.) for the contest, please contact the contest directly here: http://communitycodingcontest.org/contact.aspx

You can view the official contest website here: http://communitycodingcontest.org

Thanks, and I look forward to hearing more about what everyone thinks!

asp.net, ASP.NET MVC, Bing Maps, C#, General, Silverlight, vb.net , , , ,

Prototype of OpenStreetMap Silverlight Control using DeepEarth and Bing Maps SDK

13. November 2009

I’ve decided to expand a little on using OpenStreetMap imagery with the new Bing Maps Silverlight Control in response to the following comment posted by John O’Brien on my previous “Display OpenStreetMap Imagery using Bing Maps Silverlight Control v1” post:

“Very close Chris but you will still need to enter a Bing Maps AppID.
If however you create your own map from MapCore and don't use the Bing Maps services then you don't need creditials”

Yes, it is true that by just displaying the OpenStreetMap imagery on the Bing Maps Silverlight Control using a custom TileSource you still need to provide the control a Bing Maps Key (App ID). However, what if you inherited from the “MapCore” base class (the same one that the Bing Maps “Map” object inherits) and built out a full OpenStreetMap Map control?

Custom “OpenStreetMap” Control

I built out a test “OpenStreetMap” object that inherits from “MapCore” that automatically sets the Map Mode to a “OpenStreetMapMode” object that loads up the OpenStreetMap imagery automatically. This was some simple code to write, basically just extending only a little bit on top of what I posted in the previous post.

Here’s the code for the “OpenStreetMap”, “OpenStreetMapMode” and “OpenStreetMapTileSource” objects:

using System;
using Microsoft.Maps.MapControl;
using Microsoft.Maps.MapControl.Core;

namespace SilverlightApplication1
{
    public class OpenStreetMap : MapCore
    {
        public OpenStreetMap()
            : base()
        {
            this.Mode.SetView(new Location(), 2.0, 0.0, 0.0, false);
            this.Mode = new OpenStreetMapMode();
        }
    }

    public class OpenStreetMapMode : RoadMode
    {
        public OpenStreetMapMode()
            : base()
        {
            var tileLayer = (MapTileLayer)this.Content;
            tileLayer.TileSources.Clear();
            tileLayer.TileSources.Add(new OpenStreetMapTileSource());
        }
    }

    public class OpenStreetMapTileSource : TileSource
    {
        public OpenStreetMapTileSource()
            : base("http://tile.openstreetmap.org/{2}/{0}/{1}.png")
        {
        }

        public override System.Uri GetUri(int x, int y, int zoomLevel)
        {
            return new Uri(string.Format(this.UriFormat, x, y, zoomLevel));
        }
    }
}

 

And, here’s an example of using this new “OpenStreetMap” control:

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:map="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    xmlns:local="clr-namespace:SilverlightApplication1"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <local:OpenStreetMap Name="map">
            <local:OpenStreetMap.Children>
                <map:MapLayer>
                    <Image Source="BluePin.png" Opacity="0.8" Stretch="None" map:MapLayer.Position="42.16, -95"></Image>
                </map:MapLayer>
            </local:OpenStreetMap.Children>
        </local:OpenStreetMap>
    </Grid>
</UserControl>

 

What About Navigation Controls?

One side effect of creating this completely custom Map Control based off MapCore is that the other controls that the “Map” control includes automatically do not get displayed.

The “Map” control displays the “MapControlNavigationBar” control via the “MapForeground” object, however due to the MapForeground constructor that lets you tell it what MapBase object to attach to being marked “internal” is seems that it can’t be easily reused.

However, with a little help from the newest controls in developments within the DeepEarth project, you can fairly simply modify the Bing Maps Navigation control within the DeepEarth project to be used with the new “OpenStreetMap” control.

Here’s the code I worked up for a DeepEarth NavigationPanel for the “OpenStreetMap” control created earlier:

using System;
using System.Windows;
using DeepEarth.Client.BingMaps.Convertors;
using DeepEarth.Client.Common;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using Microsoft.Maps.MapControl;
using Point = System.Windows.Point;
using Microsoft.Maps.MapControl.Core;

namespace NewDeepEarth.NavigationPanel
{
    public class NavigationPanel : DeepEarth.Client.Controls.NavigationPanel.NavigationPanel, IMapControl<MapCore>
    {
        private string mapName;

        public NavigationPanel()
        {
            Loaded += CoordinatePanel_Loaded;
        }

        public override double ZoomLevel
        {
            get
            {
                if (MapInstance != null)
                {
                    return MapInstance.ZoomLevel;
                }
                return 0;
            }
            set
            {
                if (MapInstance != null)
                {
                    MapInstance.ZoomLevel = value;
                }
            }
        }

        public override ICoordinate Center
        {
            get
            {
                if (MapInstance != null)
                {
                    return CoordinateConvertor.Convert(MapInstance.Center);
                }
                return new Coordinate(0, 0);
            }
            set
            {
                if (MapInstance != null)
                {
                    MapInstance.Center = CoordinateConvertor.ConvertBack(value);
                }
            }
        }

        #region IMapControl<MapCore> Members

        public string MapName
        {
            get { return mapName; }
            set
            {
                mapName = value;
                setMapInstance(MapName);
            }
        }

        public MapCore MapInstance { get; set; }

        public new void Dispose()
        {
            MapInstance = null;
            base.Dispose();
        }

        public override void PanMap(int deltaX, int deltaY)
        {
            if (MapInstance != null)
            {
                NewDeepEarth.Client.BingMaps.Utilities.Pan(deltaX, deltaY, MapInstance);
            }
        }

        #endregion

        private void CoordinatePanel_Loaded(object sender, RoutedEventArgs e)
        {
            setMapInstance(MapName);
        }

        private void setMapInstance(string mapname)
        {
            MapInstance = Utilities.FindVisualChildByName<MapCore>(Application.Current.RootVisual, mapname);
        }

    }
}

Also, hers a small utility method that I needed to modify slightly within the DeepEarth project to get the above NavigationPanel to work:

using Microsoft.Maps.MapControl.Core;
using Point = System.Windows.Point;

namespace NewDeepEarth.Client.BingMaps
{
    public static class Utilities
    {
        // Convert to accept Bing Maps "MapCore" instead of "Map" object
        public static void Pan(double deltaX, double deltaY, MapCore map) //Map map)
        {
            Point center = map.LocationToViewportPoint(map.Center);
            center.X = center.X + deltaX;
            center.Y = center.Y + deltaY;
            map.Center = map.ViewportPointToLocation(center);
        }
    }
}

 

Suggestion for the DeepEarth Preview Controls

One big suggestion I have for the DeepEarth projects new Preview Controls is to make the Bing Maps objects/libraries within the project use/accept “MapCore” or “MapBase” where ever it can instead of “Map”. This way as much of the DeepEarth code can be reused, without modification, when building a completely custom Map Control using the Bing Maps Silverlight SDK.

A perfect example of this are the modifications I needed to make to the NavigationPanel object and Utilities.Pan method posted above. By simply changing them from referencing/using “Map” to “MapCore” instead, they can not be used with any Map Control built using the Bing Maps Silverlight SDK.

Conclusion

Even though the above code works, it’s really just a prototype of what can be done using the current Preview controls within the DeepEarth project along with the new Bing Maps Silverlight Control. There is definitely some cool stuff to be done with both the Bing Maps Silverlight SDK and the DeepEarth project!

Anyway, here’s a download link to the full code of the project for the above code:

C#, Bing Maps, Silverlight , , , ,

Display OpenStreetMap Imagery using Bing Maps Silverlight Control v1

12. November 2009

BingMapsSilverlight_OpenStreetMap The Bing Maps Silverlight SDK documentation on MSDN contains an article on Adding Tile Overlays to the Map, that demonstrates how to overlay your own map imagery over top of the Bing Maps Imagery. However, what if you want to completely replace the Bing Maps Imagery with some other Imagery like the OpenStreeMap Imagery?

In the “Adding Tile Overlays to the Map” article it shows using a “LocationRectTileSource” to add the custom map imagery overlay. To implement the OpenStreetMap imagery you will need to create a simple class that inherits from “TileSource” since you wont be restricting the imagery to only a small portion of the map, but instead will be showing all the OpenStreetMap Imagery in place of the Bing Maps Imagery.

First, Hide the Bing Maps Imagery

Since we will be displaying the OpenStreetMap imagery instead of the Bing Maps Imagery, we want to prevent the Map control from loading/displaying the Bing Maps Imagery completely.

To do this, all you need to do is set the Maps Mode to an instance of the “MercatorMode” object:

<UserControl x:Class="BingMapsSilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    xmlns:mCore="clr-namespace:Microsoft.Maps.MapControl.Core;assembly=Microsoft.Maps.MapControl">
    <Grid x:Name="LayoutRoot">
        <m:Map>
            <m:Map.Mode>
                <!-- Do Not Display Bing Maps Imagery -->
                <mCore:MercatorMode></mCore:MercatorMode>
            </m:Map.Mode>
        </m:Map>
    </Grid>
</UserControl>

 

Display OpenStreetMap Imagery

Next, we will create a simple “OpenStreetMapTileSource” class that inherits from “TileSource” that will be used to specify the location (URI) of the OpenStreetMap Imagery so the Map control can load and display it.

Here’s the simple OpenStreetMapTileSource class:

public class OpenStreetMapTileSource : Microsoft.Maps.MapControl.TileSource
{
    public OpenStreetMapTileSource()
        : base("http://tile.openstreetmap.org/{2}/{0}/{1}.png")
    {
    }

    public override System.Uri GetUri(int x, int y, int zoomLevel)
    {
        return new Uri(string.Format(this.UriFormat, x, y, zoomLevel));
    }
}

 

Now to put the OpenStreetMapTIleSource in place and actually display the OpenStreetMap Imagery. To do this, we will add a new MapTileLayer to the Maps Children collection, and add an instance of our OpenStreetMapTileSource object to the MapTileLayer objects TileSources collection.

Here’s the code to do this:

<UserControl x:Class="BingMapsSilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    xmlns:mCore="clr-namespace:Microsoft.Maps.MapControl.Core;assembly=Microsoft.Maps.MapControl"
    xmlns:local="clr-namespace:BingMapsSilverlightApplication1">
    <Grid x:Name="LayoutRoot">
        <m:Map>
            <m:Map.Mode>
                <!-- Do Not Display Bing Maps Imagery -->
                <mCore:MercatorMode></mCore:MercatorMode>
            </m:Map.Mode>
            <m:Map.Children>
                <m:MapTileLayer>
                    <m:MapTileLayer.TileSources>
                        <!-- Display OpenStreetMap Imagery -->
                        <local:OpenStreetMapTileSource></local:OpenStreetMapTileSource>
                    </m:MapTileLayer.TileSources>
                </m:MapTileLayer>
            </m:Map.Children>
        </m:Map>
    </Grid>
</UserControl>

 

Conclusion

It’s really pretty simple to display your own custom map tile image sets and even completely replace the Bing Maps Imagery with the new control. If your interested in seeing how to display Yahoo Maps imagery within the control, then you’ll want to refer to my previous article on doing this with the CTP version of the Bing Maps Silverlight Control.

C#, Bing Maps, Silverlight , , ,

Getting Started with Bing Maps Silverlight Control v1.0

12. November 2009

BingMapsRTW_BasicMapIt’s been 8 months since the CTP release of the Bing Maps Silverlight Control at MIX’08, and finally this week Microsoft released the Final v1.0 release of the control to the web.  Now, we can all finally start using the Bing Maps Silverlight Control in Production!

If you’ve worked with the CTP, then you’ll want to take a look at Ricky’s “Migrating from Bing Maps Silverlight CTP to Production Version” article. It explains all the stuff you’ll need to modify within your project in order to gracefully switch from the CTP to the v1 Map Control.

Now for those of you who haven’t worked with the CTP…

Where to Start?

First you’ll need to make sure you have the following Tools installed:

Of course you’ll also need to at least be familiar with the basics of Silverlight development. I’m mostly referring to XAML, so if your familiar with WPF that should be enough to get you started.

For those Bing Maps (formerly Virtual Earth) developers new to Silverlight; If you need a little help getting started learning the basics, here are some resources that will help you get started:

Now that you have all the necessary tools installed, you can go ahead and download and install the Bing Maps Silverlight v1 Control.

Download: Bing Maps Silverlight Control SDK Version 1.0

The installer will install the Bing Maps Silverlight Control within the “Program Files” on your computer, along with an SDK Documentation Help (.chm) file.

If you would like to see the Bing Maps Silverligth Control “In Action” and see the source code that powers each example, then you’ll want to check out the Bing Maps Silverlight Control Interactive SDK.

Display a Basic Map

Create a New Silverlight Application Project
  • Run VIsual Studio 2008 and Create a New Project using the Silverlight Application template.
    BingMapsRTW_CreateSilverlightProject
  • In the “Add Silverlight Application” dialog box, select “Host the Silverlight application in a new Web site” option and click “OK”. You must select this option to create an ASP.NET Web Application Project, ASP.NET Website, or ASP.NET MVC Website because the URL Access Restrictions in Silverlight require the page that hosts the Map control be hosted using HTTP in order to load/access the Map TIle Imagery.
    BingMapsRTW_CreateHostWebsite 
Add a Reference to Microsoft.Maps.MapControl.dll
  • Go to the “Solution Explorer” and RIght-Click “ References” in the Silverlight Project (not the ASP.NET Web Application Project), and Select “Add Reference…
    BingMapsRTW_AddReference 
  • Within the “Add Reference“ Dialog, Select the “Browse“ tab and navigate to the folder where the SDK is installed, then select the “Microsoft.Maps.MapControl.dll” and “Microsoft.Maps.MapControl.Common.dll” files.

    Note: Be Default the SDK is installed in this folder on x64 version of Windows: “C:\Program Files (x86)\Bing Maps Silverlight Control\V1\Libraries”. On x86 (or 32-bit) versions of Windows it’s installed within the “Program Files” folder instead of “Program Files (x86)”.

    BingMapsRTW_AddReferenceBrowse 
Display a Bing Maps Map!
  • Add a Namespace declaration to Page.xaml for the Microsoft Maps.MapControl namespace.
    To do this add the following to the <UserControl> tag:
    xmlns:m=”clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl”
  • Add the Map Control to the Page by adding the following tag within the Grid in the Page:
    <m:Map></m:Map> The resulting Page.xaml will look like this:
    <UserControl x:Class="BingMapsSilverlightApplication1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
        mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
        <Grid x:Name="LayoutRoot">
            <m:Map></m:Map>
        </Grid>
    </UserControl>
    Now, when you run the application (press F5 within Visual Studio) you will see a fully interactive Bing Maps map displayed.
    BingMapsRTW_BasicMap

BingMapsRTW_BasicMapWithDevAccountSignUpWarningSetting Up a Bing Maps Developer Account

When running the above example you may have noticed the “Invalid Credentials. Sign up for a developer account at: http://www.microsoft.com/maps/developers” message being displayed over top of the Map. This is because in order for you to use the Silverlight Map control within an application, Microsoft is requiring you to setup a Developer Account for the domain you will be hosting your application in.

Setup a Bing Maps Developer Account
  • Go to the Bing Maps Account Center at https://www.bingmapsportal.com
  • Click the “Create an Account” link on the left side of the site.
  • Sign in with your Windows Live ID
  • Enter it the required Account Details
Get a Bing Maps Key
BingMapsRTW_CreateBingMapsKey
  • Once you have created a Bing Maps Developer Account, just click the “Create or view keys” link on the left side of the Bing Maps Account Center website.
  • Type in some Application Name. This is your name for the application.
  • Type if the Application URL. This is the domain name that you will be hosting the application at.
  • Click the “Create key” button. Now you have a Bing Maps Key that you can use within your application to get rid of that pesky little warning message displayed over the top of the Map and everything on it.
    Note: For testing purposes I entered in an Application Name of “Test” and Application URL of “http://localhost”. This way I have a Bing Maps Key to use for testing/playing with the Silverlight Map Control that wont track any usage to the application I’ll eventually build.
Use the Bing Maps Key within Your Application

Now that you have a Bing Maps Key for your application, you can set your Map within that application to use this key.

To do so, just set the Map controls “CredentialsProvider” property value within XAML to the Bing Maps Key. Just copy and paste the key from the Bing Maps Account Center into your XAML code.

<UserControl x:Class="BingMapsSilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <m:Map CredentialsProvider="[Bing Maps Key Here]"></m:Map>
    </Grid>
</UserControl>

 

Set Map Properties Declaratively Using XAML and Programmatically Using Code

You can change/set the Bing Maps Controls properties Declaratively using XAML, and Programmatically using Code.

Below is an example of setting the Map Mode to Aerial using XAML:

<UserControl x:Class="BingMapsSilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <m:Map Mode="Aerial"></m:Map>
    </Grid>
</UserControl>

 

Below is an example of setting the Map Mode to Aerial using C# code:

using System.Windows.Controls;
using Microsoft.Maps.MapControl;

namespace BingMapsSilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            Map1.Mode = new AerialMode();
        }
    }
}

 

First, in order to access the Map Control programmatically you’ll need to assign its “Name”  property within XAML so you have a name to reference it by; like the following:

<UserControl x:Class="BingMapsSilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot">
        <m:Map Name="Map1"></m:Map>
    </Grid>
</UserControl>

 

Additional Articles

Here are some links to additional articles on using the Bing Maps Silverlight Version 1.0 Control:

Conclusion

It’s pretty exciting that we now have a Bing Maps Silverlight control to use within our applications, and finally there is an option other than the JavaScript Control that has far superior performance when plotting over just a couple hundred Pushpins. Plus the Silverlight controls bring the full power of Silverlight to Bing Maps development; which allows for much richer mapping applications to be built.

Keep an eye out, I’ll be posting more articles/tutorials to help you out in exploring this shiny, new Bing Maps Silverlight Control.

Bing Maps, C#, Silverlight , , ,

Virtual Router Project – Turn any Windows 7 PC into a Wireless Access Point / Hot Spot

9. November 2009

Over the last week some of you may have heard about Connectify. It’s an app that unleashes the “Virtual WiFi” and Wireless Hosted Network features of Windows 7 to turn a PC into a Wireless Access Point or Hot Spot. Well, I looked into what it would take to build such an app, and it really wasn’t that difficult since Windows 7 has all the API’s built in to do it. After some time of looking things up and referencing the “Wireless Hosted Network” C++ sample within the WIndows 7 SDK, I now have a nice working version of the application to release. I’m calling this project “Virtual Router” since it essentially allows you to host a software based wireless router from your laptop or other PC with a Wifi card. Oh, and did I mention that this is FREE and OPEN SOURCE!

Download “Virtual Router”

Virtual Router Project - Turn Windows 7 into a Wifi Hot Spot

C# , ,

Resolve IP Address And Host Name From MAC Address using C# and Windows ARP Utility

8. November 2009

While working on the Virtual Router project, I’ve come across a need to be able to retrieve the IP Address and Host Name of a given machine on the local network when only the machines MAC Address is known. This took a bit of research to figure out, and eventually I stumbled across the “arp.exe” utility within Windows.

“arp.exe” uses the Address Resolution Protocol to provide functionality to add, delete or display the IP address for MAC (Media Access Control) address translation.

To see the IP / MAC Address translations, just open up the Command Prompt in Windows, and type “arp –a” and press enter.

Now, you may be asking, how do we use this utility from within a .NET application? Well, it’s rather simple. All you need to do is use the System.Diagnostics.Process class to execute the “arp –a” call and retrieve the results. Then just parse through the results and get out the IP Address for the desired MAC Address.

I’m not going to give full detail on how to call “arp” from .NET. Below is an example on using the “IPInfo” class I created to perform the previously mentioned method of retrieving the IP Address for a specified MAC Address. I also added in the ability to get the machines Host Name using the the “Dns.GetHostEntry” method.

var ipinfo = IPInfo.GetIPInfo(txtMAC.Text);

if (ipinfo == null)
{
    // Machine Not Found!
}
else
{
    var ip = ipinfo.IPAddress;
    var hostname = ipinfo.HostName;
}


Below is the full code of my “IPInfo” class:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;

/// <summary>
/// This class allows you to retrieve the IP Address and Host Name for a specific machine on the local network when you only know it's MAC Address.
/// </summary>
public class IPInfo
{
    public IPInfo(string macAddress, string ipAddress)
    {
        this.MacAddress = macAddress;
        this.IPAddress = ipAddress;
    }

    public string MacAddress { get; private set; }
    public string IPAddress { get; private set; }

    private string _HostName = string.Empty;
    public string HostName
    {
        get
        {
            if (string.IsNullOrEmpty(this._HostName))
            {
                try
                {
                    // Retrieve the "Host Name" for this IP Address. This is the "Name" of the machine.
                    this._HostName = Dns.GetHostEntry(this.IPAddress).HostName;
                }
                catch
                {
                    this._HostName = string.Empty;
                }
            }
            return this._HostName;
        }
    }


    #region "Static Methods"

    /// <summary>
    /// Retrieves the IPInfo for the machine on the local network with the specified MAC Address.
    /// </summary>
    /// <param name="macAddress">The MAC Address of the IPInfo to retrieve.</param>
    /// <returns></returns>
    public static IPInfo GetIPInfo(string macAddress)
    {
        var ipinfo = (from ip in IPInfo.GetIPInfo()
                  where ip.MacAddress.ToLowerInvariant() == macAddress.ToLowerInvariant()
                  select ip).FirstOrDefault();

        return ipinfo;
    }

    /// <summary>
    /// Retrieves the IPInfo for All machines on the local network.
    /// </summary>
    /// <returns></returns>
    public static List<IPInfo> GetIPInfo()
    {
        try
        {
            var list = new List<IPInfo>();

            foreach (var arp in GetARPResult().Split(new char[] { '\n', '\r' }))
            {
                // Parse out all the MAC / IP Address combinations
                if (!string.IsNullOrEmpty(arp))
                {
                    var pieces = (from piece in arp.Split(new char[] { ' ', '\t' })
                                  where !string.IsNullOrEmpty(piece)
                                  select piece).ToArray();
                    if (pieces.Length == 3)
                    {
                        list.Add(new IPInfo(pieces[1], pieces[0]));
                    }
                }
            }

            // Return list of IPInfo objects containing MAC / IP Address combinations
            return list;
        }
        catch(Exception ex)
        {
            throw new Exception("IPInfo: Error Parsing 'arp -a' results", ex);
        }
    }

    /// <summary>
    /// This runs the "arp" utility in Windows to retrieve all the MAC / IP Address entries.
    /// </summary>
    /// <returns></returns>
    private static string GetARPResult()
    {
        Process p = null;
        string output = string.Empty;

        try
        {
            p = Process.Start(new ProcessStartInfo("arp", "-a")
            {
                CreateNoWindow = true,
                UseShellExecute = false,
                RedirectStandardOutput = true
            });

            output = p.StandardOutput.ReadToEnd();

            p.Close();
        }
        catch(Exception ex)
        {
            throw new Exception("IPInfo: Error Retrieving 'arp -a' Results", ex);
        }
        finally
        {
            if (p != null)
            {
                p.Close();
            }
        }
        
        return output;
    }

    #endregion
}

C# , ,

.NET Windows Service Installer and Auto Start After Installation

6. November 2009

It’s not very often that I need to build a Windows Service for a specific task, and it’s even more rare that I need to create an Installer (.msi) to install/uninstall the service. Well, the project I’m currently working on (http://virtualrouter.codeplex.com) requires me to 1) Install a Windows Service using a Setup Project, and 2) Start the Windows Service immediately after installation.

Create Setup Project for Windows Service

Just create a Setup Project within your Solution in Visual Studio, then follow the below steps:

1) Right-Click the Setup Project you just created
2) Click “Add”, then “Project Output…”
3) In the dialog that appears select your Windows Service Project as Primary Output, then click OK.

That’s really all that’s required. When the resulting Installer is executed it will Install / Uninstall the Windows Service.

You can find a longer description of how to do this here:

http://support.microsoft.com/kb/317421

Auto Start Windows Service After Installation

Whether you use a Setup Project of the “installutil” tool to install your Windows Service, it can be very convenient if the service were to automatically start once installed. To do this all it takes is adding a couple lines of code to the ServiceInstaller that you have defined within your Windows Service Project to handle the “Committed” event, then use the ServiceController class to Start the service.

Here’s an example ServiceInstaller with the “Auto Start” code in place:

[RunInstaller(true)]
public class ServiceInstaller : Installer
{
    string strServiceName = "MyServiceName";

    public ServiceInstaller()
    {
        var processInstaller = new ServiceProcessInstaller();
        var serviceInstaller = new ServiceInstaller();

        processInstaller.Account = ServiceAccount.LocalSystem;
        processInstaller.Username = null;
        processInstaller.Password = null;

        serviceInstaller.DisplayName = strServiceName;
        serviceInstaller.StartType = ServiceStartMode.Automatic;

        serviceInstaller.ServiceName = strServiceName;

        this.Installers.Add(processInstaller);
        this.Installers.Add(serviceInstaller);

        this.Committed += new InstallEventHandler(ServiceInstaller_Committed);
    }

    void ServiceInstaller_Committed(object sender, InstallEventArgs e)
    {
        // Auto Start the Service Once Installation is Finished.
        var controller = new ServiceController(strServiceName);
        controller.Start();
    }
}

 

Conclusion

These are two tips that for some reason had eluded me for a long time. Usually I just created a .bat file to call “installutil” and “net start” to install and start my services manually. In most cases that worked perfect since it was very rare that the service was installed on a new computer or updated to a newer version. However, while building an “End User Friendly” Installer that anyone can run, you need to automate the installation and start up of the Windows Service.

This is just the thing that I’m running into with the new http://virtualrouter.codeplex.com project that I’m working on. The “Virtual Router” project utilizes Windows 7’s Virtual Wifi and Wireless Hosted Network API’s to turn any computer into a Wireless Access Point / Router. Look for the first release of this project soon!

C# , , ,

C#: Using IProgressDialog to show a “native” Progress Dialog from .NET in Windows

17. August 2009

CSharp-IProgressDialog-Native-dotNet-in-Windows A few months ago I posted some code that I originally wrote back in about 2004… Well, I was looking through some more of my prototypes that I’ve written and I came across the following example of how to use the IProgressDialog Win32 Interface to harness the power of the Built-in Progress Dialog in Windows within your own .NET applications.

I have test this on WIndows 7, but it should work as expected on Windows XP and Vista. The MSDN Documentation for IProgressDialog says it’s minimum supported operating systems are Windows 2000 and Windows XP.

The full source code for this example is listed below. Just copy from this post and paste it within your app to test.

“ProgressDialog” Usage Example

Here’s an example of how to use this example “ProgressDialog” class: This is an example of using it from Windows Forms, however it could be used from within WPF exactly the same way.

using System;
using System.Windows.Forms;

namespace WindowsProgressDialog
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private Pietschsoft.ProgressDialog pd;
        private uint progressPercent;

        private void button1_Click(object sender, EventArgs e)
        {
            pd = new Pietschsoft.ProgressDialog(this.Handle);
            pd.Title = "Performing Operation";
            pd.CancelMessage = "Please wait while the operation is cancelled";
            pd.Maximum = 100;
            pd.Value = 0;
            pd.Line1 = "Line One";
            pd.Line3 = "Calculating Time Remaining...";
            
            //pd.ShowDialog(); // Defaults to PROGDLG.Normal
            pd.ShowDialog(Pietschsoft.ProgressDialog.PROGDLG.Modal, Pietschsoft.ProgressDialog.PROGDLG.AutoTime, Pietschsoft.ProgressDialog.PROGDLG.NoMinimize);

            progressPercent = 0;
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            progressPercent++;

            if (pd.HasUserCancelled)
            {
                timer1.Stop();
                pd.CloseDialog();
            }
            else
            {
                // Update the progress value
                pd.Value = progressPercent;

                pd.Line2 = "Percent " + progressPercent.ToString() + "%";

                if (progressPercent >= 100)
                {
                    timer1.Stop();
                    pd.CloseDialog();
                }
            }
        }
    }
}

 

“ProgressDialog” Class

And, here’s the full code for the “ProgressDialog” class itself:

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace Pietschsoft
{
    public class ProgressDialog
    {
        private IntPtr _parentHandle;

        private Win32IProgressDialog pd = null;

        public ProgressDialog(IntPtr parentHandle)
        {
            this._parentHandle = parentHandle;
        }

        public void ShowDialog(params PROGDLG[] flags)
        {
            if (pd == null)
            {
                pd = (Win32IProgressDialog)new Win32ProgressDialog();
                
                pd.SetTitle(this._Title);
                pd.SetCancelMsg(this._CancelMessage, null);
                pd.SetLine(1, this._Line1, false, IntPtr.Zero);
                pd.SetLine(2, this._Line2, false, IntPtr.Zero);
                pd.SetLine(3, this._Line3, false, IntPtr.Zero);

                PROGDLG dialogFlags = PROGDLG.Normal;
                if (flags.Length != 0)
                {
                    dialogFlags = flags[0];
                    for (var i = 1; i < flags.Length; i++)
                    {
                        dialogFlags = dialogFlags | flags[i];
                    }
                }
                
                pd.StartProgressDialog(this._parentHandle, null, dialogFlags, IntPtr.Zero);
            }
        }

        public void CloseDialog()
        {
            if (pd != null)
            {
                pd.StopProgressDialog();
                //Marshal.ReleaseComObject(pd);
                pd = null;
            }
        }

        private string _Title = string.Empty;
        public string Title
        {
            get
            {
                return this._Title;
            }
            set
            {
                this._Title = value;
                if (pd != null)
                {
                    pd.SetTitle(this._Title);
                }
            }
        }

        private string _CancelMessage = string.Empty;
        public string CancelMessage
        {
            get
            {
                return this._CancelMessage;
            }
            set
            {
                this._CancelMessage = value;
                if (pd != null)
                {
                    pd.SetCancelMsg(this._CancelMessage, null);
                }
            }
        }

        private string _Line1 = string.Empty;
        public string Line1
        {
            get
            {
                return this._Line1;
            }
            set
            {
                this._Line1 = value;
                if (pd != null)
                {
                    pd.SetLine(1, this._Line1, false, IntPtr.Zero);
                }
            }
        }

        private string _Line2 = string.Empty;
        public string Line2
        {
            get
            {
                return this._Line2;
            }
            set
            {
                this._Line2 = value;
                if (pd != null)
                {
                    pd.SetLine(2, this._Line2, false, IntPtr.Zero);
                }
            }
        }

        private string _Line3 = string.Empty;
        public string Line3
        {
            get
            {
                return this._Line3;
            }
            set
            {
                this._Line3 = value;
                if (pd != null)
                {
                    pd.SetLine(3, this._Line3, false, IntPtr.Zero);
                }
            }
        }

        private uint _value = 0;
        public uint Value
        {
            get
            {
                return this._value;
            }
            set
            {
                this._value = value;
                if (pd != null)
                {
                    pd.SetProgress(this._value, this._maximum);
                }
            }
        }

        private uint _maximum = 100;
        public uint Maximum
        {
            get
            {
                return this._maximum;
            }
            set
            {
                this._maximum = value;
                if (pd != null)
                {
                    pd.SetProgress(this._value, this._maximum);
                }
            }
        }
        public bool HasUserCancelled
        {
            get
            {
                if (pd != null)
                {
                    return pd.HasUserCancelled();
                }
                else
                    return false;
            }
        }

        #region "Win32 Stuff"
        // The below was copied from: http://pinvoke.net/default.aspx/Interfaces/IProgressDialog.html

        public static class shlwapi
        {
            [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
            static extern bool PathCompactPath(IntPtr hDC, [In, Out] StringBuilder pszPath, int dx);
        }

        [ComImport]
        [Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface Win32IProgressDialog
        {
            /// <summary>
            /// Starts the progress dialog box.
            /// </summary>
            /// <param name="hwndParent">A handle to the dialog box's parent window.</param>
            /// <param name="punkEnableModless">Reserved. Set to null.</param>
            /// <param name="dwFlags">Flags that control the operation of the progress dialog box. </param>
            /// <param name="pvResevered">Reserved. Set to IntPtr.Zero</param>
            void StartProgressDialog(
                IntPtr hwndParent, //HWND
                [MarshalAs(UnmanagedType.IUnknown)]    object punkEnableModless, //IUnknown
                PROGDLG dwFlags,  //DWORD
                IntPtr pvResevered //LPCVOID
                );

            /// <summary>
            /// Stops the progress dialog box and removes it from the screen.
            /// </summary>
            void StopProgressDialog();

            /// <summary>
            /// Sets the title of the progress dialog box.
            /// </summary>
            /// <param name="pwzTitle">A pointer to a null-terminated Unicode string that contains the dialog box title.</param>
            void SetTitle(
                [MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
                );

            /// <summary>
            /// Specifies an Audio-Video Interleaved (AVI) clip that runs in the dialog box. Note: Note  This method is not supported in Windows Vista or later versions.
            /// </summary>
            /// <param name="hInstAnimation">An instance handle to the module from which the AVI resource should be loaded.</param>
            /// <param name="idAnimation">An AVI resource identifier. To create this value, use the MAKEINTRESOURCE macro. The control loads the AVI resource from the module specified by hInstAnimation.</param>
            void SetAnimation(
                IntPtr hInstAnimation, //HINSTANCE
                ushort idAnimation //UINT
                );

            /// <summary>
            /// Checks whether the user has canceled the operation.
            /// </summary>
            /// <returns>TRUE if the user has cancelled the operation; otherwise, FALSE.</returns>
            /// <remarks>
            /// The system does not send a message to the application when the user clicks the Cancel button.
            /// You must periodically use this function to poll the progress dialog box object to determine
            /// whether the operation has been canceled.
            /// </remarks>
            [PreserveSig]
            [return: MarshalAs(UnmanagedType.Bool)]
            bool HasUserCancelled();

            /// <summary>
            /// Updates the progress dialog box with the current state of the operation.
            /// </summary>
            /// <param name="dwCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
            /// <param name="dwTotal">An application-defined value that specifies what value dwCompleted will have when the operation is complete.</param>
            void SetProgress(
                uint dwCompleted, //DWORD
                uint dwTotal //DWORD
                );

            /// <summary>
            /// Updates the progress dialog box with the current state of the operation.
            /// </summary>
            /// <param name="ullCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
            /// <param name="ullTotal">An application-defined value that specifies what value ullCompleted will have when the operation is complete.</param>
            void SetProgress64(
                ulong ullCompleted, //ULONGLONG
                ulong ullTotal //ULONGLONG
                );

            /// <summary>
            /// Displays a message in the progress dialog.
            /// </summary>
            /// <param name="dwLineNum">The line number on which the text is to be displayed. Currently there are three lines—1, 2, and 3. If the PROGDLG_AUTOTIME flag was included in the dwFlags parameter when IProgressDialog::StartProgressDialog was called, only lines 1 and 2 can be used. The estimated time will be displayed on line 3.</param>
            /// <param name="pwzString">A null-terminated Unicode string that contains the text.</param>
            /// <param name="fCompactPath">TRUE to have path strings compacted if they are too large to fit on a line. The paths are compacted with PathCompactPath.</param>
            /// <param name="pvResevered"> Reserved. Set to IntPtr.Zero.</param>
            /// <remarks>This function is typically used to display a message such as "Item XXX is now being processed." typically, messages are displayed on lines 1 and 2, with line 3 reserved for the estimated time.</remarks>
            void SetLine(
                uint dwLineNum, //DWORD
                [MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
                [MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
                IntPtr pvResevered //LPCVOID
                );

            /// <summary>
            /// Sets a message to be displayed if the user cancels the operation.
            /// </summary>
            /// <param name="pwzCancelMsg">A pointer to a null-terminated Unicode string that contains the message to be displayed.</param>
            /// <param name="pvResevered">Reserved. Set to NULL.</param>
            /// <remarks>Even though the user clicks Cancel, the application cannot immediately call
            /// IProgressDialog::StopProgressDialog to close the dialog box. The application must wait until the
            /// next time it calls IProgressDialog::HasUserCancelled to discover that the user has canceled the
            /// operation. Since this delay might be significant, the progress dialog box provides the user with
            /// immediate feedback by clearing text lines 1 and 2 and displaying the cancel message on line 3.
            /// The message is intended to let the user know that the delay is normal and that the progress dialog
            /// box will be closed shortly.
            /// It is typically is set to something like "Please wait while ...". </remarks>
            void SetCancelMsg(
                [MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg, //LPCWSTR
                object pvResevered //LPCVOID
                );

            /// <summary>
            /// Resets the progress dialog box timer to zero.
            /// </summary>
            /// <param name="dwTimerAction">Flags that indicate the action to be taken by the timer.</param>
            /// <param name="pvResevered">Reserved. Set to NULL.</param>
            /// <remarks>
            /// The timer is used to estimate the remaining time. It is started when your application
            /// calls IProgressDialog::StartProgressDialog. Unless your application will start immediately,
            /// it should call Timer just before starting the operation.
            /// This practice ensures that the time estimates will be as accurate as possible. This method
            /// should not be called after the first call to IProgressDialog::SetProgress.</remarks>
            void Timer(
                PDTIMER dwTimerAction, //DWORD
                object pvResevered //LPCVOID
                );

        }

        [ComImport]
        [Guid("F8383852-FCD3-11d1-A6B9-006097DF5BD4")]
        public class Win32ProgressDialog
        {
        }

        /// <summary>
        /// Flags that indicate the action to be taken by the ProgressDialog.SetTime() method.
        /// </summary>
        public enum PDTIMER : uint //DWORD
        {
            /// <summary>Resets the timer to zero. Progress will be calculated from the time this method is called.</summary>
            Reset = (0x01),
            /// <summary>Progress has been suspended.</summary>
            Pause = (0x02),
            /// <summary>Progress has been resumed.</summary>
            Resume = (0x03)
        }

        [Flags]
        public enum PROGDLG : uint //DWORD
        {
            /// <summary>Normal progress dialog box behavior.</summary>
            Normal = 0x00000000,
            /// <summary>The progress dialog box will be modal to the window specified by hwndParent. By default, a progress dialog box is modeless.</summary>
            Modal = 0x00000001,
            /// <summary>Automatically estimate the remaining time and display the estimate on line 3. </summary>
            /// <remarks>If this flag is set, IProgressDialog::SetLine can be used only to display text on lines 1 and 2.</remarks>
            AutoTime = 0x00000002,
            /// <summary>Do not show the "time remaining" text.</summary>
            NoTime = 0x00000004,
            /// <summary>Do not display a minimize button on the dialog box's caption bar.</summary>
            NoMinimize = 0x00000008,
            /// <summary>Do not display a progress bar.</summary>
            /// <remarks>Typically, an application can quantitatively determine how much of the operation remains and periodically pass that value to IProgressDialog::SetProgress. The progress dialog box uses this information to update its progress bar. This flag is typically set when the calling application must wait for an operation to finish, but does not have any quantitative information it can use to update the dialog box.</remarks>
            NoProgressBar = 0x00000010
        }
        #endregion
    }
}

C#, Win32API ,

.NET Framework: Communicate through NAT Router via UPnP (Universal Plug and Play)

5. February 2009

I've been working on an application recently that needs to be able to communicate through a router/firewall using TCP. I've read/heard a bit of information about NAT Routers and UPnP; the technoligies used in almost every router sold commercially. So, I knew that you could use the Universal Plug and Play (UPnP) features of the NAT Router to automatically open up the firewall via Port Forwarding to allow other computers on the Internet to connect directly to the one your application is running on. One thing I didn't know what that Windows (since Windows XP) has the NATUPnP 1.0 Type Library (NATUPNP.DLL) COM Component that you can utilize within your applications to do this for you.

I haven't found very many articles online (via Google search) on using the NATUPnP 1.0 Type Library; there's pretty much only small code snippets available. So, I've decided to explain a little further, in this article, the What and Why of communicating through a NAT Router utilizing UPnP.

What is NAT (Network Address Translation)?

Network Address Translation is the method employed by a Network Router (and Wireless Routers) that allows you to have multiple computers connected to the internet using your home/work broadband internet connection. Your Router gets assigned an Internet IP Address by your Internet Service Provider, and then the Router assigns a Private IP Address to each computer on your network. The Router then "routes" Incoming and Outgoing network traffic from the Internet to the appropriate computer on your Private Network. This technique is also known as "IP masquerading", since it hides your entire Private Network from the Internet making it appear as if it were only a single computer connected to the Internet.

One of the biggest side effets of NAT is that it essentially turns your Router into a Firewall. This is because when any unsolicited incoming traffic gets sent you the Router, it has no idea which computer on the Private Network might be the intended recipient. So, consiquentially, it just ignores or blocks the unsolicited incoming traffic. This is also the feature that allows you to safely enable File and Printer Sharing between the computers on the Private Network without the risk of any computer/server on the Internet being able to copy your files.

Also worth noting that NAT can be implemented by other devices than a Network Router, such as: a computer running Windows with Internet Connection Sharing and Windows Firewall, or a computer running software that provides NAT.

You can read more about NAT at the following links:

http://en.wikipedia.org/wiki/Network_address_translation

http://en.wikipedia.org/wiki/NAT_traversal

What is Port Forwarding / Static NAT?

Port Forwarding, also called "Static NAT", allows computers on the Internet to connect directly to a specific computer on your Private Network. This works by configuring your NAT Router to forward any incoming traffic on a specific Port to a specific computer on your network. For example you can run a Web Server by setting the NAT Router to forward all incoming traffic on Port 80 to your Web Server on your Private Network.

You can read more aboug Port Forwarding at the following link:

http://en.wikipedia.org/wiki/Port_forwarding

What is Universal Plug and Play (UPnP)?

Universal Plug and Play (UPnP) is a simple communications mechanism that allows computers and network appliances to auto-configure Port Forwarding on a NAT Router. This greatly simplifies the setup of P2P networks such that the user doesn't need to configure the NAT Router, instead the computer can connect directly to the Router and setup the appropriate Port Forwarding to allow it to accept direct connections from external computers on the Internet.

This is essentially the technology that allows Peer-to-Peer Software (like Bit Torrent) to communicate through a NAT Router (Firewall) and connect directly to other computers across the Internet. There is also alot of Software and Devices that rely on this to function, such as Windows Live Messenger and XBox 360.

You can read more about UPnP at the following link:

http://en.wikipedia.org/wiki/Universal_Plug_and_Play

Other Resources on Network Address Translation (NAT)

Including the above links there are many articles online that discuss in further detail what NAT and UPnP is. One resource that I really recommend is Episode #42 of the Security Now Podcast with Steve Gibson. If you prefer not to listen to the podcast, there is also a full transcript available for you to read. I really encourage you to read or listen to this.

Security Now! #42 - NAT Traversal

Using the NATUPnP 1.0 Type Library to Setup Port Forwarding

The NATUPnP 1.0 Type Library (NATUPNP.DLL) COM Component, which is part of Windows (since Windows XP), makes it possible to easily manage Network Address Translation (NAT) through Universal Plug and Play (UPnP). This library provides the ability to configure port mappings on a remote Gateway Device (IGD) that uses NAT. It is worth mentioning that the remote Gateway Device could be another computer running Windows with Internet Connection Sharing and Windows Firewall, or a dedicated hardware device.

The API Reference for the NATUPnP 1.0 Type Library can be found here: http://msdn.microsoft.com/en-us/library/aa366276(VS.85).aspx

In order to use the NATUPnP Library you need to first add a reference to it to your project within Visual Studio. To do so open the Add Reference dialog, click the COM tab and select the "NATUPnP 1.0 Type Library". The resulting reference that's added will be named "NATUPNPLib" and will be the namespace that contains all the functionality contained within the library (all Interfaces and Classes).



The NATUPnP 1.0 Type Library makes it so easy to setup Port Forwarding it literally only requires a couple lines of code.

Get a listing of all existing Static Port Mappings

To get a list of all the Static Port Mappings that are already setup in your NAT enabled Internet Gateway Device (such as Router) you simply create an instance of the UPnPNATClass object and access its StaticPortMappingCollection property.

NATUPNPLib.UPnPNATClass upnpnat = new NATUPNPLib.UPnPNATClass();
NATUPNPLib.IStaticPortMappingCollection mappings = upnpnat.StaticPortMappingCollection;

Once you get the collection of existing port mapping, you can iterate through them just as you would any normal Collection of objects:

foreach(NATUPNPLib.IStaticPortMapping portMapping in mappings)
{
    // do something with the port mapping, such as displaying it in a listbox
}

Add New Static Port Mapping

To add a new Static Port Mapping you simply call the Add method of the IStaticPortMappingCollection object that is returned by the UPnPNATClass.StaticPortMappingCollection property.

// Here's an example of opening up TCP Port 80 to forward to a specific Computer on the Private Network
mappings.Add(80, "TCP", 80, "192.168.1.100", true, "Local Web Server");

// Here's an example of forwarding the UDP traffic of Internet Port 80 to Port 8080 on a Computer on the Private Network
mappings.Add(80, "UDP", 8080, "192.168.1.100", true, "Local Web Server");

Remove Existing Static Port Mapping

To remove an existing Static Port Mapping you simply call the Remove method of the IStaticPortMappingCollection object that is returned by the UPnPNATClass.StaticPortMappingCollection property.

// Remove TCP forwarding for Port 80
mappings.Remove(80, "TCP");

// Remove UDP forwarding for Port 8080
mappings.Remove(8080, "UDP");

Example Port Forwarding Management Application

While working with the NATUPnP 1.0 Type Library I created a simple application that allows you to maintain the Static Port Mappings that are setup on your Private Networks NAT Router.

Download Sample: NATUPnPPortForwardManager.zip (20.68 kb)


Conclusion

Setup up Port Forwarding on your local router via UPnP makes it extremely convenient for users of your networking application. Instead of requiring them to manually setup Port Forwarding, we can automatically set it up for them. Many popular P2P applications have utilized this functionality for years, and I'm positive you've used at least one of these applications already at some point.

C#, vb.net, Win32API , , ,

C#: Flash Window in Taskbar via Win32 FlashWindowEx

26. January 2009

The Windows API (Win32) has the FlashWindowEx method within the User32 library; this method allows you (the developer) to Flash a Window, signifying to the user that some major event occurred within the application that requires their attention. The most common use of this is to flash the window until the user returns focus to the application. However, you can also flash the window a specified number of times, or just keep flashing it until you decide when to stop.

The use of the FlashWindowEx method however isn't built into the .NET Framework anywhere. In order to access it you need to use the Platform Invoke (PInvoke) features of .NET to "drop" down to the Windows API (Win32) and call it directly. Also, as with many other functionalities in the Windows API (that aren't directly exposed by .NET) the FlashWindowEx method can be a little tricky to use if you aren't familiar with working with the Windows API from within .NET.

Now rather than go too deep into the specifics of PInvoke or the Win32 FlashWindowEx method, below is a simple static class in C# that allows you to easily utilize this method. There is actually quite a bit of information needed to explain how to use PInvoke to utilize the Windows API (Win32), so maybe I'll cover that in a future article.

Here's some example usage of this static class:

// One this to note with this example usage code, is the "this" keyword is referring to
// the current System.Windows.Forms.Form.

// Flash window until it recieves focus
FlashWindow.Flash(this);

// Flash window 5 times
FlashWindow.Flash(this, 5);

// Start Flashing "Indefinately"
FlashWindow.Start(this);

// Stop the "Indefinate" Flashing
FlashWindow.Stop(this);

One thing to note about the FlashWindowEx method is that it requires (and will only work on) Windows 2000 or later.

Here's the code for the static class in C#:

public static class FlashWindow
{
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);

    [StructLayout(LayoutKind.Sequential)]
    private struct FLASHWINFO
    {
        /// <summary>
        /// The size of the structure in bytes.
        /// </summary>
        public uint cbSize;
        /// <summary>
        /// A Handle to the Window to be Flashed. The window can be either opened or minimized.
        /// </summary>
        public IntPtr hwnd;
        /// <summary>
        /// The Flash Status.
        /// </summary>
        public uint dwFlags;
        /// <summary>
        /// The number of times to Flash the window.
        /// </summary>
        public uint uCount;
        /// <summary>
        /// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.
        /// </summary>
        public uint dwTimeout;
    }

    /// <summary>
    /// Stop flashing. The system restores the window to its original stae.
    /// </summary>
    public const uint FLASHW_STOP = 0;
   
    /// <summary>
    /// Flash the window caption.
    /// </summary>
    public const uint FLASHW_CAPTION = 1;
   
    /// <summary>
    /// Flash the taskbar button.
    /// </summary>
    public const uint FLASHW_TRAY = 2;
   
    /// <summary>
    /// Flash both the window caption and taskbar button.
    /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
    /// </summary>
    public const uint FLASHW_ALL = 3;

    /// <summary>
    /// Flash continuously, until the FLASHW_STOP flag is set.
    /// </summary>
    public const uint FLASHW_TIMER = 4;

    /// <summary>
    /// Flash continuously until the window comes to the foreground.
    /// </summary>
    public const uint FLASHW_TIMERNOFG = 12;


    /// <summary>
    /// Flash the spacified Window (Form) until it recieves focus.
    /// </summary>
    /// <param name="form">The Form (Window) to Flash.</param>
    /// <returns></returns>
    public static bool Flash(System.Windows.Forms.Form form)
    {
        // Make sure we're running under Windows 2000 or later
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
    {
        FLASHWINFO fi = new FLASHWINFO();
        fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
        fi.hwnd = handle;
        fi.dwFlags = flags;
        fi.uCount = count;
        fi.dwTimeout = timeout;
        return fi;
    }

    /// <summary>
    /// Flash the specified Window (form) for the specified number of times
    /// </summary>
    /// <param name="form">The Form (Window) to Flash.</param>
    /// <param name="count">The number of times to Flash.</param>
    /// <returns></returns>
    public static bool Flash(System.Windows.Forms.Form form, uint count)
    {
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, count, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    /// <summary>
    /// Start Flashing the specified Window (form)
    /// </summary>
    /// <param name="form">The Form (Window) to Flash.</param>
    /// <returns></returns>
    public static bool Start(System.Windows.Forms.Form form)
    {
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL, uint.MaxValue, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    /// <summary>
    /// Stop Flashing the specified Window (form)
    /// </summary>
    /// <param name="form"></param>
    /// <returns></returns>
    public static bool Stop(System.Windows.Forms.Form form)
    {
        if (Win2000OrLater)
        {
            FLASHWINFO fi = Create_FLASHWINFO(form.Handle, FLASHW_STOP, uint.MaxValue, 0);
            return FlashWindowEx(ref fi);
        }
        return false;
    }

    /// <summary>
    /// A boolean value indicating whether the application is running on Windows 2000 or later.
    /// </summary>
    private static bool Win2000OrLater
    {
        get { return System.Environment.OSVersion.Version.Major >= 5; }
    }
}

 

C#, Win32API , ,