una - web forms c# ejemplos
Inicio y detenciĆ³n de IIS Express mediante programaciĆ³n (10)
Estoy intentando crear una pequeña aplicación en C # que debería iniciar / detener un proceso de trabajo de IIS Express. Para este propósito, quiero utilizar la "API Express IIS" oficial que está documentada en MSDN: http://msdn.microsoft.com/en-us/library/gg418415.aspx
Por lo que yo entiendo, la API se basa (solo) en las interfaces COM. Para utilizar estas interfaces COM, he agregado una referencia a la biblioteca COM en VS2010 a través de Agregar referencia -> COM -> "Interfaz de administrador de versiones instaladas de IIS":
Hasta aquí todo bien, pero ¿qué sigue? Existe una interfaz IIISExprProcessUtility
disponible que incluye los dos "métodos" para iniciar / detener un proceso de IIS. ¿Debo escribir una clase que implemente esta interfaz?
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();
}
}
Como puede ver, no soy un desarrollador profesional. Alguien me puede apuntar en la dirección correcta. Cualquier ayuda es muy apreciada.
Actualización 1: de acuerdo con las sugerencias que he probado el siguiente código que lamentablemente no funciona:
Bien, se puede instanciar pero no puedo ver cómo usar este objeto ...
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)).
Aquí está mi solución también. Ejecuta IIS Express con ventanas ocultas. La clase Manager controla varias instancias de 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();
}
}
}
Necesito varias instancias. Clase de gerente diseñado para controlarlos.
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();
}
}
}
Aunque es demasiado tarde, daré una respuesta a esta pregunta.
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;
Eso es. Luego puede llamar al método StopProcess en el objeto util.
Sin embargo, debe recibir un aviso de Microsoft.
"Version Manager API (IIS Express); http://msdn.microsoft.com/en-us/library/gg418429(v=VS.90).aspx
Nota: La API de IIS Version Manager es compatible con la infraestructura de IIS Express y no está destinada a ser utilizada directamente desde su código. "
Esta implementación funciona para iniciar / detener IIS Express mediante programación, se puede usar desde pruebas.
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();
}
}
}
Figura que arrojaría mi solución aquí también. Derivado de la solución de SeongTae Jeong y otra publicación (No recuerdo dónde ahora).
- Instale el nuget
Microsoft.Web.Administration
. - Consulte la biblioteca de tipo COM de la
IIS Installed Versions Manager Interface
como se indicó anteriormente. Agregue la siguiente clase:
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(); } } } }
Configure su dispositivo de prueba de la siguiente manera. La ruta es relativa a la carpeta bin de su suite de pruebas.
[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"); } }
Y un punto más si esto también se va a ejecutar en un servidor de compilación. Primero, deberá instalar IIS Express en el servidor de compilación . En segundo lugar, deberá crear un applicationhost.config
en el servidor de compilación. Puede copiar uno de su cuadro de desarrollo en C:/Users/<User>/Documents/IISExpress/config/
. Debe copiarse en la ruta correspondiente del usuario en el que se ejecuta su servidor de compilación. Si se está ejecutando como sistema, la ruta sería C:/Windows/System32/config/systemprofile/Documents/IISExpress/config/
.
Harvey Kwok me ha dado una buena pista, ya que quiero romper y romper el servicio cuando ejecuto casos de prueba de integración. Pero los códigos de Harvey son demasiado largos con PInvoke y la mensajería.
Aquí hay una alternativa.
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();
}
}
Y en mi prueba de integración con MS Test, tengo
[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();
}
He adoptado una solución diferente. Simplemente puede matar el árbol de proceso usando "taskkill" y el nombre del proceso. Esto funciona a nivel local y en TFS 2013
public static void FinalizeIis()
{
var startInfo = new ProcessStartInfo
{
UseShellExecute = false,
Arguments = string.Format("/F /IM iisexpress.exe"),
FileName = "taskkill"
};
Process.Start(startInfo);
}
Intentaba hacer algo similar. Llegué a la conclusión de que la biblioteca COM proporcionada por Microsoft está incompleta. No lo uso porque el documento menciona que "Nota: este tema es una documentación preliminar y está sujeto a cambios en versiones futuras".
Entonces, decidí echar un vistazo a lo que IISExpressTray.exe está haciendo. Parece estar haciendo cosas similares.
Desarmo el IISExpressTray.dll y descubrí que no es mágico enumerar todos los procesos de IISexpress y detener el proceso de IISexpress.
No llama a esa biblioteca COM. No busca nada desde el registro.
Entonces, la solución que terminé es muy simple. Para iniciar un proceso express de IIS, solo uso Process.Start () y paso todos los parámetros que necesito.
Para detener un proceso de IIS express, copié el código de IISExpressTray.dll usando el reflector. Vi que simplemente envía un mensaje WM_QUIT al proceso IISExpress de destino.
Aquí está la clase que escribí para iniciar y detener un proceso urgente de IIS. Espero que esto pueda ayudar a alguien más.
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();
}
}
No necesito enumerar todo el proceso exprés de IIS existente. Si lo necesita, por lo que vi en el reflector, lo que hace IISExpressTray.dll es llamar a Process.GetProcessByName("iisexpress", ".")
Para usar la clase que proporcioné, aquí hay un programa de muestra que usé para probarlo.
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();
}
}
Puede que esta no sea la respuesta a su pregunta, pero creo que las personas interesantes en su pregunta pueden encontrar útil mi trabajo. Siéntase libre de mejorar los códigos. Hay algunos lugares que quizás desee mejorar.
- En lugar de codificar de forma rígida la ubicación de iisexpress.exe, puede arreglar mi código para leer desde el registro.
- No incluí todos los argumentos soportados por iisexpress.exe
- No hice el manejo de errores. Entonces, si el proceso de IISExpress no pudo iniciarse por alguna razón (por ejemplo, el puerto está en uso), no lo sé. Creo que la forma más sencilla de solucionarlo es supervisar la transmisión StandardError y lanzar una excepción si obtengo algo de la transmisión StandardError.
No, no heredas la interfaz. Puede crear una instancia de IISVersionManager con la nueva palabra clave. La forma en que obtiene una referencia a una instancia de IIISExpressProcessUtility no está del todo clara. Los documentos de MSDN son horribles. Tal vez puedas uno nuevo pero no parece que lo soporte.
Si modifica el archivo web.config de la aplicación web, IIS (incluido Express) reiniciará el grupo de aplicaciones. Esto le permitirá implementar conjuntos actualizados.
Una forma de modificar web.config es copiarlo en un nuevo archivo y luego volverlo a mover.
copy /Y path/web.config path/web_touch.config
move /Y path/web_touch.config path/web.config
Es posible que desee tener más control sobre IIS Express que simplemente reiniciar el grupo de aplicaciones. Pero si eso es todo lo que necesitas, esto funcionará.
Siento que lo estás haciendo de una manera difícil. Dé una pista de esta pregunta: detenga / reinicie ASP.NET Development Server en Build automáticamente y vea si puede adoptar el mismo proceso.
Respondiendo a su pregunta, creo que pinvoke.net podría ayudarlo. También tienen muchos ejemplos que pueden ayudarlo a construir su solución.