Starting and stopping IIS Express programmatically

2019-01-06 08:38发布

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)).

10条回答
smile是对你的礼貌
2楼-- · 2019-01-06 09:14

This implementation works for starting/stopping IIS Express programmatically, 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();
        }
    }
}
查看更多
The star\"
3楼-- · 2019-01-06 09:14

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

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 want more control over IIS Express than simply restarting the app pool. But if that's all you need, this will work.

查看更多
孤傲高冷的网名
4楼-- · 2019-01-06 09:19

I feel you are doing it in a hard way. Take a hint from this question Automatically stop/restart ASP.NET Development Server on Build and see if you can adopt the same process.

Answering your question, I think pinvoke.net might help you. They have lot of examples as well which can help you build your solution.

查看更多
Melony?
5楼-- · 2019-01-06 09:19

Harvey Kwok had provided a good hint, since I want to tear up and tear down the service when running integration test cases. But Harvey codes is too long with PInvoke and messaging.

Here's 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 integration test suit 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();
    }
查看更多
叼着烟拽天下
6楼-- · 2019-01-06 09:19

I have adopted a different solution. You can simply kill the process tree using "taskkill" and the name of the process. This works perfectly locally and on TFS 2013

public static void FinalizeIis()
{
    var startInfo = new ProcessStartInfo
    {
        UseShellExecute = false,
        Arguments = string.Format("/F /IM iisexpress.exe"),
        FileName = "taskkill"
    };

    Process.Start(startInfo);
}
查看更多
虎瘦雄心在
7楼-- · 2019-01-06 09:25

Here is my solution too. It runs IIS Express with hidden windows. Manager class controls several IIS Express instances.

class IISExpress
{               
    private const string IIS_EXPRESS = @"C:\Program Files\IIS Express\iisexpress.exe";        

    private Process process;

    IISExpress(Dictionary<string, string> args)
    {
        this.Arguments = new ReadOnlyDictionary<string, string>(args);

        string argumentsInString = args.Keys
            .Where(key => !string.IsNullOrEmpty(key))
            .Select(key => $"/{key}:{args[key]}")
            .Aggregate((agregate, element) => $"{agregate} {element}");

        this.process = Process.Start(new ProcessStartInfo()
        {
            FileName = IIS_EXPRESS,
            Arguments = argumentsInString,
            WindowStyle = ProcessWindowStyle.Hidden                
        });
    }

    public IReadOnlyDictionary<string, string> Arguments { get; protected set; }        

    public static IISExpress Start(Dictionary<string, string> args)
    {
        return new IISExpress(args);
    }

    public void Stop()
    {
        try
        {
            this.process.Kill();
            this.process.WaitForExit();
        }
        finally
        {
            this.process.Close();
        }            
    }        
}

I need several instances. Designed manager class to control them.

static class IISExpressManager
{
    /// <summary>
    /// All started IIS Express hosts
    /// </summary>
    private static List<IISExpress> hosts = new List<IISExpress>();

    /// <summary>
    /// Start IIS Express hosts according to the config file
    /// </summary>
    public static void StartIfEnabled()
    {
        string enableIISExpress = ConfigurationManager.AppSettings["EnableIISExpress"]; // bool value from config file
        string pathToConfigFile = ConfigurationManager.AppSettings["IISExpressConfigFile"]; // path string to iis configuration file
        string quotedPathToConfigFile = '"' + pathToConfigFile + '"';

        if (bool.TryParse(enableIISExpress, out bool isIISExpressEnabled) 
            && isIISExpressEnabled && File.Exists(pathToConfigFile))
        {                
            hosts.Add(IISExpress.Start(
                new Dictionary<string, string> {
                    {"systray", "false"},
                    {"config", quotedPathToConfigFile},
                    {"site", "Site1" }                        
                }));

            hosts.Add(IISExpress.Start(
                new Dictionary<string, string> {
                    {"systray", "false"},
                    { "config", quotedPathToConfigFile},
                    {"site", "Site2" }
                }));

        }
    }

    /// <summary>
    /// Stop all started hosts
    /// </summary>
    public static void Stop()
    {
        foreach(var h in hosts)
        {
            h.Stop();
        }
    }
}
查看更多
登录 后发表回答