Skip to content

Instantly share code, notes, and snippets.

@stevenkuhn
Created November 19, 2014 02:35
Show Gist options
  • Save stevenkuhn/8c92631df887be2ba97a to your computer and use it in GitHub Desktop.
Save stevenkuhn/8c92631df887be2ba97a to your computer and use it in GitHub Desktop.
IISExpressFixture
public class IISExpressFixture : IDisposable
{
public Uri Server { get; private set; }
public string SitePath { get; private set; }
private readonly Process _process;
public IISExpressFixture()
{
// Run site on a random port to prevent port clashes
int port;
if (!TryFindRandomAvailablePort(10, out port))
{
throw new InvalidOperationException("Unable to find an open random port for IIS Express.");
}
// Make the url available to any test that needs to access site
Server = new Uri("http://localhost:" + port + "/");
// This assumes the website is located in a subdirectory called "web" relative to where the tests are executed.
SitePath = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, @"web")).FullName;
var iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\\IIS Express\\iisexpress.exe";
var arguments = new StringBuilder();
arguments.AppendFormat("/path:{0} ", SitePath);
arguments.AppendFormat("/port:{0} ", port);
_process = Process.Start(new ProcessStartInfo()
{
FileName = iisExpressPath,
Arguments = arguments.ToString(),
RedirectStandardOutput = false,
UseShellExecute = false,
CreateNoWindow = true
});
// Give IIS Express enough time to intialize. This does not mean the site has JITed.
// That will happen when the first request is received.
System.Threading.Thread.Sleep(500);
}
public virtual void Dispose()
{
SendStopMessageToProcess(_process.Id);
_process.Close();
System.Threading.Thread.Sleep(500);
}
internal class NativeMethods
{
// Methods
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}
private static void SendStopMessageToProcess(int pid)
{
try
{
for (var ptr = NativeMethods.GetTopWindow(IntPtr.Zero); ptr != IntPtr.Zero; ptr = NativeMethods.GetWindow(ptr, 2))
{
uint num;
NativeMethods.GetWindowThreadProcessId(ptr, out num);
if (pid == num)
{
var hWnd = new HandleRef(null, ptr);
NativeMethods.PostMessage(hWnd, 0x12, IntPtr.Zero, IntPtr.Zero);
return;
}
}
}
catch (ArgumentException)
{ }
}
private static bool TryFindRandomAvailablePort(int maxAttempts, out int port)
{
port = 0;
if (maxAttempts <= 0)
{
return false;
}
var rnd = new Random();
for (var attempts = 0; attempts < maxAttempts; attempts++)
{
port = rnd.Next(2000, 60000);
try
{
var listener = new TcpListener(IPAddress.Loopback, port);
listener.Start();
listener.Stop();
return true;
}
catch (SocketException)
{ }
finally
{ }
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment