62 votes

Commencer et arrêter IIS Express programmationmaticalement

Je suis en train de construire une petite application en C# qui devrait démarrer/arrêter un processus de travail IIS Express. À cette fin, je veux utiliser l'API officielle "IIS Express" qui est documentée sur MSDN : http://msdn.microsoft.com/en-us/library/gg418415.aspx

D'après ce que je comprends, l'API est basée (uniquement) sur des 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 des Versions Installées d'IIS":

Jusque-là tout va bien, mais que faire ensuite? Il existe une interface IIISExprProcessUtility disponible 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 voir, je ne suis pas un développeur professionnel. Quelqu'un pourrait-il me mettre dans la bonne direction. Toute aide est grandement appréciée.

Mise à jour 1: Selon les suggestions, j'ai essayé le code suivant qui malheureusement ne fonctionne pas :

texte alternatif D'accord, il peut être instancié mais je ne vois pas comment utiliser cet objet...

texte alternatif

texte alternatif

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)).

66voto

Harvey Kwok Points 5508

J'essayais de faire une chose similaire. J'ai conclu que la bibliothèque COM fournie par Microsoft est incomplète. Je ne l'utilise pas car la documentation mentionne que "Note : Ce sujet est une documentation préliminaire et est susceptible d'être modifiée dans les versions futures".

Alors, j'ai décidé de jeter un œil à ce que fait IISExpressTray.exe. Il semble faire des choses similaires.

Je désassemble le IISExpressTray.dll et j'ai trouvé qu'il n'y a pas de magie dans la liste de tous les processus IISexpress et d'arrêter le processus IISexpress.

Il n'appelle pas cette bibliothèque COM. Il ne recherche rien dans le registre.

Donc, la solution à laquelle j'ai abouti est très simple. Pour démarrer un processus IIS Express, j'utilise simplement Process.Start() et je passe tous les paramètres dont j'ai besoin.

Pour arrêter un processus IIS Express, j'ai copié le code du IISExpressTray.dll en utilisant un réflecteur. J'ai vu qu'il envoie simplement un message WM_QUIT au processus IISExpress cible.

Voici la classe que j'ai écrite pour démarrer et arrêter un processus IIS Express. J'espère que cela pourra aider quelqu'un d'autre.

class IISExpress
{
    internal class NativeMethods
    {
        // Méthodes
        [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 lister tous les processus IIS Express existants. Si vous en avez besoin, d'après ce que j'ai vu dans le réflecteur, ce que fait IISExpressTray.dll est d'appeler Process.GetProcessByName("iisexpress", ".")

Pour utiliser la classe que j'ai fournie, voici un programme d'exemple que j'ai utilisé pour le tester.

class Program
{

    static void Main(string[] args)
    {
        Console.Out.WriteLine("Lancement d'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("Appuyez sur ENTRÉE pour arrêter");
        Console.In.ReadLine();

        iis1.Stop();
        iis2.Stop();
    }
}

Cela peut ne pas répondre à votre question mais je pense que les personnes intéressées par votre question pourraient trouver mon travail utile. N'hésitez pas à améliorer les codes. Il y a des endroits que vous voudrez peut-être améliorer.

  1. Au lieu de coder en dur l'emplacement de iisexpress.exe, vous pouvez corriger mon code pour lire à partir du registre.
  2. Je n'ai pas inclus tous les arguments pris en charge par iisexpress.exe
  3. Je n'ai pas fait de gestion des erreurs. Donc, si le processus IISExpress échoue à démarrer pour certaines raisons (par exemple, le port est utilisé), je ne le sais pas. Je pense que le moyen le plus simple de le corriger est de surveiller le flux StandardError et de lever une exception si je reçois quelque chose du flux StandardError

2 votes

Wow! Merci beaucoup pour cette réponse détaillée! Je pense que tant qu'il n'y a pas de documentation COM complète/correcte sur MSDN, cela semble être la solution parfaite. Je vais l'essayer dès que je serai de retour sur le PC.

0 votes

Excellente, le message d'arrêt a fonctionné à merveille. Je lance IIS Express à partir de la ligne de commande mais c'était la pièce manquante. Merci!

1 votes

Brillant! J'ai ajouté CreateNoWindow = true, dans Process.Start et j'utilise cela pendant la configuration/désassemblage de mes équipements. Merci beaucoup!

15voto

SeongTae Jeong Points 101

Bien que ce soit trop tard, je vais fournir une réponse à 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 l'objet util.

Cependant, vous devez recevoir un avis de la part de Microsoft.

" API du gestionnaire de versions (IIS Express) ; http://msdn.microsoft.com/en-us/library/gg418429(v=VS.90).aspx

Remarque : L'API du gestionnaire de versions IIS prend en charge l'infrastructure IIS Express et n'est pas destinée à être utilisée directement dans votre code. "

0 votes

Merci (+1). Le compilateur s'est plaint et m'a dit de modifier la première ligne : IISVersionManagerLibrary.IISVersionManager mgr = new IISVersionManagerLibrary.IISVersionManager();

2 votes

Merci! Ce code fonctionne bien pour moi, mais je suppose que je suis un peu perdu quant à ce qu'il faut faire ensuite. Comment puis-je savoir sur quel port il fonctionne? Comment puis-je utiliser cela pour lancer un site que je suis en train de développer?

0 votes

Hélas, cela ne fonctionne pas avec la dernière version (4.5+) de .NET.... erreur CS0656 : Membre du compilateur manquant requis 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'

7voto

Mharlin Points 1446

Cette implémentation fonctionne pour démarrer / arrêter IIS Express de manière programmable et peut être utilisée à partir des 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();
        }
    }
}

4 votes

C'est génial! J'ai trouvé une implémentation un peu plus complète de ceci ici.

1 votes

Environment.Is64BitOperatingSystem est le mauvais choix ici. Cela dépend vraiment de la taille des binaires pour lesquels le site est construit.

3voto

Pradeep Points 2647

Je sens que vous le faites d'une manière compliquée. Prenez un indice de cette question Automatically stop/restart ASP.NET Development Server on Build et voyez si vous pouvez adopter le même processus.

En répondant à votre question, je pense que pinvoke.net pourrait vous aider. Ils ont aussi beaucoup d'exemples qui peuvent vous aider à créer votre solution.

0 votes

Pradeep, merci pour votre réponse. La solution du premier lien ne fonctionne pas pour moi car ils tuent le processus avec proc.Kill(); pinvoke.net est très intéressant, merci pour le conseil. Malheureusement, je n'ai trouvé aucune information sur la façon d'utiliser l'API COM d'IIS Express.

3voto

ZZZ Points 918

Harvey Kwok avait donné un bon indice, puisque je veux déchirer et démonter le service lors de l'exécution des cas de test d'intégration. Mais les codes de 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 suite de tests 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;

    //Utilisez ClassCleanup pour exécuter du code après l'exécution de tous les tests d'une classe
    [ClassCleanup()]
    public static void MyClassCleanup()
    {
        iis.Stop();
    }

0 votes

Vous pouvez constater que le modèle IDisposable habituel n'est pas utilisé ici. Cependant, la ressource de processus est en réalité libérée dans la méthode ClassCleanup/teardown, et le processus du test est de courte durée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X