Introduction
When I worked for a network services company a few years back, we had to visit up to ten sites per day, each having their own distinct IP schemes. It was a constant pain to reconfigure network connections every time I moved between sites or subnets. I wrote this program to handle that task and keep track of the different settings for each of our customers. In this article, I will discuss several topics including some easy ways in which you can add a little style to your basic Windows Forms.
Application Interface
For this application, I chose to use a borderless form. In my opinion, it is a quick and easy way to add an aesthetic touch to your app. The can be done programmatically in the code of your form using the following statement:
this.FormBorderStyle = FormBorderStyle.None;
More information is presented on this below.
The Application Main Window
Edit Profile Window
The Code and Concepts Behind the Application
Manipulating the Windows Registry
The Microsoft.Win32
namespace provides access to the RegistryKey
class which allows you to manipulate the Windows registry. Three methods of the RegistryKey
class are utilized in this application, OpenSubKey()
, GetValue()
and SetValue()
. The code below illustrates how each of the aforementioned methods can be used.
In the following code block, I create the object Reg
and set it to reference the HKEY_LOCAL_MACHINE
hive of the registry, then navigate to a subkey "SOFTWARE\AntiDesign\NetProfiles".
RegistryKey Reg = Registry.LocalMachine;
Reg = Reg.OpenSubKey("Software\\AntiDesign\\NetProfiles", true);
Below, I create another RegistryKey
object from a subkey of the Reg
object using the OpenSubKey()
method. The string currProfile
is assigned to using the GetValue()
method of the RegistryKey
class.
RegistryKey adapter = Reg.OpenSubKey("__Adapters\\" + cboSelectAdapter.Text, true);
string currProfile = adapter.GetValue("CurrentProfile").ToString();
The SetValue()
method is called in this instance to store a string
representation of your IP address in the registry. As you can see from the example below, the Type being stored is selected using an Enum
, RegistryValueKind
.
Reg.SetValue("IPAddress", txtIPAddress.Text, RegistryValueKind.String);
Enumerating your Network Adapters
The System.Net
namespace provides numerous classes related to networking. This project only utilizes the NetworkInterface
class's GetAllNetworkInterfaces()
method. GetAllNetworkInterfaces()
returns an array of NetworkInterface
objects. I iterate through each of the objects in the array, checking the NetworkInterfaceType
property so that only Ethernet and 802.11 interfaces are listed.
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
if (netInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet |
netInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
{
cboSelectAdapter.Items.Add(netInterface.Name);
}
}
try
{
cboSelectAdapter.SelectedIndex = 0;
}
catch { }
Applying IP Settings Programmatically
Windows provides a command line tool for changing your network settings called netsh
. Rather than reinvent the wheel, it serves our purpose to call this utility transparently to accomplish this task. To do this, I am going to use the System.Diagnostics.Process
class. Configuration of the process is done through Process.StartInfo
. In the example, I define the following properties:
FileName
(string
)
UseShellExecute
(bool
) - Use the shell to launch the program? This value must be false
to redirect IO or hide the console window.
CreateNoWindow
(bool
) - Should the process be run in a hidden window?
Arguments
(string
) - Any command line arguments to be passed to the application
After the Start()
method is called and the process begins to execute, we also call the WaitForExit()
method to stop execution until netsh.exe has finished modifying the IP settings. The netsh
object is reused to set the DNS servers after changing the Arguments
property.
string argsIP, argsDNS;
argsIP = "interface ip set address name=\"" + AdapterName + "\" static " +
ipAddress + " " + subnetMask + " " + gateway + " 1";
argsDNS = "interface ip set dns name=\"" + AdapterName + "\" static " + dnsServer;
//apply the changes
Process netsh = new Process();
netsh.StartInfo.FileName = "netsh.exe";
netsh.StartInfo.UseShellExecute = false;
netsh.StartInfo.CreateNoWindow = true;
netsh.StartInfo.Arguments = argsIP;
netsh.Start();
netsh.WaitForExit();
//Add the dns server
netsh.StartInfo.Arguments = argsDNS;
netsh.Start();
netsh.WaitForExit();
netsh.Dispose();
Using Embedded Fonts
Embedded fonts are another easy way to give your application that "customized" look without too much trouble or added code. Please note that embedding resources in your program can make the size of your application increase drastically, so if executable size is a major concern, then you may want to skip this step.
To begin embedding fonts, you must add use Interop to import gdi32.dll. A method prototype needs to be declared for AddFontMemResourceEx()
. A PrivateFontCollection
will be used to hold the fonts stored in memory.
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont,
IntPtr pdv, [System.Runtime.InteropServices.In] ref uint pcFonts);
public static PrivateFontCollection PFC = new PrivateFontCollection();
Due to the nature of the Interop method being called, you have to wrap the code in an unsafe
structure.
try
{
unsafe
{
fixed (byte* pFontData = Properties.Resources.RAVIE)
{
uint nil = 0;
PFC.AddMemoryFont((IntPtr)pFontData, Properties.Resources.RAVIE.Length);
AddFontMemResourceEx((IntPtr)pFontData,
(uint)Properties.Resources.RAVIE.Length, IntPtr.Zero, ref nil);
}
}
}
catch
{
MessageBox.Show("Ravie font was not loaded.");
}
Here is an example of how to use our embedded font for the Label3
Font
property. If more than one font has been added to the PrivateFontCollection
, you will need to iterate through the Families[i].Name
properties to get the correct index. In the example below, it assumes that only one font is in the PrivateFontCollection
.
label3.Font = new Font(PFC.Families[0], (float)10);
Borderless Forms
I will quite often use borderless forms because it gives the "customized" look to an application and requires very little effort. You will however, have to add a few lines of code to allow your application to be moved around the screen. This is accomplished by adding MouseDown
and MouseMove
event handlers to the object(s) that you wish to be able to click and drag from.
The following code is inserted in the MouseDown
events for each object that you wish to use as a drag point. The variable e
is of type MouseEventArgs
and contains two properties that are useful for us, e.X
and e.Y
, which tell us the X and Y coordinates of the mouse click. These values are stored in public int
variables, x
and y
.
if (e.Button == MouseButtons.Left)
{
x = e.X;
y = e.Y;
}
The following code needs to be inserted into the MouseMove
event handler code for the objects that will be used as drag points. It uses the current mouse position and the x
and y
variables that were populated during the MouseDown
event to relocate the form.
if (e.Button == MouseButtons.Left)
{
this.Left += (e.X - x);
this.Top += (e.Y - y);
}
Conclusion
Little tweaks such as ensuring a font is available (embedding) and removing the borders from your forms can go a long way as far as user experience is concerned. The version of the program that I have uploaded does not contain the Ravie Font and isn't fully implementing embedded fonts. (I started, but ran out of time on this one...)
Note: Using netsh.exe on Vista can take a while while Windows "discovers?" your network connection. This seems to be resolved in Windows 7 and wasn't an issue in XP.
Final Thought
I still use this application on both of my laptops that I carry with me to and from work. Although I no longer spend as much time at client sites, it still saves time switching from static IP (work) and DHCP (home).
History
- 7-14-2010 - Article posted