Retrieving last commit ID / revision number from a deployed application - c#

I would like to have a function returning the last revision / commit ID of the source from which the deployed application / library was built.
I have been using the __DATE__ and __TIME__ macros in C++ but this approach has obvious drawbacks.
What tools are available to achieve this in an automated manner?
I am using Eclipse-CDT for C++ (Linux, SVN) but it I am also interested in git solutions, and sources written in Java and C#.

By using Visual Studio and C# you could within a Pre-Built step calling the SubWCRev command with a template file, that will be copied to a file used within the solution.
The command within the Pre-Built step is:
<PreBuildEvent>SubWCRev "$(ProjectDir)\" "$(ProjectDir)VersionProvider.template.cs" "$(ProjectDir)VersionProvider.cs"</PreBuildEvent>
Within the project add the following two files:
<Compile Include="VersionProvider.cs">
<DependentUpon>VersionProvider.template.cs</DependentUpon>
</Compile>
<None Include="VersionProvider.template.cs" />
With this content:
internal static class VersionProvider
{
/// <summary>
/// Provides the current subversion revision number
/// </summary>
internal const string CurrentSVNRevision = "$WCREV$";
}
Last but not least within AssemblyInfo.cs add the following line:
[assembly: AssemblyInformationalVersion(VersionProvider.CurrentSVNRevision)]
The project will by this way automatically get the current subversion revision number of this project folder baked into version info of the application which can be seen on the details page of the file properties.
You can also retrieve this information through code at runtime:
private string GetAdditionalVersionInfo()
{
var assembly = Assembly.GetEntryAssembly();
var attributesFound = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), true);
var version = attributesFound.OfType<AssemblyInformationalVersionAttribute>().FirstOrDefault();
return version != null ? version.InformationalVersion : String.Empty;
}

/// <summary>
/// Gets the SVN date.
/// </summary>
/// <value>The SVN date. value</value>
// ReSharper disable UnusedMember.Global
public static new string SvnDate{
// ReSharper restore UnusedMember.Global
get {
const string S = "$Date$";
return S.Substring(7, 19);
}
}
/// <summary>
/// Gets the SVN ID.
/// </summary>
/// <value>The SVN ID. value</value>
// ReSharper disable UnusedMember.Global
public static new string SvnID{
// ReSharper restore UnusedMember.Global
get{
const string S = "$Id$";
const string D = "$";
return S.Replace(D + "Id: ", string.Empty).Replace(" " + D, string.Empty);
}
}
/// <summary>
/// Gets the SVN rev.
/// </summary>
/// <value>The SVN rev. value</value>
// ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global
public static new string SvnRev{
// ReSharper restore MemberCanBePrivate.Global
// ReSharper restore UnusedMember.Global
get {
const string S = "$Rev$";
return S.Item(1);
}
}
/// <summary>
/// Gets the SVN author.
/// </summary>
/// <value>The SVN author. value</value>
// ReSharper disable UnusedMember.Global
public static new string SvnAuthor{
// ReSharper restore UnusedMember.Global
get {
const string S = "$Author$";
return S.Item(1);
}
}
Note S.Item(n) returns a space delimited zero based item. Replace with your own code. Also, new is only necessary to deal with inheritance. Props must be set in svn. I dont have a snippet showing the expansions since I am on Mercurial now.

If you were working with a Makefile and SVN, you could do something like this for C or C++:
REVISION=$(shell svnversion)
CFLAGS=... -D__REVISION__=\"$REVISION\"
I'm not that familiar with CDT, but I understand it's able to use a Makefile-based build system.

Related

Using VST.net with Unity3d to create a simple VST host

