Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Starting and stopping IIS Express programmatically

I am trying to build a small application in C# which should start/stop an IIS Express worker process. For this purpose 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 this COM interfaces I've added a reference to the COM library in VS2010 via Add Reference -> COM -> "IIS Installed Versions Manager Interface":

So far so good, but what's next? There is an IIISExprProcessUtility interface available which includes the the two "methods" to start/stop an IIS process. Do I have to write a class which 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'm not a professional developer. Can someone point me in the right direction. Any help is greatly appreciated.

Update 1: According to the suggestions I've tried the following code which unfortunately doesn't work:

alt text Ok, it can be instantiated but I cannot 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)). 
like image 592
Mike Avatar asked Jan 23 '11 03:01

Mike


People also ask

How do I stop IIS Express service?

Closing IIS Express By default Visual Studio places the IISExpress icon in your system tray at the lower right hand side of your screen, by the clock. You can right click it and choose exit. If you don't see the icon, try clicking the small arrow to view the full list of icons in the system tray.

How do I change IIS Express settings?

Configure IIS express on visual studioSelect the web application project and open properties -> select the web tab -> under server's select IIS express-> Specify the project URL. Now open the project folder and . vs folder (Hidden) -> Config -> applicationhost.


1 Answers

I was trying to do similar thing. I concluded that the COM library provided by Microsoft is incomplete. I don't use it because the doc mentioned that "Note: This topic is pre-release documentation and is subject to change in future releases".

So, I decided to take a look at what IISExpressTray.exe is doing. It seems to be doing similar things.

I disassemble the IISExpressTray.dll and found that there is no magic in listing out all the IISexpress processes and stoping the IISexpress process.

It doesn't call that COM library. It doesn't lookup anything from registry.

So, the solution I ended up is very simple. To start an IIS express process, I just use Process.Start() and pass in all the parameters I need.

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

Here is the class I wrote to start and stop an IIS express process. Hope this can help somebody 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 don't need to list all the existing IIS express process. If you need that, from what I saw in the reflector, what IISExpressTray.dll does is to call Process.GetProcessByName("iisexpress", ".")

To use the class I provided, here is a sample program 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 an answer to your question but I think people interesting in your question may find my work useful. Feel free to improve the codes. There are some places that you might want to enhance.

  1. Instead of hardcoding the iisexpress.exe location, you can fix my code to read from the registry.
  2. I didn't include all the arguments supported by iisexpress.exe
  3. I didn't do error handling. So, if the IISExpress process failed to start for some reasons (e.g. port is in used), I don't know. I think the easiest way to fix it is to monitor the StandardError stream and throw exception if I get anything from StandardError stream
like image 192
Harvey Kwok Avatar answered Oct 10 '22 15:10

Harvey Kwok