61

IIS Express ワーカー プロセスを開始/停止する小さなアプリケーションを C# で構築しようとしています。この目的のために、MSDN に記載されている公式の「IIS Express API」を使用したいと思います: http://msdn.microsoft.com/en-us/library/gg418415.aspx

私の知る限り、API は (のみ) COM インターフェイスに基づいています。この COM インターフェイスを使用するために、[参照の追加] -> [COM] -> [IIS Installed Versions Manager Interface] を使用して、VS2010 に COM ライブラリへの参照を追加しました。

ここまでは順調ですが、次はどうなるでしょうか。IIISExprProcessUtilityIIS プロセスを開始/停止する 2 つの「メソッド」を含む利用可能なインターフェイスがあります。このインターフェースを実装するクラスを作成する必要がありますか?

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

10 に答える 10

65

私は同じようなことをしようとしていました。Microsoftが提供するCOMライブラリは不完全であると結論付けました。ドキュメントに「注:このトピックはプレリリースのドキュメントであり、将来のリリースで変更される可能性がある」と記載されているため、使用しません。

そこで、IISExpressTray.exeが何をしているのかを見てみることにしました。同様のことをしているようです。

IISExpressTray.dllを分解すると、すべてのIISexpressプロセスを一覧表示し、IISexpressプロセスを停止することに魔法がないことがわかりました。

そのCOMライブラリは呼び出されません。レジストリからは何も検索しません。

だから、私が最終的に得た解決策は非常に簡単です。IISエクスプレスプロセスを開始するには、Process.Start()を使用して、必要なすべてのパラメーターを渡します。

IIS Expressプロセスを停止するために、リフレクターを使用してIISExpressTray.dllからコードをコピーしました。ターゲットIISExpressプロセスにWM_QUITメッセージを送信するだけであることがわかりました。

これは、IISExpressプロセスを開始および停止するために作成したクラスです。これが他の誰かを助けることができることを願っています。

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エクスプレスプロセスをすべてリストする必要はありません。それが必要な場合は、リフレクターで見たものから、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. iisexpress.exeの場所をハードコーディングする代わりに、レジストリから読み取るようにコードを修正できます。
  2. iisexpress.exeでサポートされているすべての引数を含めませんでした
  3. エラー処理はしませんでした。したがって、IISExpressプロセスが何らかの理由で開始できなかった場合(たとえば、ポートが使用されている場合)、わかりません。これを修正する最も簡単な方法は、StandardErrorストリームを監視し、StandardErrorストリームから何かを取得した場合に例外をスローすることだと思います。
于 2011-01-24T01:57:19.383 に答える
15

遅くなりましたが、この質問にお答えします。

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 から通知を受け取る必要があります。

" バージョン マネージャー API (IIS Express) ; http://msdn.microsoft.com/en-us/library/gg418429(v=VS.90).aspx

注: IIS バージョン マネージャー API は、IIS Express インフラストラクチャをサポートしており、コードから直接使用するためのものではありません。 "

于 2011-04-30T10:45:41.673 に答える
3

統合テスト ケースを実行するときにサービスを破棄したり破棄したりしたいので、Harvey Kwok が良いヒントを提供してくれました。しかし、Harvey コードは、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();
    }
于 2014-07-10T00:10:37.030 に答える
3

一生懸命やっていると思います。この質問からヒントを得て、ビルド時に ASP.NET 開発サーバーを自動的に停止/再起動し、同じプロセスを採用できるかどうかを確認してください。

あなたの質問に答えると、pinvoke.netが役立つと思います。ソリューションの構築に役立つ多くの例もあります。

于 2011-01-23T05:20:51.873 に答える
2

ここにも私のソリューションを投入したいと思います。SeongTae Jeong のソリューションと別の投稿から派生しました (今どこにあるか覚えていません)。

  1. Microsoft.Web.Administration ナゲットをインストールします。
  2. IIS Installed Versions Manager Interface上記の COM タイプ ライブラリを参照します。
  3. 次のクラスを追加します。

    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();
                }
            }
        }
    }
    
  4. 次のようにテスト フィクスチャをセットアップします。パスは、テスト スイートの 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");
        }
    }
    

これがビルドサーバーでも実行される場合は、もう1つのポイントです。まず、IIS Express をビルド サーバーにインストールする必要があります。applicationhost.config次に、ビルド サーバーにを作成する必要があります。の下にある開発ボックスから 1 つをコピーできますC:\Users\<User>\Documents\IISExpress\config\。ビルド サーバーを実行しているユーザーの対応するパスにコピーする必要があります。システムとして実行されている場合、パスはC:\Windows\System32\config\systemprofile\Documents\IISExpress\config\.

于 2015-04-18T21:13:10.847 に答える
1

いいえ、インターフェイスを継承しません。newキーワードを使用して IISVersionManager のインスタンスを作成できます。IIISExpressProcessUtility インスタンスへの参照を取得する方法は完全に不明です。MSDN のドキュメントはひどいものです。新しいものを作成できるかもしれませんが、それをサポートしているようには見えません。

于 2011-01-23T10:16:23.540 に答える