Created
November 19, 2014 02:35
-
-
Save stevenkuhn/8c92631df887be2ba97a to your computer and use it in GitHub Desktop.
IISExpressFixture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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