I've successfully simplified the Vst.net host sample to directly load a vst instrument. Mostly I've just stripped out the GUI and made it automatically fire a few test notes. This code works when I build it as a console application.
using System;
using Jacobi.Vst.Core;
using Jacobi.Vst.Interop.Host;
using NAudio.Wave;
using CommonUtils.VSTPlugin;
namespace Jacobi.Vst.Samples.Host
{
///<Summary>
/// Gets the answer
///</Summary>
public class pianoVST
{
///<Summary>
/// Gets the answer
///</Summary>
public static VST vst = null;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
}
/// <summary>
/// Play some test notes.
/// </summary>
public void playTest()
{
var asioDriverNames = AsioOut.GetDriverNames();
if (asioDriverNames.Length > 0)
{
MidiVstTest.UtilityAudio.OpenAudio(MidiVstTest.AudioLibrary.NAudio, asioDriverNames[0]);
MidiVstTest.UtilityAudio.StartAudio();
vst = MidiVstTest.UtilityAudio.LoadVST("[path-to-vst-dll]");
for (int i = 0; i < 3; i++)
{
vst.MIDI_NoteOn(60, 100);
System.Threading.Thread.Sleep(1000);
vst.MIDI_NoteOn(60, 0);
System.Threading.Thread.Sleep(1000);
}
}
}
}
}
However, when I build this as a dll, import it into Unity and then attach it to a simple GameObject, I'm not able to get it to run or build. The error message I get is:
ArgumentException: The Assembly Jacobi.Vst.Interop is referenced by Jacobi.Vst.Samples.Host ('Assets/Jacobi.Vst.Samples.Host.dll'). But the dll is not allowed to be included or could not be found.
I've rebuilt the C++ interop dll from source but nothing I do makes it work with Unity.
Is there something I can do to make the Jacobi.Vst.Interop dll work nicely with Unity?
VST.Net has a dependency on the VC110.CRT package. That error could be a result of not have the VC runtime installed.
https://github.com/obiwanjacobi/vst.net/blob/master/docs/Using_VST.NET.md

Delete Rows Start in CSV file In SSIS Controlflow Script Task

