以编程方式启动和停止 IIS Express

发布于 2024-10-13 19:31:51 字数 1896 浏览 6 评论 0原文

我正在尝试用 C# 构建一个小型应用程序,它应该启动/停止 IIS Express 工作进程。为此,我想使用 MSDN 上记录的官方“IIS Express API”: http://msdn.microsoft.com/en-us/library/gg418415.aspx

据我了解,API(仅)基于 COM 接口。为了使用这个 COM 接口,我通过 Add Reference -> 添加了对 VS2010 中 COM 库的引用。通讯-> “IIS 安装版本管理器界面”:

到目前为止一切顺利,但下一步是什么?有一个可用的 IIISExprProcessUtility 接口,其中包括启动/停止 IIS 进程的两种“方法”。我必须编写一个实现该接口的类吗?

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();
    }
} 

如您所见,我不是专业开发人员。有人能指出我正确的方向吗? 非常感谢任何帮助。

更新1: 根据建议,我尝试了以下代码,不幸的是它不起作用:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

夜雨飘雪 2024-10-20 19:31:51

我试图做类似的事情。我的结论是微软提供的COM库是不完整的。我不使用它,因为文档提到“注意:本主题是预发布文档,在未来版本中可能会发生变化”。

因此,我决定看看 IISExpressTray.exe 正在做什么。似乎也在做类似的事情。

我反汇编了 IISExpressTray.dll,发现列出所有 IISexpress 进程并停止 IISexpress 进程并没有什么神奇之处。

它不调用该 COM 库。它不会从注册表中查找任何内容。

所以,我最终得到的解决方案非常简单。要启动 IIS Express 进程,我只需使用 Process.Start() 并传入我需要的所有参数。

为了停止 IIS Express 进程,我使用 Reflector 从 IISExpressTray.dll 复制了代码。我看到它只是向目标 IISExpress 进程发送一条 WM_QUIT 消息。

这是我编写的用于启动和停止 IIS Express 进程的类。希望这可以帮助其他人。

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();
    }
}

我不需要列出所有现有的 IIS Express 进程。如果您需要的话,从我在反射器中看到的情况来看,IISExpressTray.dll 所做的是调用 Process.GetProcessByName("iisexpress", ".")

要使用我提供的类,这里有一个我用来测试它的示例程序。

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();
    }
}

这可能不是您问题的答案,但我认为对您的问题感兴趣的人可能会发现我的工作有用。请随意改进代码。有些地方您可能需要增强。

  1. 您可以修复我的代码以从注册表中读取,而不是对 iisexpress.exe 位置进行硬编码。
  2. 我没有包含 iisexpress.exe 支持的所有参数,
  3. 也没有进行错误处理。因此,如果 IISExpress 进程由于某些原因(例如端口已被使用)而无法启动,我不知道。我认为修复它的最简单方法是监视 StandardError 流并在从 StandardError 流中获取任何内容时抛出异常

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
时间海 2024-10-20 19:31:51

虽然为时已晚,但我会回答这个问题。

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;

就是这样。然后您可以在 util 对象上调用 StopProcess 方法。

但是,您必须得到 Microsoft 的通知。

" 版本管理器 API (IIS Express) ;
http://msdn.microsoft.com/en- us/library/gg418429(v=VS.90).aspx

注意:IIS 版本管理器 API
支持 IIS Express
基础设施和无意
直接从您的代码中使用。

Although, it's too late, I'll provide 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;

That's it. Then you can call StopProcess method on util object.

However, you have to get notice 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.

"

呆橘 2024-10-20 19:31:51

此实现适用于以编程方式启动/停止 IIS Express,可以在测试中使用。

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();
        }
    }
}

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();
        }
    }
}
风渺 2024-10-20 19:31:51

我感觉你做事很艰难。从这个问题中获取提示 自动停止/重新启动 ASP.NET 开发Server on Build 并查看是否可以采用相同的流程。

回答你的问题,我认为 pinvoke.net 可能对你有帮助。他们还有很多示例可以帮助您构建解决方案。

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.

北渚 2024-10-20 19:31:51

Harvey Kwok 提供了一个很好的提示,因为我想在运行集成测试用例时拆除和拆除服务。但 Harvey 代码对于 PInvoke 和消息传递来说太长了。

这是一个替代方案。

    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();
    }
}

在我的 MS Test 集成测试套件中,我有

       [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();
    }

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();
    }
長街聽風 2024-10-20 19:31:51

这也是我的解决方案。它运行带有隐藏窗口的 IIS Express。 Manager 类控制多个 IIS Express 实例。

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();
        }            
    }        
}

我需要几个实例。设计了manager类来控制它们。

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();
        }
    }
}

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();
        }
    }
}
很糊涂小朋友 2024-10-20 19:31:51

