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

аюсь создать небольшое приложение на 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: В соответствии с предложениями я попробовал следующий код, который, к сожалению, не работает:

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

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)

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

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

 Anton Kalcik23 апр. 2017 г., 16:13
Никогда не поздно!
 David V. Corbin21 янв. 2016 г., 16:57
Увы, это не работает с последней (4.5+) .NET .... ошибка CS0656: отсутствует компилятор, требуется член 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'
 SeongTae Jeong22 янв. 2016 г., 11:23
@ DavidV.Corbin Ваша проблема не имеет ничего общего с этим ответом. Иди и найди свой ответ. Должно быть. (К вашему сведению, я успешно скомпилировал его в 4.6.1 .NET)
 Dan Csharpster10 дек. 2015 г., 18:28
Спасибо! Этот код работает нормально для меня, но я думаю, я немного растерялся, что делать дальше. Как я могу выяснить, на каком порту он работает? Как я могу использовать это для запуска сайта, который я разрабатываю?
 ilmatte25 мая 2013 г., 01:00
Спасибо (+1). Компилятор пожаловался и сказал мне изменить первую строку: IISVersionManagerLibrary.IISVersionManager mgr = new IISVersionManagerLibrary.IISVersionManager ();

так как я хочу разорвать сервис, когда выполняю интеграционные тесты. Но коды Харви слишком длинны с 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();
    }
 ZZZ14 июл. 2014 г., 02:25
Вы можете обнаружить, что обычный шаблон IDisposable здесь не используется. Однако ресурс процесса фактически высвобождается в методе ClassCleanup / teardown, и процесс тестирования проходит недолго.

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

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

 Pradeep23 янв. 2011 г., 14:36
Прочитатьstackoverflow.com/questions/1093536/... если вы хотите знать, как работает newing на интерфейсе.
 Hans Passant23 янв. 2011 г., 23:57
Да, извините, но я предупреждал вас об этом. Боюсь, вы сильно опережаете поворот. Crappy MSDN docs на самом деле является главным флагом, программисты, которых Microsoft может нанять, знают, что интерфейс автоматизации важен, но не знают, как его реализовать. COM это то, что ребята с бородой и животом знают, как это сделать. Это плохо документировано, потому что не работает. Модные слова, которые вы хотите избежать, это "CTP" и "Express".
 Hans Passant23 янв. 2011 г., 15:23
Это зависит от COM, все методы доступны через интерфейсы.
 Mike23 янв. 2011 г., 22:51
Большое спасибо за поддержку! Я сделал несколько тестов и обновил свой пост.
 Mike24 янв. 2011 г., 05:22
В любом случае, спасибо за вашу поддержку. Однако, поскольку IIS Express уже выпущен, я все еще надеюсь, что Microsoft завершит / исправит документацию COM на MSDN в ближайшем будущем.

может использоваться из тестов.

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();
        }
    }
}
 orad16 июн. 2016 г., 23:51
Это здорово! Я нашел немного более полную реализацию этогоВот.
 jpmc2608 нояб. 2017 г., 22:38
Environment.Is64BitOperatingSystem это неправильный выбор здесь. Это действительно зависит от разрядности двоичных файлов, для которых создан сайт.

используя «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);
}

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

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

 tomfanning23 янв. 2011 г., 22:47
+1 для pinvoke.net
 Mike23 янв. 2011 г., 22:15
Прадип, спасибо за ваш ответ. Решение из первой ссылки не работает для меня, потому что они убивают процесс с помощью proc.Kill (); pinvoke.net очень интересный, спасибо за совет. К сожалению, я не нашел информации о том, как использовать IIS Express COM API

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

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

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

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

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

УстановитеMicrosoft.Web.Administration NuGet.Ссылка наIIS Installed Versions Manager Interface Библиотека типов COM, как указано выше.

Добавьте следующий класс:

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

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

Решение Вопроса

что предоставленная 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();
    }
}

Возможно, это не ответ на ваш вопрос, но я думаю, что люди, интересующиеся вашим вопросом, могут найти мою работу полезной. Не стесняйтесь улучшать коды. Есть некоторые места, которые вы можете улучшить.

Вместо жесткого определения местоположения iisexpress.exe, вы можете исправить мой код для чтения из реестра.Я не включил все аргументы, поддерживаемые iisexpress.exeЯ не делал обработку ошибок. Итак, если процесс IISExpress не удалось запустить по каким-либо причинам (например, используется порт), я не знаю. Я думаю, что самый простой способ исправить это - отслеживать поток StandardError и выдавать исключение, если я получаю что-либо из потока StandardError.
 Mike25 янв. 2011 г., 05:17
Хорошо, я попробовал, отлично работает. Еще раз спасибо :-)
 Derek Greer20 июл. 2012 г., 16:30
Именно то, что мне было нужно. Спасибо, что поделились этим кодом!
 jameschinnock15 нояб. 2013 г., 16:15
Большое спасибо за то, что поделились этим! Отлично!
 Mike24 янв. 2011 г., 05:15
Вау! Большое спасибо за этот подробный ответ! Я думаю, что пока на MSDN нет полной / правильной документации по COM, это, кажется, идеальное решение. Я попробую, как только вернусь на ПК.
 jpierson18 мар. 2012 г., 18:44
Отлично, стоп-сообщение сработало как шарм. Я запускаю IIS Express из командной строки, но это был недостающий элемент. Спасибо!

Ваш ответ на вопрос