I have a .csv file that looks like this:
#Example Company
#(999) 999-9999
#http://yourwebsite.com
#Report Date Range: Dec 26, 2013 - Dec 26, 2013
#Exported: Dec 26, 2013
#Twitter : Profile Summary
#Screen Name,Name,Description,Location,Followers,Following,Listed
SctaSa,statisticalgraph,statistical Screen- The official account for your
organization,Saudi Arabia,6775,8,75
So, I need to take specific data from the .csv file to be readable to SSIS Transformation, start from column "Screen Name" and remove the garbage data which start with # , to be look like that
Screen Name,Name,Description,Location,Followers,Following,Listed,Exported,Report Date Range
SctaSa,statisticalgraph,statistical Screen- The official account for your organization,Saudi Arabia,6775,8,75,26-Dec-13,26-Dec-13
i tried to use this C# script but it does not wore file (I'm not an expert in C# so I don't know what the problem is) I tried to use the following script to delete any line start with # but the file dose not transfare to the out put path; could you give me any suggestions?!
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
#endregion
namespace ST_a7b941606e0b40aa920bfe13fc81dc81
{
/// <summary>
/// ScriptMain is the entry point class of the script. Do not change the name, attributes,
/// or parent of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
protected void Page_Load(object sender, EventArgs e)
{
var lines = new List<string>();
string line;
using (var file = new System.IO.StreamReader("D:\\try.csv"))
{
while ((line = file.ReadLine()) != null)
{
if (line.Length != 0)
{
if (!line.StartsWith("#") )
{
lines.Add(line);
}
}
}
}
File.WriteAllLines("D:\\SCTA_ETL\\try.csv", lines);
}
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
// TODO: Add your code here
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
Another way:
File.WriteAllLines(outputPath, File.ReadAllLines("c:\\mycsv.csv").Where(x => !x.StartsWith("#")).ToArray());
You might want to change your logic in the middle:
var lines = new List<string>();
string outputPath = // your output path here
using (var file = new System.IO.StreamReader("c:\\mycsv.csv"))
{
string line;
while ((line = file.ReadLine()) != null)
{
if (!line.StartsWith("#"))
{
lines.Add(line);
}
}
}
File.WriteAllLines(outputPath, lines);
You had been removing all lines that had "#" anywhere inside.
Instead, only add in lines that do not start with "#".
Also, be sure to close and dispose your StreamReader when you are done with it, or just put the whole thing in using section.

How do I get the architecture of a dll in C#? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a C# program that needs to copy over a user provided dll for another program to load and use. In the case of the program running on a 64 bit machine, the user should not be allowed to pass a 32 bit dll and should inform the user that they've provided an incorrect dll. So how can I find the architecture of a dll?
I saw a couple similar questions and they mentioned DUMPBIN and Corflags.exe, but there is no example code, so where do I find these programs and how do I use these?
Code example
This is the complete code of a C# console application that can detect dll architectures that also includes the ones you wanted.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MachineType type = GetDllMachineType("path/to/MyAssembly.dll");
if (type.Equals(MachineType.IMAGE_FILE_MACHINE_I386))
{
Console.WriteLine("Dll architecture: x86/32bit");
}
else if (type.Equals(MachineType.IMAGE_FILE_MACHINE_IA64))
{
Console.WriteLine("Dll architecture: x64/64bit");
}
Console.ReadKey();
}
public static MachineType GetDllMachineType(string dllPath)
{
//see http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
//offset to PE header is always at 0x3C
//PE header starts with "PE\0\0" = 0x50 0x45 0x00 0x00
//followed by 2-byte machine type field (see document above for enum)
FileStream fs = new FileStream(dllPath, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
fs.Seek(0x3c, SeekOrigin.Begin);
Int32 peOffset = br.ReadInt32();
fs.Seek(peOffset, SeekOrigin.Begin);
UInt32 peHead = br.ReadUInt32();
if (peHead != 0x00004550) // "PE\0\0", little-endian
throw new Exception("Can't find PE header");
MachineType machineType = (MachineType)br.ReadUInt16();
br.Close();
fs.Close();
return machineType;
}
public enum MachineType : ushort
{
IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
IMAGE_FILE_MACHINE_AM33 = 0x1d3,
IMAGE_FILE_MACHINE_AMD64 = 0x8664,
IMAGE_FILE_MACHINE_ARM = 0x1c0,
IMAGE_FILE_MACHINE_EBC = 0xebc,
IMAGE_FILE_MACHINE_I386 = 0x14c,
IMAGE_FILE_MACHINE_IA64 = 0x200,
IMAGE_FILE_MACHINE_M32R = 0x9041,
IMAGE_FILE_MACHINE_MIPS16 = 0x266,
IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
IMAGE_FILE_MACHINE_R4000 = 0x166,
IMAGE_FILE_MACHINE_SH3 = 0x1a2,
IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
IMAGE_FILE_MACHINE_SH4 = 0x1a6,
IMAGE_FILE_MACHINE_SH5 = 0x1a8,
IMAGE_FILE_MACHINE_THUMB = 0x1c2,
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169,
}
// returns true if the dll is 64-bit, false if 32-bit, and null if unknown
public static bool? UnmanagedDllIs64Bit(string dllPath)
{
switch (GetDllMachineType(dllPath))
{
case MachineType.IMAGE_FILE_MACHINE_AMD64:
case MachineType.IMAGE_FILE_MACHINE_IA64:
return true;
case MachineType.IMAGE_FILE_MACHINE_I386:
return false;
default:
return null;
}
}
}
}
Using Corflags...
You wrote about this and, just to know, this will help you to get some information regarding your assembly (dll) but this is not C#! this is a tool that can be used in Visual Studio console.
Just open Visual Studio console and use this command:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC>corflags C:/path/to/MyAssembly.dll
This will be the output:
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 3.5.21022.8 Copyright (c) Microsoft Corporation. All rights reserved.
Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 24
ILONLY : 0
32BIT : 0
Signed : 1
Then, focus on PE:PE32, this will describe your assembly architecture:
So, according to this...
AnyCPU means -> PE: PE32 -> 32BIT: 0
x86 means -> PE: PE32 -> 32BIT: 1
x64 means -> PE: PE32+ -> 32BIT: 0
The architecture of MyAssembly.dll is 32bit
Idea...
Well, if you want to simplify all this, an idea could be to create a background process using C# then in the arguments use the command I gave you above and print the output of PE:XX to get the assembly architecture and according to that value tell your application what to do.
I just made some research, hope this helps :-)
A while back we wanted to run 64bit mixed mode unit tests in build without changing the build environment. I wrote a tool that creates a proxy assembly using reflection emit. Here is the utility class I wrote that detects if assembly is 64bit. This is different then the flags controlled by corflags
namespace MstestRunner.TestProxyGenerator
{
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
/// <summary>
/// TODO: Update summary.
/// </summary>
public static class AssemblyUtility
{
/// <summary>
/// The value 'PE\0\0'
/// </summary>
private const uint PeHeaderValue = 0x4550;
/// <summary>
/// Image file value found at start of PE header that indicates assembly is 64bit.
/// </summary>
private const ushort ImageFileMachineAmd64 = 0x8664;
/// <summary>
/// The offset to PIMAGE_DOS_HEADER->e_lfanew
/// </summary>
private const int DosHeaderLfaNewOffset = 0x3c;
/// <summary>
/// Checks to see if the module is a 64 bit
/// </summary>
/// <param name="path">The path to the assembly.</param>
/// <returns>
/// True if is 64bit
/// </returns>
public static bool Is64BitImage(string path)
{
return ReadImageMachineType(path) == MachineType.ImageFileMachineAMD64;
}
/// <summary>
/// Reads the machine type from the pe header.
/// </summary>
/// <param name="path">The path to the image.</param>
/// <returns>The assembly machinetype.</returns>
public static MachineType ReadImageMachineType(string path)
{
// The memory layout varies depending on 32/64 bit. The portions of the PE header we are reading should be the same though regardless.
byte[] buffer = new byte[4];
using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
// skip to PIMAGE_DOS_HEADER->e_lfanew of dos header.
fileStream.Seek(DosHeaderLfaNewOffset, SeekOrigin.Begin);
// read and jump to offset in PIMAGE_DOS_HEADER->e_lfanew. This is start of PIMAGE_NT_HEADERS
fileStream.Read(buffer, 0, 4);
fileStream.Seek(BitConverter.ToUInt32(buffer, 0), SeekOrigin.Begin);
// Validate PE\0\0 header.
fileStream.Read(buffer, 0, 4);
if (BitConverter.ToUInt32(buffer, 0) != PeHeaderValue)
{
throw new TestRunnerException(string.Format(CultureInfo.InvariantCulture, "The specified assembly '{0}' does not appear to be valid.", path));
}
// Read the PIMAGE_FILE_HEADER->Machine value.
fileStream.Read(buffer, 0, 2);
return (MachineType)BitConverter.ToUInt16(buffer, 0);
}
}
/// <summary>
/// Safely loads the assembly.
/// </summary>
/// <param name="path">The path to the assembly to load.</param>
/// <returns>The loaded assembly</returns>
public static Assembly SafeLoadAssembly(string path)
{
try
{
return Assembly.Load(path);
}
catch (ArgumentNullException)
{
}
catch (FileNotFoundException)
{
}
catch (FileLoadException)
{
}
catch (BadImageFormatException)
{
}
return null;
}
}
}
Just try to load the dll with imageload
http://msdn.microsoft.com/en-us/library/ms680209(v=vs.85).aspx
Of course unload it or get last error if this function fails so you can study the output and decide after that if this dll is same architecture like your c# application or not.

