J'essaie de créer une petite application en C # qui devrait démarrer/arrêter un processus de travail IIS Express. Pour ce faire, je souhaite utiliser "l'API IIS Express" officielle, documentée sur MSDN: http://msdn.Microsoft.com/en-us/library/gg418415.aspx
Autant que je sache, l'API est basée (uniquement) sur les interfaces COM. Pour utiliser ces interfaces COM, j'ai ajouté une référence à la bibliothèque COM dans VS2010 via Ajouter une référence -> COM -> "Interface du gestionnaire de versions installées IIS":
Jusqu'ici tout va bien, mais quelle est la prochaine? Il existe une interface IIISExprProcessUtility
qui inclut les deux "méthodes" pour démarrer/arrêter un processus IIS. Dois-je écrire une classe qui implémente cette interface?
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();
}
}
Comme vous pouvez le constater, je ne suis pas un développeur professionnel. Quelqu'un peut-il me diriger dans la bonne direction ... Toute aide est grandement appréciée.
Mise à jour 1: Suivant les suggestions, j'ai essayé le code suivant qui ne fonctionne malheureusement pas:
Ok, il peut être instancié mais je ne vois pas comment utiliser cet objet ...
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)).
J'essayais de faire la même chose. J'ai conclu que la bibliothèque COM fournie par Microsoft est incomplète. Je ne l'utilise pas parce que le document mentionnait "Remarque: cette rubrique est une documentation préliminaire pouvant être modifiée dans les prochaines versions".
J'ai donc décidé de jeter un coup d'œil à ce que fait IISExpressTray.exe. Il semble faire des choses similaires.
Je démonte IISExpressTray.dll et constate qu’il n’ya pas de magie dans la liste de tous les processus IISexpress et que l’arrêt du processus IISexpress.
Il n'appelle pas cette bibliothèque COM. Il ne cherche rien dans le registre.
Donc, la solution que j'ai trouvée est très simple. Pour démarrer un processus express IIS, il suffit d'utiliser Process.Start () et de lui transmettre tous les paramètres nécessaires.
Pour arrêter un processus express IIS, j'ai copié le code à partir de IISExpressTray.dll à l'aide de réflecteur. J'ai vu qu'il envoie simplement un message WM_QUIT au processus IISExpress cible.
Voici le cours que j'ai écrit pour démarrer et arrêter un processus express IIS. J'espère que cela peut aider quelqu'un d'autre.
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();
}
}
Je n'ai pas besoin de répertorier tous les processus express IIS existants. Si vous avez besoin de cela, d'après ce que j'ai vu dans le réflecteur, IISExpressTray.dll appelle la fonction Process.GetProcessByName("iisexpress", ".")
.
Pour utiliser le cours que j'ai fourni, voici un exemple de programme que j'ai utilisé pour le tester.
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();
}
}
Ce n'est peut-être pas une réponse à votre question mais je pense que les personnes intéressantes dans votre question pourraient trouver mon travail utile. N'hésitez pas à améliorer les codes. Vous voudrez peut-être améliorer certains endroits.
Bien qu'il soit trop tard, je vais répondre à cette question.
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;
C'est tout. Ensuite, vous pouvez appeler la méthode StopProcess sur un objet util.
Cependant, vous devez recevoir une notification de Microsoft.
"API Version Manager (IIS Express); http://msdn.Microsoft.com/en-us/library/gg418429(v=VS.90).aspx
Remarque: l'API IIS Version Manager prend en charge le IIS Express infrastructure et n’est pas destiné à utiliser directement à partir de votre code. "
Cette implémentation fonctionne pour démarrer/arrêter IIS Express par programmation, peut être utilisée à partir de tests.
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();
}
}
}
Harvey Kwok avait fourni un bon indice, car je souhaite mettre fin au service lors de la réalisation de scénarios de test d'intégration. Mais les codes Harvey sont trop longs avec PInvoke et la messagerie.
Voici une alternative.
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();
}
}
Et dans ma combinaison de test d'intégration avec MS Test, j'ai
[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();
}
Je pense que vous le faites de manière difficile. Inspirez-vous de cette question Arrêtez/redémarrez automatiquement le serveur de développement ASP.NET sur Build et voyez si vous pouvez adopter le même processus.
En réponse à votre question, je pense que pinvoke.net pourrait vous aider. Ils ont également de nombreux exemples qui peuvent vous aider à élaborer votre solution.
Figure je jetterais ma solution ici aussi. Dérivé de la solution de SeongTae Jeong et d'un autre article (je ne me rappelle plus où maintenant).
Microsoft.Web.Administration
nuget .IIS Installed Versions Manager Interface
comme indiqué ci-dessus.Ajouter la classe suivante:
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();
}
}
}
}
Configurez votre appareil de test comme suit. Le chemin est relatif au dossier bin de votre suite de tests.
[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");
}
}
Et un autre point si cela doit également être exécuté sur un serveur de compilation. Vous devez d’abord installer IIS Express sur le serveur de construction . Deuxièmement, vous devrez créer un applicationhost.config
sur le serveur de construction. Vous pouvez en copier un de votre boîte de dev sous C:\Users\<User>\Documents\IISExpress\config\
. Il doit être copié dans le chemin correspondant à l'utilisateur sous lequel votre serveur de génération est exécuté. S'il fonctionne en tant que système, le chemin serait C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\
.
Si vous modifiez le fichier web.config de l'application Web, IIS (y compris Express) redémarrera le pool d'applications. Cela vous permettra de déployer des assemblys mis à jour.
Une façon de modifier web.config consiste à le copier dans un nouveau fichier, puis à le déplacer à nouveau.
copy /Y path/web.config path/web_touch.config
move /Y path/web_touch.config path/web.config
Vous souhaiterez peut-être davantage de contrôle sur IIS Express que le simple redémarrage du pool d'applications. Mais si c'est tout ce dont vous avez besoin, cela fonctionnera.
Non, vous n'héritez pas de l'interface. Vous pouvez créer une instance de IISVersionManager avec le mot clé new . Comment cela vous permet-il d'obtenir une référence à une instance IIISExpressProcessUtility? Les documents MSDN sont affreux. Peut-être que vous pouvez nouveau un mais cela ne semble pas le supporter.
J'ai adopté une solution différente. Vous pouvez simplement supprimer l’arbre de processus en utilisant "taskkill" et le nom du processus . Cela fonctionne parfaitement localement et sur TFS 2013
public static void FinalizeIis()
{
var startInfo = new ProcessStartInfo
{
UseShellExecute = false,
Arguments = string.Format("/F /IM iisexpress.exe"),
FileName = "taskkill"
};
Process.Start(startInfo);
}
Voici ma solution aussi. Il exécute IIS Express avec des fenêtres cachées. La classe Manager contrôle plusieurs instances 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();
}
}
}
J'ai besoin de plusieurs instances. Conçu la classe de gestionnaire pour les contrôler.
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();
}
}
}