Starting and stopping IIS Express software

I am trying to create a small C # application that should start / stop the IIS Express workflow. For this, I want to use the official "IIS Express API", which is documented on MSDN: http://msdn.microsoft.com/en-us/library/gg418415.aspx

As far as I understand, the API is based (only) on COM interfaces. To use these COM interfaces, I added a link to the COM library in VS2010 through the link "Add Link" → "COM" - "IIS Interface Manager Interface":

So far so good, but what's next? There is an IIISExprProcessUtility interface that includes two “methods” for starting / stopping the IIS process. Should I write a class that implements this interface?

 public class test : IISVersionManagerLibrary.IIISExprProcessUtility { public string ConstructCommandLine(string bstrSite, string bstrApplication, string bstrApplicationPool, string bstrConfigPath) { throw new NotImplementedException(); } public uint GetRunningProcessForSite(string bstrSite, string bstrApplication, string bstrApplicationPool, string bstrConfigPath) { throw new NotImplementedException(); } public void StopProcess(uint dwPid) { throw new NotImplementedException(); } } 

As you can see, I am not a professional developer. Can someone point me in the right direction. Any help is appreciated.

Update 1: As suggested, I tried the following code, which unfortunately does not work:

alt text Ok, it can be created, but I don’t see how to use this object ...

alt text

alt text

 IISVersionManagerLibrary.IIISExpressProcessUtility test3 = (IISVersionManagerLibrary.IIISExpressProcessUtility) Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("5A081F08-E4FA-45CC-A8EA-5C8A7B51727C"))); Exception: Retrieving the COM class factory for component with CLSID {5A081F08-E4FA-45CC-A8EA-5C8A7B51727C} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)). 
+52
c # iis com com-interop iis-express
Jan 23 '11 at 3:12
source share
9 answers

I tried to do this. I came to the conclusion that the COM library provided by Microsoft is incomplete. I do not use it because the document mentions that "Note: this section is a preliminary version of the documentation and is subject to change in future releases."

So, I decided to see what IISExpressTray.exe does. He seems to be doing similar things.

I parsed IISExpressTray.dll and found that there is no magic in listing all IISexpress processes and stopping the IISexpress process.

It does not call this COM library. It does not look for anything from the registry.

So the solution that I have finished is very simple. To start the IIS express process, I just use Process.Start () and pass all the parameters I need.

To stop the IIS express delivery process, I copied the code from IISExpressTray.dll using a reflector. I saw that it just sends a WM_QUIT message to the target IISExpress process.

Here is a class I wrote to start and stop the IIS express process. Hope this helps someone else.

 class IISExpress { 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); } public static void SendStopMessageToProcess(int PID) { try { for (IntPtr ptr = NativeMethods.GetTopWindow(IntPtr.Zero); ptr != IntPtr.Zero; ptr = NativeMethods.GetWindow(ptr, 2)) { uint num; NativeMethods.GetWindowThreadProcessId(ptr, out num); if (PID == num) { HandleRef hWnd = new HandleRef(null, ptr); NativeMethods.PostMessage(hWnd, 0x12, IntPtr.Zero, IntPtr.Zero); return; } } } catch (ArgumentException) { } } const string IIS_EXPRESS = @"C:\Program Files\IIS Express\iisexpress.exe"; const string CONFIG = "config"; const string SITE = "site"; const string APP_POOL = "apppool"; Process process; IISExpress(string config, string site, string apppool) { Config = config; Site = site; AppPool = apppool; StringBuilder arguments = new StringBuilder(); if (!string.IsNullOrEmpty(Config)) arguments.AppendFormat("/{0}:{1} ", CONFIG, Config); if (!string.IsNullOrEmpty(Site)) arguments.AppendFormat("/{0}:{1} ", SITE, Site); if (!string.IsNullOrEmpty(AppPool)) arguments.AppendFormat("/{0}:{1} ", APP_POOL, AppPool); process = Process.Start(new ProcessStartInfo() { FileName = IIS_EXPRESS, Arguments = arguments.ToString(), RedirectStandardOutput = true, UseShellExecute = false }); } public string Config { get; protected set; } public string Site { get; protected set; } public string AppPool { get; protected set; } public static IISExpress Start(string config, string site, string apppool) { return new IISExpress(config, site, apppool); } public void Stop() { SendStopMessageToProcess(process.Id); process.Close(); } } 

I do not need to list the entire existing IIS process. If you need it, what I saw in the reflector, what IISExpressTray.dll does is call Process.GetProcessByName("iisexpress", ".")