Creating persistent RAS connection with opennetcf Ras in Windows CE 6.0

I'm in need to create an GPRS connection in an PDA that has windows ce 6. Now normally i would had to use the manufacturer's dll to create that, but they said that they use ras to accomplish this. The only problem of using that is that i program in .net c#, and the library is an unmanaged code one.
Fortunately i came by the opennetcf ras library that does already the necessary pInvokes for the windows ras library, the only problem being the poor documentation.
I created then an library that would call and set-up the necessary GPRS connection on windows. I'm using an Portuguese telecom operator that uses the following definitions:
Operator Name: Optimus P
Apn: umts
Password: *******
User: ******
Consulting the gsm module definition, i had the following modem settings:
Connection Name: GPRS
Device: Hayes Compatible on COM1:
Baund Rate:115200
Data Bits: 8
Parity:1
Stop Bits: 1
Flow Control: Hardware
and of course the extra settings (or how i call it the atCall)
+cgdcont=1, "ip", "umts"
This settings when i use the control panel and do an connect with that profile, it connects and i'm able to call all the webservices without an error. It also shows an extra profile for the modem that shows the settings for the device, incluid the ipaddress, subnet mask and even the default gateway.
The problem is that when i use the library that i created to create an gprs connection programatically, and then call the webservices at some point it throws me an web exception : The remote name could not be resolved. I also checked and the extra icon does not appear, but if i see the GPRS status it appears as it is connected.
The code that create, destroys and query if it exists an connection is as follows:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using OpenNETCF.Net;
using OpenNETCF.Diagnostics;
namespace gsmAdapterNet
{
/// <summary>
/// GPRS Connection class
/// </summary>
public class GPRS
{
private static string connectionName = "GPRS";
/// <summary>
/// Connects the GPRS.
/// </summary>
/// <returns></returns>
public static bool ConnectGPRS()
{
//precisamos de obter as connecoes e ligar
RasEntryCollection connecoesPossiveis = Ras.Entries;
RasEntry _currentEntry = connecoesPossiveis[connectionName];
_currentEntry.RasStatus += new RasNotificationHandler(RasStatusHandler);
RasError resultado = _currentEntry.Dial(false);
if (resultado == RasError.Success)
return true;
else
return false;
}
static void RasStatusHandler(int hConn, RasConnState State, RasError ErrorCode)
{
Logger.WriteLine("");
Logger.WriteLine("RAS STATUS: " + ErrorCode.ToString() + " , State: " + State.ToString());
}
/// <summary>
/// Disconnects the GPRS.
/// </summary>
/// <returns></returns>
public static void DisconnectGPRS()
{
RasEntryCollection entradas = Ras.Entries;
foreach (RasEntry possivelEntrada in entradas)
{
if (possivelEntrada.Name == connectionName)
{
possivelEntrada.Hangup();
}
}
}
/// <summary>
/// Determines whether this instance is connected.
/// </summary>
/// <returns>
/// <c>true</c> if this instance is connected; otherwise, <c>false</c>.
/// </returns>
public static bool isConnected()
{
RasConnection[] conecoes = Ras.ActiveConnections;
foreach (RasConnection conecao in conecoes)
{
if (conecao.Name == connectionName)
return true;
}
return false;
}
/// <summary>
/// Dumps the ras entries.
/// </summary>
public static void DumpRasEntries()
{
foreach (RasEntry entry in Ras.Entries)
{
Logger.DumpRasEntry(entry);
}
}
}
}
So resuming the question is how i can create an viable connection with the opennetcf ras library
Best Greetings
It seems as if the network interface for the GPRS connection that you get when dialing in is not configured with the correct DNS servers. Alternatively, the domain names needed for your service calls may be wrong.
To verify this:
Is it only a specific web service whose domain name cannot be resolved? Is it always the same? Do others work? Can you simply HTTP GET something like http://stackoverflow.com programmatically after the connection has been established?

