我正在尝试在C#中构建一个小应用程序,它应该启动/停止IIS Express工作进程。为此,我想使用MSDN上记录的官方“IIS Express API”:http://msdn.microsoft.com/en-us/library/gg418415.aspx
据我所知,API仅基于COM接口。为了使用这个COM接口,我通过Add Reference添加了对VS2010中COM库的引用 - > 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)).
答案 0 :(得分:58)
我试图做类似的事情。我得出结论,微软提供的COM库是不完整的。我不使用它,因为该文档提到“注意:本主题是预发布文档,在将来的版本中可能会有所变化”。
所以,我决定看看IISExpressTray.exe正在做什么。它似乎在做类似的事情。
我反汇编了IISExpressTray.dll,发现列出所有IISexpress进程并停止IISexpress进程没有任何魔力。
它不会调用该COM库。它不会从注册表中查找任何内容。
所以,我最终解决的问题非常简单。要启动IIS express进程,我只需使用Process.Start()并传入我需要的所有参数。
要停止IIS表达过程,我使用反射器从IISExpressTray.dll复制了代码。我看到它只是向目标IISExpress进程发送WM_QUIT消息。
这是我编写的用于启动和停止IIS表达过程的类。希望这可以帮助别人。
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 :(得分:13)
虽然为时已晚,但我会回答这个问题。
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;
就是这样。然后你可以在util对象上调用StopProcess方法。
但是,您必须得到Microsoft的注意。
“Version Manager API(IIS Express); http://msdn.microsoft.com/en-us/library/gg418429(v=VS.90).aspx
注意:IIS Version Manager API 支持IIS Express 基础设施和无意 直接从您的代码中使用。 “
答案 2 :(得分:7)
此实现适用于以编程方式启动/停止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 :(得分:3)
我觉得你是在努力做到这一点。从这个问题Automatically stop/restart ASP.NET Development Server on Build中提示一下,看看你是否可以采用相同的过程。
回答你的问题,我认为pinvoke.net可能对您有所帮助。他们还有很多例子可以帮助您构建解决方案。
答案 4 :(得分:3)
Harvey Kwok提供了一个很好的提示,因为我想在运行集成测试用例时撕掉并拆除服务。但是使用PInvoke和消息传递Harvey代码太长了。
这是另一种选择。
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();
}
答案 5 :(得分:1)
不,您不继承该接口。您可以使用 new 关键字创建IISVersionManager的实例。如何让你引用IIISExpressProcessUtility实例是完全不清楚的。 MSDN文档很糟糕。也许你可以 new 一个,但看起来它不支持它。
答案 6 :(得分:1)
如果修改Web应用程序的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,而不仅仅是重新启动应用程序池。但如果这就是你所需要的,那就行了。
答案 7 :(得分:1)
我采用了不同的解决方案。您可以使用“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);
}
答案 8 :(得分:1)
图我也在这里抛出我的解决方案。源自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");
}
}
还有一点,如果这也将在构建服务器上运行。首先,您需要install IIS Express on the build server。其次,您必须在构建服务器上创建applicationhost.config
。您可以在C:\Users\<User>\Documents\IISExpress\config\
下的开发框中复制一个。它需要复制到您的构建服务器运行的用户的相应路径。如果它作为系统运行,则路径为C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\
。
答案 9 :(得分:1)
这也是我的解决方案。它运行带有隐藏窗口的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();
}
}
}