To use the class I provided, here is an example program that I used to test it.

 class Program { static void Main(string[] args) { Console.Out.WriteLine("Launching IIS Express..."); IISExpress iis1 = IISExpress.Start( @"C:\Users\Administrator\Documents\IISExpress\config\applicationhost.config", @"WebSite1(1)", @"Clr4IntegratedAppPool"); IISExpress iis2 = IISExpress.Start( @"C:\Users\Administrator\Documents\IISExpress\config\applicationhost2.config", @"WebSite1(1)", @"Clr4IntegratedAppPool"); Console.Out.WriteLine("Press ENTER to kill"); Console.In.ReadLine(); iis1.Stop(); iis2.Stop(); } } 

This may not be the answer to your question, but I think that people who are interesting in your question may find my work useful. Feel free to upgrade the codes. There are some places you can improve.

  • Instead of hard coding the location of iisexpress.exe, you can fix my code to read from the registry.
  • I did not include all the arguments supported by iisexpress.exe
  • I did not handle error handling. So, if the IISExpress process did not start for some reason (for example, the port is used), I do not know. I think the easiest way to fix this is to control the StandardError stream and throw an exception if I get anything from the StandardError stream.
+52
Jan 24 2018-11-11T00:
source share

Although, too late, I will give an answer to this question.

 IISVersionManagerLibrary.IISVersionManager mgr = new IISVersionManagerLibrary.IISVersionManagerClass(); IISVersionManagerLibrary.IIISVersion ver = mgr.GetVersionObject("7.5", IISVersionManagerLibrary.IIS_PRODUCT_TYPE.IIS_PRODUCT_EXPRESS); object obj1 = ver.GetPropertyValue("expressProcessHelper"); IISVersionManagerLibrary.IIISExpressProcessUtility util = obj1 as IISVersionManagerLibrary.IIISExpressProcessUtility; 

What is it. Then you can call the StopProcess method on the util object.

However, you should receive a notification from Microsoft.

"Version Manager API (IIS Express); http://msdn.microsoft.com/en-us/library/gg418429(v=VS.90).aspx

Note. The IIS Version Manager API supports the IIS Express infrastructure and is not intended to be used directly from your code. "

+13
Apr 30 '11 at 10:45
source share

This implementation works to start / stop IIS Express programmatically; it can be used from tests.

 public class IisExpress : IDisposable { private Boolean _isDisposed; private Process _process; public void Dispose() { Dispose(true); } public void Start(String directoryPath, Int32 port) { var iisExpressPath = DetermineIisExpressPath(); var arguments = String.Format( CultureInfo.InvariantCulture, "/path:\"{0}\" /port:{1}", directoryPath, port); var info = new ProcessStartInfo(iisExpressPath) { WindowStyle = ProcessWindowStyle.Normal, ErrorDialog = true, LoadUserProfile = true, CreateNoWindow = false, UseShellExecute = false, Arguments = arguments }; var startThread = new Thread(() => StartIisExpress(info)) { IsBackground = true }; startThread.Start(); } protected virtual void Dispose(Boolean disposing) { if (_isDisposed) { return; } if (disposing) { if (_process.HasExited == false) { _process.Kill(); } _process.Dispose(); } _isDisposed = true; } private static String DetermineIisExpressPath() { String iisExpressPath; iisExpressPath = Environment.GetFolderPath(Environment.Is64BitOperatingSystem ? Environment.SpecialFolder.ProgramFilesX86 : Environment.SpecialFolder.ProgramFiles); iisExpressPath = Path.Combine(iisExpressPath, @"IIS Express\iisexpress.exe"); return iisExpressPath; } private void StartIisExpress(ProcessStartInfo info) { try { _process = Process.Start(info); _process.WaitForExit(); } catch (Exception) { Dispose(); } } } 
+6
Dec 10 '13 at
source share

I feel that you are doing it with difficulty. Take a hint at this question. Automatically stop / restart ASP.NET Development Server on the assembly and see if you can accept the same process.

Answering your question, I think pinvoke.net can help you. They also have many examples that can help you create your solution.

+3
Jan 23 '11 at 5:20
source share

Harvey Kwok gave a good hint since I want to tear up and tear up the service when running integration tests. But Harvey codes are too long with PInvoke and messaging.

