Запуск и остановка IIS Express программным способом

Я пытаюсь создать небольшое приложение на C# , которое должно запустить / остановить рабочий процесс IIS Express. Для этого я хочу использовать официальный "IIS Express API", который документирован на MSDN:http://msdn.microsoft.com/en-us/library/gg418415.aspx

насколько я понимаю, API основан (только) на COM-интерфейсах. Чтобы использовать эти com-интерфейсы, я добавил ссылку на библиотеку COM в VS2010 через Add Reference - > 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

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 ответов


Я пытался сделать то же самое. Я пришел к выводу, что библиотека COM, предоставляемая Microsoft, является неполной. Я не использую его, потому что doc упомянул, что "Примечание: эта тема является предварительной документацией и может быть изменена в будущих выпусках".

Итак, я решил взглянуть на то, что IISExpressTray.exe делает. Похоже, он делает то же самое.

я разбираю IISExpressTray.dll и обнаружил, что нет магии в перечислении всех IISexpress процессы и остановка процесса IISexpress.

это не называется библиотекой COM. Он ничего не ищет из реестра.

Итак, решение, которое я закончил, очень простое. Чтобы запустить процесс IIS express, я просто использую Process.Start () и передайте все параметры, которые мне нужны.

чтобы остановить процесс IIS express, я скопировал код из IISExpressTray.dll с помощью рефлектора. Я видел, что он просто отправляет сообщение WM_QUIT целевому IISExpress процесс.

вот класс, который я написал, чтобы запустить и остановить процесс 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. Если вам это нужно, из того, что я видел в отражателе, что Iisexpressray.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.расположение проблемы, вы можете исправить мой код для чтения из реестра.
  2. я не включил все аргументы, поддерживаемые iisexpress.exe
  3. я не занимался обработкой ошибок. Итак, если процесс IISExpress не удалось запустить по некоторым причинам (например, порт используется), я не знаю. Я думаю, что самый простой способ исправить это-контролировать поток StandardError и выбрасывать исключение, если я получаю что-либо из потока StandardError

хотя, это слишком поздно, я дам ответ на этот вопрос.

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;

вот и все. Затем вы можете вызвать метод StopProcess на объекте util.

однако вы должны получить уведомление от Microsoft.

" API диспетчера версий (IIS Express) ; http://msdn.microsoft.com/en-us/library/gg418429 (v=VS.90).aspx

Примечание: API диспетчера версий IIS поддерживает IIS Express инфраструктура и is не предназначить для использования непосредственно из кода. "


эта реализация работает для запуска/остановки 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();
        }
    }
}

Я чувствую, что вы делаете это в нелегкий путь. Возьмите подсказку из этого вопроса автоматически остановить / перезапустить ASP.NET сервер разработки на Build и посмотрите, можете ли вы принять тот же процесс.

отвечая на ваш вопрос, я думаю pinvoke.net может помочь вам. У них также есть много примеров, которые могут помочь вам построить свое решение.


Харви Квок дал хороший намек, так как я хочу разорвать и разорвать службу при запуске интеграционных тестовых случаев. Но Harvey codes слишком долго работает с 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();
    }

нет, вы не наследуете интерфейс. Вы можете создать экземпляр IISVersionManager с помощью new ключевое слово. Как это дает вам ссылку на экземпляр IIISExpressProcessUtility, совершенно неясно. Документы MSDN ужасны. Может быть, вы можете new но не похоже, что он поддерживает это.


Если вы измените веб.файл конфигурации веб-приложения IIS (включая Express) перезапустит пул приложений. Это позволит развернуть обновленные сборки.

один из способов изменить web.конфиг скопировать его в новый файл, а затем вернуть его обратно.

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

может потребоваться больший контроль над IIS Express, чем просто перезапуск пула приложений. Но если это все, что вам нужно, это сработает.


Я принял другое решение. Вы можете просто убить дерево процессов, используя "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);
}

рисунок я бы бросил свое решение здесь тоже. Получено из решения SeongTae Jeong и другого сообщения (не могу вспомнить, где сейчас).

  1. установить Microsoft.Web.Administration NuGet для.
  2. ссылка IIS Installed Versions Manager Interface библиотека типов COM, Как указано выше.
  3. добавить следующий класс:

    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. настройте ваше приспособление теста следующим образом. Путь относительно папки bin вашего теста комплект.

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

и еще один момент, если это будет работать и на сервере сборки. Сначала вам нужно будет установите IIS Express на сервере сборки. Во-вторых, вам придется создать applicationhost.config на сервере сборки. Вы можете скопировать его из окна dev в разделе C:\Users\<User>\Documents\IISExpress\config\. Его необходимо скопировать в соответствующий путь пользователя, от имени которого работает сервер сборки. Если он работает как система, то путь будет C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\.


вот мое решение тоже. Он запускает 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();
        }            
    }        
}

мне нужно несколько экземпляров. Разработан класс менеджера для управления ими.

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