Запуск и остановка IIS Express программно - PullRequest
56 голосов
/ 23 января 2011

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

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

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 ]

58 голосов
/ 24 января 2011

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

Итак, я решил взглянуть на то, что 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.Если вам это нужно, то, что я увидел в отражателе, что 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
13 голосов
/ 30 апреля 2011

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

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 инфраструктура и не предназначена для использования непосредственно из вашего кода. «

7 голосов
/ 10 декабря 2013

Эта реализация работает для программного запуска / остановки 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();
        }
    }
}
3 голосов
/ 10 июля 2014

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

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

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

1 голос
/ 23 марта 2018

Вот и моё решение. Он запускает 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();
        }
    }
}
1 голос
/ 19 апреля 2015

Думаю, здесь я тоже добавлю свое решение. Получено из решения 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 на сервере сборки. Вы можете скопировать один из вашего окна разработчика под C:\Users\<User>\Documents\IISExpress\config\. Его необходимо скопировать в соответствующий путь пользователя, от имени которого работает сервер сборки. Если он работает как система, путь будет C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\.

1 голос
/ 22 октября 2013

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

Если вы измените файл 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, чем просто перезапуск пула приложений. Но если это все, что вам нужно, это сработает.

1 голос
/ 23 января 2011

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

...