Here is an alternative.

  public class IisExpressAgent { public void Start(string arguments) { ProcessStartInfo info= new ProcessStartInfo(@"C:\Program Files (x86)\IIS Express\iisexpress.exe", arguments) { // WindowStyle= ProcessWindowStyle.Minimized, }; process = Process.Start(info); } Process process; public void Stop() { process.Kill(); } } 

And in my test for integration with MS Test I have

  [ClassInitialize()] public static void MyClassInitialize(TestContext testContext) { iis = new IisExpressAgent(); iis.Start("/site:\"WcfService1\" /apppool:\"Clr4IntegratedAppPool\""); } static IisExpressAgent iis; //Use ClassCleanup to run code after all tests in a class have run [ClassCleanup()] public static void MyClassCleanup() { iis.Stop(); } 
+3
Jul 10 '14 at 0:10
source share

No, you do not inherit the interface. You can create an instance of IISVersionManager with a new keyword. How you get the reference to the IIISExpressProcessUtility instance is completely unclear. MSDN docs are terrible. You may be new, but it doesn't seem to support this.

+1
Jan 23 '11 at 10:16
source share

If you modify the web.config file of the web application, IIS (including Express) will restart the application pool. This will allow you to deploy updated builds.

One way to modify web.config is to copy it to a new file and then move it back.

 copy /Y path/web.config path/web_touch.config move /Y path/web_touch.config path/web.config 

You may need more control over IIS Express than just restarting the application pool. But if that’s all you need, it will work.

+1
Aug 05 '13 at 16:36
source share

I made another decision. You can simply kill the process tree using taskkill and the process name. This works fine on TFS 2013 as well.

 public static void FinalizeIis() { var startInfo = new ProcessStartInfo { UseShellExecute = false, Arguments = string.Format("/F /IM iisexpress.exe"), FileName = "taskkill" }; Process.Start(startInfo); } 
+1
Oct 22 '13 at 12:05 on
source share

Picture. I will also throw my decision. Obtained from SeongTae Jeong's solution and another message (I don’t remember where now).

  • Install Microsoft.Web.Administration nuget .
  • Link to the IIS Installed Versions Manager Interface COM COM Type Library as described above.
  • Add the following class:

     using System; using System.Diagnostics; using System.IO; using System.Text.RegularExpressions; using IISVersionManagerLibrary; using Microsoft.Web.Administration; public class Website { private const string DefaultAppPool = "Clr4IntegratedAppPool"; private const string DefaultIISVersion = "8.0"; private static readonly Random Random = new Random(); private readonly IIISExpressProcessUtility _iis; private readonly string _name; private readonly string _path; private readonly int _port; private readonly string _appPool; private readonly string _iisPath; private readonly string _iisArguments; private readonly string _iisConfigPath; private uint _iisHandle; private Website(string path, string name, int port, string appPool, string iisVersion) { _path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, path)); _name = name; _port = port; _appPool = appPool; _iis = (IIISExpressProcessUtility)new IISVersionManager() .GetVersionObject(iisVersion, IIS_PRODUCT_TYPE.IIS_PRODUCT_EXPRESS) .GetPropertyValue("expressProcessHelper"); var commandLine = _iis.ConstructCommandLine(name, "", appPool, ""); var commandLineParts = new Regex("\\\"(.*?)\\\" (.*)").Match(commandLine); _iisPath = commandLineParts.Groups[1].Value; _iisArguments = commandLineParts.Groups[2].Value; _iisConfigPath = new Regex("\\/config:\\\"(.*?)\\\"").Match(commandLine).Groups[1].Value; Url = string.Format("http://localhost:{0}/", _port); } public static Website Create(string path, string name = null, int? port = null, string appPool = DefaultAppPool, string iisVersion = DefaultIISVersion) { return new Website(path, name ?? Guid.NewGuid().ToString("N"), port ?? Random.Next(30000, 40000), appPool, iisVersion); } public string Url { get; private set; } public void Start() { using (var manager = new ServerManager(_iisConfigPath)) { manager.Sites.Add(_name, "http", string.Format("*:{0}:localhost", _port), _path); manager.CommitChanges(); } Process.Start(new ProcessStartInfo { FileName = _iisPath, Arguments = _iisArguments, RedirectStandardOutput = true, UseShellExecute = false }); var startTime = DateTime.Now; do { try { _iisHandle = _iis.GetRunningProcessForSite(_name, "", _appPool, ""); } catch { } if (_iisHandle != 0) break; if ((DateTime.Now - startTime).Seconds >= 10) throw new TimeoutException("Timeout starting IIS Express."); } while (true); } public void Stop() { try { _iis.StopProcess(_iisHandle); } finally { using (var manager = new ServerManager(_iisConfigPath)) { var site = manager.Sites[_name]; manager.Sites.Remove(site); manager.CommitChanges(); } } } } 
  • Install the test device as follows. The path refers to the bin folder of your test suite.

     [TestFixture] public class Tests { private Website _website; [TestFixtureSetUp] public void Setup() { _website = Website.Create(@"..\..\..\TestHarness"); _website.Start(); } [TestFixtureTearDown] public void TearDown() { _website.Stop(); } [Test] public void should_serialize_with_bender() { new WebClient().UploadString(_website.Url, "hai").ShouldEqual("hai"); } } 

And one more thing, if it will also be running on the build server. First you need to install IIS Express on the build server . Secondly, you need to create applicationhost.config on the build server. You can copy it from your dev window to C:\Users\<User>\Documents\IISExpress\config\ . It must be copied to the appropriate user path on which your build server is running. If it works as a system, then the path will be C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\ .

+1
Apr 18 '15 at 21:13
source share



All Articles