我想我也会把我的解决方案放在这里。源自 SeongTae Jeong 的解决方案和另一篇文章(现在不记得在哪里了)。

  1. 安装 Microsoft.Web.Administration nuget
  2. 如上所述,引用 IIS Installed Versions Manager Interface COM 类型库。
  3. 添加以下类:

    使用系统;
    使用系统诊断;
    使用系统.IO;
    使用 System.Text.RegularExpressions;
    使用 IISVersionManagerLibrary;
    使用 Microsoft.Web.Administration;
    
    公开课网站
    {
        私有 const string DefaultAppPool = "Clr4IntegratedAppPool";
        私有常量字符串DefaultIISVersion =“8.0”;
    
        私有静态只读随机随机 = new Random();
        私有只读 IIISExpressProcessUtility _iis;
        私有只读字符串_name;
        私有只读字符串_path;
        私有只读 int _port;
        私有只读字符串_appPool;
        私有只读字符串_iisPath;
        私有只读字符串_iisArguments;
        私有只读字符串_iisConfigPath;
        私有uint _iisHandle;
    
        私有网站(字符串路径、字符串名称、int 端口、字符串 appPool、字符串 iisVersion)
        {
            _path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, 路径));
            _名称 = 名称;
            _端口=端口;
            _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);
        }
    
        公共静态网站创建(字符串路径,
            字符串名称 = null,int?端口=空,
            字符串应用程序池=默认应用程序池,
            字符串 iisVersion = 默认IISVersion)
        {
            返回新网站(路径,
                姓名 ?? Guid.NewGuid().ToString("N"),
                港口 ??随机.下一个(30000, 40000),
                应用程序池,iis版本);
        }
    
        公共字符串网址{获取;私人套装; }
    
        公共无效开始()
        {
            使用 (var manager = new ServerManager(_iisConfigPath))
            {
                manager.Sites.Add(_name, "http", string.Format("*:{0}:localhost", _port), _path);
                经理.CommitChanges();
            }
            Process.Start(新的ProcessStartInfo
            {
                文件名 = _iisPath,
                参数 = _iisArguments,
                重定向标准输出 = true,
                UseShellExecute = false
            });
            var startTime = DateTime.Now;
            做
            {
                尝试
                {
                    _iisHandle = _iis.GetRunningProcessForSite(_name, "", _appPool, "");
                }
                抓住 { }
                if (_iisHandle != 0) 中断;
                if ((DateTime.Now - startTime).Seconds >= 10)
                    throw new TimeoutException("启动 IIS Express 超时。");
            而(真);
        }
    
        公共无效停止()
        {
            尝试
            {
                _iis.StopProcess(_iisHandle);
            }
            最后
            {
                使用 (var manager = new ServerManager(_iisConfigPath))
                {
                    var site = manager.Sites[_name];
                    manager.Sites.Remove(site);
                    经理.CommitChanges();
                }
            }
        }
    }
    
  4. 按如下方式设置测试装置。该路径相对于测试套件的 bin 文件夹。

    <前><代码>[测试夹具]
    公开课测试
    {
    私人网站_网站;

    [测试夹具设置]
    公共无效设置()
    {
    _website = Website.Create(@"..\..\..\TestHarness");
    _website.Start();
    }

    [测试夹具拆解]
    公共无效拆解()
    {
    _website.Stop();
    }

    [测试]
    公共无效should_serialize_with_bender()
    {
    new WebClient().UploadString(_website.Url, "hai").ShouldEqual("hai");
    }
    }

如果这也将在构建服务器上运行,那么还有一点。首先,您需要在构建服务器上安装 IIS Express。其次,您必须在构建服务器上创建一个 applicationhost.config。您可以从开发盒中的 C:\Users\\Documents\IISExpress\config\ 下复制一份。需要将其复制到构建服务器运行的用户的相应路径。如果它作为系统运行,则路径将为 C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\。

Figure I'd throw my solution in here too. Derived from the SeongTae Jeong's solution and another post (Can't remember where now).

  1. Install the Microsoft.Web.Administration nuget.
  2. Reference the IIS Installed Versions Manager Interface COM type library as noted above.
  3. 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();
                }
            }
        }
    }
    
  4. Setup your test fixture as follows. The path is relative 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 point if this is going to also run on a build server. First you will need to install IIS Express on the build server. Second, you'll have to create an applicationhost.config on the build server. You can copy one from your dev box under C:\Users\<User>\Documents\IISExpress\config\. It needs to be copied to the corresponding path of the user your build server is running as. If it is running as system then the path would be C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\.

秋心╮凉 2024-10-20 19:31:51

不,您不会继承该接口。您可以使用 new 关键字创建 IISVersionManager 的实例。如何让您获得对 IIISExpressProcessUtility 实例的引用尚不清楚。 MSDN 文档太糟糕了。也许您可以一个,但它看起来并不支持这一点。

No, you don't inherit the interface. You can create an instance of IISVersionManager with the new keyword. How that gets you a reference to an IIISExpressProcessUtility instance is completely unclear. The MSDN docs are awful. Maybe you can new one but it doesn't look like it supports that.

李不 2024-10-20 19:31:51

如果修改Web应用程序的web.config文件,IIS(包括Express)将重新启动应用程序池。这将允许您部署更新的程序集。

修改 web.config 的一种方法是将其复制到新文件,然后将其移回。

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

您可能需要对 IIS Express 进行更多控制,而不仅仅是重新启动应用程序池。但如果这就是您所需要的,那么这将起作用。

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.

思慕 2024-10-20 19:31:51

我采用了不同的解决方案。您可以使用“taskkill”和进程名称简单地终止进程树。
这在本地和 TFS 2013 上完美运行

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

    Process.Start(startInfo);
}

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文