NUnit with an ASP.NET web site

I'm currently trying to upgrade our build server at work, going from having no build server to having one!
I'm using JetBrains' TeamCity (having used ReSharper for a couple of years I trust their stuff), and intend to use NUnit and MSBuild.
However, I've come up with an issue: it appears that it is not possible to test an ASP.NET web site with NUnit. I had assumed it would be possible to configure it to test App_Code after a build, however it seems that the only way to do tests nicely is through converting the web site to a web application (which my boss does not like the idea of).
How could I go about this?
Please bear in mind that the testing needs to be able to be fired automatically from TeamCity.
If you want to smoke test your site, or bang on some endpoints - see the code below.
If, on the other hand, you want to test the untestable (detestable) ASP.NET website assembly (as opposed to a web app), you are, as they say in France, S.O.L.
The assembly is a randomly named dynamically compiled assembly deep in the bowels of the framework temporary ASP.NET files, making testing close to impossible.
You really do need to consider a couple of options:
place the logic that needs testing in a seperate assembly.
change to a web application project that delivers a testable assembly.
Sorry, I don't think that you will find what you are looking for, but I could be wrong. Let's see.
Good Luck
Download Visual Studio 2008 sample with site and applicatgion.
I wrote a wrapper for WebHost.WebServer.dll which is the core of the development server that works quite well in CI. I use it all the time.
Here is a scaled down version including a usage example.
Test.cs
using System.Net;
using NUnit.Framework;
namespace Salient.Excerpts
{
[TestFixture]
public class WebHostServerFixture : WebHostServer
{
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
// debug/bin/testproject/solution/siteundertest - make sense?
StartServer(#"..\..\..\..\TestSite");
// is the equivalent of
// StartServer(#"..\..\..\..\TestSite",
// GetAvailablePort(8000, 10000, IPAddress.Loopback, true), "/", "localhost");
}
[TestFixtureTearDown]
public void TestFixtureTearDown()
{
StopServer();
}
[Test]
public void Test()
{
// while a reference to the web app under test is not necessary,
// if you do add a reference to this test project you may F5 debug your tests.
// if you debug this test you will break in Default.aspx.cs
string html = new WebClient().DownloadString(NormalizeUri("Default.aspx"));
}
}
}
WebHostServer.cs
// Project: Salient
// http://salient.codeplex.com
// Date: April 16 2010
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;
using Microsoft.VisualStudio.WebHost;
namespace Salient.Excerpts
{
/// <summary>
/// A general purpose Microsoft.VisualStudio.WebHost.Server test fixture.
/// WebHost.Server is the core of the Visual Studio Development Server (WebDev.WebServer).
///
/// This server is run in-process and may be used in F5 debugging.
/// </summary>
/// <remarks>
/// If you are adding this source code to a new project, You will need to
/// manually add a reference to WebDev.WebHost.dll to your project. It cannot
/// be added from within Visual Studio.
///
/// Please see the Readme.txt accompanying this code for details.
/// </remarks>
/// NOTE: code from various namespaces/classes in the Salient project have been merged into this
/// single class for this post in the interest of brevity
public class WebHostServer
{
private Server _server;
public string ApplicationPath { get; private set; }
public string HostName { get; private set; }
public int Port { get; private set; }
public string VirtualPath { get; private set; }
public string RootUrl
{
get { return string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}{2}", HostName, Port, VirtualPath); }
}
/// <summary>
/// Combine the RootUrl of the running web application with the relative url specified.
/// </summary>
public virtual Uri NormalizeUri(string relativeUrl)
{
return new Uri(RootUrl + relativeUrl);
}
/// <summary>
/// Will start "localhost" on first available port in the range 8000-10000 with vpath "/"
/// </summary>
/// <param name="applicationPath"></param>
public void StartServer(string applicationPath)
{
StartServer(applicationPath, GetAvailablePort(8000, 10000, IPAddress.Loopback, true), "/", "localhost");
}
/// <summary>
/// </summary>
/// <param name="applicationPath">Physical path to application.</param>
/// <param name="port">Port to listen on.</param>
/// <param name="virtualPath">Optional. defaults to "/"</param>
/// <param name="hostName">Optional. Is used to construct RootUrl. Defaults to "localhost"</param>
public void StartServer(string applicationPath, int port, string virtualPath, string hostName)
{
if (_server != null)
{
throw new InvalidOperationException("Server already started");
}
// WebHost.Server will not run on any other IP
IPAddress ipAddress = IPAddress.Loopback;
if(!IsPortAvailable(ipAddress, port))
{
throw new Exception(string.Format("Port {0} is in use.", port));
}
applicationPath = Path.GetFullPath(applicationPath);
virtualPath = String.Format("/{0}/", (virtualPath ?? string.Empty).Trim('/')).Replace("//", "/");
_server = new Server(port, virtualPath, applicationPath, false, false);
_server.Start();
ApplicationPath = applicationPath;
Port = port;
VirtualPath = virtualPath;
HostName = string.IsNullOrEmpty(hostName) ? "localhost" : hostName;
}
/// <summary>
/// Stops the server.
/// </summary>
public void StopServer()
{
if (_server != null)
{
_server.Stop();
_server = null;
// allow some time to release the port
Thread.Sleep(100);
}
}
public void Dispose()
{
StopServer();
}
/// <summary>
/// Gently polls specified IP:Port to determine if it is available.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
public static bool IsPortAvailable(IPAddress ipAddress, int port)
{
bool portAvailable = false;
for (int i = 0; i < 5; i++)
{
portAvailable = GetAvailablePort(port, port, ipAddress, true) == port;
if (portAvailable)
{
break;
}
// be a little patient and wait for the port if necessary,
// the previous occupant may have just vacated
Thread.Sleep(100);
}
return portAvailable;
}
/// <summary>
/// Returns first available port on the specified IP address.
/// The port scan excludes ports that are open on ANY loopback adapter.
///
/// If the address upon which a port is requested is an 'ANY' address all
/// ports that are open on ANY IP are excluded.
/// </summary>
/// <param name="rangeStart"></param>
/// <param name="rangeEnd"></param>
/// <param name="ip">The IP address upon which to search for available port.</param>
/// <param name="includeIdlePorts">If true includes ports in TIME_WAIT state in results.
/// TIME_WAIT state is typically cool down period for recently released ports.</param>
/// <returns></returns>
public static int GetAvailablePort(int rangeStart, int rangeEnd, IPAddress ip, bool includeIdlePorts)
{
IPGlobalProperties ipProps = IPGlobalProperties.GetIPGlobalProperties();
// if the ip we want a port on is an 'any' or loopback port we need to exclude all ports that are active on any IP
Func<IPAddress, bool> isIpAnyOrLoopBack = i => IPAddress.Any.Equals(i) ||
IPAddress.IPv6Any.Equals(i) ||
IPAddress.Loopback.Equals(i) ||
IPAddress.IPv6Loopback.
Equals(i);
// get all active ports on specified IP.
List<ushort> excludedPorts = new List<ushort>();
// if a port is open on an 'any' or 'loopback' interface then include it in the excludedPorts
excludedPorts.AddRange(from n in ipProps.GetActiveTcpConnections()
where
n.LocalEndPoint.Port >= rangeStart &&
n.LocalEndPoint.Port <= rangeEnd && (
isIpAnyOrLoopBack(ip) || n.LocalEndPoint.Address.Equals(ip) ||
isIpAnyOrLoopBack(n.LocalEndPoint.Address)) &&
(!includeIdlePorts || n.State != TcpState.TimeWait)
select (ushort)n.LocalEndPoint.Port);
excludedPorts.AddRange(from n in ipProps.GetActiveTcpListeners()
where n.Port >= rangeStart && n.Port <= rangeEnd && (
isIpAnyOrLoopBack(ip) || n.Address.Equals(ip) || isIpAnyOrLoopBack(n.Address))
select (ushort)n.Port);
excludedPorts.AddRange(from n in ipProps.GetActiveUdpListeners()
where n.Port >= rangeStart && n.Port <= rangeEnd && (
isIpAnyOrLoopBack(ip) || n.Address.Equals(ip) || isIpAnyOrLoopBack(n.Address))
select (ushort)n.Port);
excludedPorts.Sort();
for (int port = rangeStart; port <= rangeEnd; port++)
{
if (!excludedPorts.Contains((ushort)port))
{
return port;
}
}
return 0;
}
}
}
NOTE: The Microsoft.VisualStudio.WebHost namespace is contained in the file WebDev.WebHost.dll. This file is in the GAC, but it is not possible to add a reference to this assembly from within Visual Studio.
To add a reference you will need to open your .csproj file in a text editor and add the reference manually.
Look for the ItemGroup that contains the project references and add the following element:
<Reference Include="WebDev.WebHost, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86">
<Private>False</Private>
</Reference>
Reference: http://www.codeproject.com/KB/aspnet/test-with-vs-devserver-2.aspx

Categories

Resources