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

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


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, является неполной. Я не использую его, потому что документ упоминал, что "Примечание: эта тема является предрелизной документацией и может быть изменена в будущих выпусках".
Итак, я решил взглянуть на то, что 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(); } }Это не может быть ответ на ваш вопрос, но я думаю, что люди интересный в вашем вопросе может найти мою работу полезной. Не стесняйтесь улучшать коды. Есть некоторые места, которые вы захотите увеличить.
- вместо жесткого кодирования iisexpress.расположение проблемы, вы можете исправить мой код для чтения из реестра.
- я не включил все аргументы, поддерживаемые iisexpress.exe
- я не делал обработку ошибок. Так что, если процесс 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 и другого сообщения (не могу вспомнить, где сейчас).
- установить
Microsoft.Web.Administrationnuget.- ссылка
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на сервере сборки. Вы можете скопировать один из вашего 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