Запуск и остановка 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)).
842   10  

10 ответов:

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

Итак, я решил взглянуть на то, что IISExpressTray.exe это делать. Похоже, он делает подобные вещи.

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

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

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

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

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

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.

" Version Manager API (IIS Express) ; http://msdn.microsoft.com/en-us/library/gg418429(в=против.90).аспн

Примечание: 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 сервер разработки на сборке и посмотреть, если вы можете принять тот же процесс.

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

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

Comments

    Ничего не найдено.