Partager via


Creating a Stand-Alone Network Emulator using VS2010 – Beta 1 Release

In the Beta 1 release of Visual Studio 10, we released “True Network Emulation.”  With this release, you can simulate different types of networks when running tests (such as a unit test or load test).  One thing that is missing, however, is the ability to easily emulate different networks when not running tests using the Visual Studio Team System framework.  For example, I might want to just take an application and see how it performs under the restrictions of a low bandwidth connection.   I might not want to create a test for this, I just want to play with it and see what happens.  To accomplish this “out of the box”, I need to create a unit test that sleeps for a very long period of time and turn on network emulation.  While this is fairly easy, this seems a bit awkward.  This post will focus on creating a solution that will make this process as simple as pressing a start button when I want to start emulating and pressing a stop button when I want to quit.

Create a stub application

The first step in creating the network emulator is to open Visual Studio 2010 and create an Empty “Windows Forms Application.”

image

The next step is to copy the assembly that contains the Network Emulation API.  This assembly is named “userapi.dll” and is located in %Program Files%\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\DataCollectors\x86 (or x64 for 64 bit systems).  Copy this file and place it into the solution directory, then add it to the solution and change the “Copy to Output Directory” to “Copy if newer”.

 

image

Create a class to hold the Simplified Network Emulation API

In this release of Visual Studio, the network emulation API is native code.  Therefore we have a limited amount of ways that we can access the API from managed code.  In this sample, we will make calls into the API using PInvoke.  In order to do this, we will have to create some definitions for each of these methods before we can use them.  So create a new class called NativeNetworkEmulationAPI and add the following declarations:

using System;
using System.Runtime.InteropServices;

namespace StandaloneNetworkEmulation
{
public class NativeNetworkEmulationAPI
{
[DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int NEGetVersion(ref uint pdwVersion);

        [DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int NEInitialize(ref uint pdwVersion, ref IntPtr phEmulator);

        [DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int NECleanup(IntPtr hEmulator);

        [DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int NEStartEmulation(IntPtr hEmulator);

        [DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int NEStopEmulation(IntPtr hEmulator, uint dwTimeout, int fForce);

        [DllImport("userapi.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int NELoadProfile(IntPtr hEmulator,
[MarshalAs(UnmanagedType.LPWStr)]
string profileName);
}
}

Copy and Modify the Predefined Network Profiles

The next step is to copy the predefined network profiles from VS2010 to our solution so that we can modify them for use in our stand-alone emulator.  To do this, lets create a folder in the solution called “Profile” and copy all of the predefined network profiles (located in %Program Files%\Microsoft Visual Studio 10.0\Common7\IDE\Templates\LoadTest\Networks) to this folder and set the Build Action = “None” for each profile.  Once this is done, we need to slightly modify each profile in order to get the emulator to read the profile properly.  To do this, we need to load each profile and remove the two lines that contain the name  “NetworkEmulationProfile” (This should be the first and last lines in the profiles).  In the “3G.network” file, the two lines removed are:

After modifying this profile, the top level xml node should be <Emulation>. One final note is to just delete the LAN.network profile as it will not work with this emulator. It is a special case that we use in our framework to indicate that we want to use the full bandwidth of the available network (in other words, don’t simulate anything).

Complete the Main Form in the application

Next, lets add the start and stop buttons to the main form and a combobox that will allow us to select a profile to use for the emulation.

image

Add member variables to the form:
    1: private IntPtr m_emulatorHandle;
Add a Load event handler to the form and add the following code:
    1: NativeNetworkEmulationAPI.NELoadProfile(m_emulatorHandle, Path.Combine("Profiles", 
    2:     m_networkProfiles.SelectedText) + ".Network");
    3: if (0 == NativeNetworkEmulationAPI.NEStartEmulation(m_emulatorHandle))
    4: {
    5:     startButton.Enabled = false;
    6:     stopButton.Enabled = true;
    7: }
    8: else
    9: {
   10:     MessageBox.Show("There was an error starting the emulation.");
   11: }
  

Add a FormClosed event handler to the form and add the following code:
    1: if (0 != NativeNetworkEmulationAPI.NECleanup(m_emulatorHandle))
    2: {
    3:     MessageBox.Show("There was an error cleaning up the emulator.");
    4: }

 

Add a Click event handler to the start button and add the following code:
    1: NativeNetworkEmulationAPI.NELoadProfile(m_emulatorHandle, Path.Combine("Profiles", 
    2:     m_networkProfiles.SelectedText) + ".Network");
    3: if (0 == NativeNetworkEmulationAPI.NEStartEmulation(m_emulatorHandle))
    4: {
    5:     startButton.Enabled = false;
    6:     stopButton.Enabled = true;
    7: }
    8: else
    9: {
   10:     MessageBox.Show("There was an error starting the emulation.");
   11: }

Add a Click event handler to the stop button and add the following code:
    1: if (0 == NativeNetworkEmulationAPI.NEStopEmulation(m_emulatorHandle, 2000, 1))
    2: {
    3:     startButton.Enabled = false;
    4:     stopButton.Enabled = true;
    5: }
    6: else
    7: {
    8:     MessageBox.Show("There was an error stopping the emulation.");
    9: }
   10: startButton.Enabled = true;
   11: stopButton.Enabled = false;

Conclusion

In this article, we created a sample network emulator using the API provided in the VS2010 Network Emulation feature.  Although this example was simple, there are a lot of things that can be done using this API, such as packet tracing, filtering, etc.  Keep an eye on this blog and in the future,  I will show you how to customize the profiles to perform some of the advanced features of network emulation. 

Try it out and let me know

If you haven’t done so already, please download the Beta1 build of Visual Studio Team System 2010.  Then try out this network emulator and let me know what you think.  We would love to hear your feedback.

Comments

  • Anonymous
    October 07, 2012
    Hello, I am trying to use this api developing a C# wrapper as in your example. It works for loading a profile, but I am not able to manage to set and get trace information. Any clues about this? So far, I have added to NativeNetworkEmulationAPI : [DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]        public static extern long NESetTraceInfo(IntPtr emulator, int fEnabled, uint dwPacketSize, uint dwBufferSize);        [DllImport("userapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]        public static extern long NEGetTraceData(IntPtr emulator, out NetworkEmulator.TraceData tData, ref uint dwBufferSize); The structure TraceData should look like this:    public struct TraceData    {        public int bDownStream;        public uint dwPacketSize;        public long llEnterTime;        public TraceError TraceError;        public TraceLatency TraceLatency;        public TraceLoss TraceLoss;        public TracePacket TracePacket;        public TraceQueue TraceQueue;        public TraceReorder TraceReorder;        public TraceTraffic TraceTraffic;        public byte[] ucPacket;    } I am trying to set the trace info as: NESetTraceInfo(m_emulatorPtr,1, 1500, 1500); And when I try to get trace info, I do: NEGetTraceData(m_emulatorPtr, out tData, ref dwBufferSize); However, I cannot see any data being traced, and always get tData structure as all-zeros. I hope that you can help me with this. Many thanks in advance!

  • Anonymous
    December 09, 2012
    Hi, We built a standalone network tool based on NEWT. Our client wants to buy a copy of the tool for testing.  We're unclear whether this would infringe on Copyright as it uses the API provided in the VS2010 Network Emulation feature. What do you think?