프로그래밍 방식으로 IIS Express 시작 및 중지
IIS Express 작업자 프로세스를 시작 / 중지해야하는 작은 응용 프로그램을 C #으로 빌드하려고합니다. 이를 위해 MSDN에 문서화되어있는 공식 "IIS Express API"를 사용하고 싶습니다. http://msdn.microsoft.com/en-us/library/gg418415.aspx
내가 이해하는 한, API는 COM 인터페이스를 기반으로합니다. 이 COM 인터페이스를 사용하기 위해 참조 추가-> COM-> "IIS 설치 버전 관리자 인터페이스"를 통해 VS2010의 COM 라이브러리에 대한 참조를 추가했습니다.
지금까지는 좋지만 다음은 무엇입니까? 있다 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)).
저도 비슷한 일을하려고했습니다. Microsoft에서 제공하는 COM 라이브러리가 불완전하다는 결론을 내 렸습니다. 문서에서 "참고 :이 항목은 시험판 문서이며 향후 릴리스에서 변경 될 수 있습니다."라고 언급했기 때문에 사용하지 않습니다.
그래서 IISExpressTray.exe가 무엇을하는지 살펴보기로했습니다. 비슷한 일을하는 것 같습니다.
IISExpressTray.dll을 분해하고 모든 IISexpress 프로세스를 나열하고 IISexpress 프로세스를 중지하는 데 마법이 없다는 것을 알았습니다.
이 COM 라이브러리를 호출하지 않습니다. 레지스트리에서 아무것도 조회하지 않습니다.
그래서 제가 만든 해결책은 매우 간단합니다. IIS 익스프레스 프로세스를 시작하려면 Process.Start ()를 사용하고 필요한 모든 매개 변수를 전달합니다.
IIS Express 프로세스를 중지하기 위해 리플렉터를 사용하여 IISExpressTray.dll에서 코드를 복사했습니다. 나는 단순히 대상 IISExpress 프로세스에 WM_QUIT 메시지를 보내는 것을 보았습니다.
다음은 IIS Express 프로세스를 시작하고 중지하기 위해 작성한 클래스입니다. 이것이 다른 사람을 도울 수 있기를 바랍니다.
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();
}
}
이것은 귀하의 질문에 대한 답변이 아닐 수 있지만 귀하의 질문에 흥미로운 사람들이 내 작업을 유용하게 사용할 수 있다고 생각합니다. 코드를 자유롭게 개선하십시오. 향상시키고 싶은 곳이 있습니다.
- iisexpress.exe 위치를 하드 코딩하는 대신 레지스트리에서 읽도록 코드를 수정할 수 있습니다.
- iisexpress.exe에서 지원하는 모든 인수를 포함하지 않았습니다.
- 오류 처리를하지 않았습니다. 따라서 IISExpress 프로세스가 어떤 이유로 시작되지 않은 경우 (예 : 포트가 사용 중임) 모르겠습니다. 나는 그것을 고치는 가장 쉬운 방법은 StandardError 스트림을 모니터링하고 StandardError 스트림에서 무언가를 얻으면 예외를 던지는 것이라고 생각합니다.
너무 늦었지만이 질문에 대한 답변을 제공하겠습니다.
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 인프라를 지원하며 사용자 코드에서 직접 사용할 수 없습니다. "
이 구현은 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();
}
}
}
나는 당신이 그것을 힘들게하고 있다고 느낍니다. 이 질문에서 힌트를 얻으십시오. 빌드에서 ASP.NET 개발 서버를 자동으로 중지 / 다시 시작 하고 동일한 프로세스를 채택 할 수 있는지 확인하십시오.
귀하의 질문에 대답 하면 pinvoke.net 이 도움이 될 것이라고 생각 합니다. 솔루션 구축에 도움이 될 수있는 많은 예제도 있습니다.
Harvey Kwok은 통합 테스트 케이스를 실행할 때 서비스를 해체하고 해체하고 싶기 때문에 좋은 힌트를 제공했습니다. 그러나 하비 코드는 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();
}
아니요, 인터페이스를 상속하지 않습니다. new 키워드를 사용하여 IISVersionManager의 인스턴스를 만들 수 있습니다 . 이것이 IIISExpressProcessUtility 인스턴스에 대한 참조를 얻는 방법은 완전히 불분명합니다. MSDN 문서는 끔찍합니다. 아마 당신은 새로운 것을 할 수 있지만 그것이 그것을 지원하는 것처럼 보이지 않습니다.
웹 응용 프로그램의 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에 대한 더 많은 제어가 필요할 수 있습니다. 하지만 그게 전부라면 작동합니다.
나는 다른 해결책을 채택했습니다. "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);
}
여기에도 내 솔루션을 던질 것입니다. 정성태의 솔루션과 다른 게시물에서 파생되었습니다 (지금 어디인지 기억 나지 않습니다).
Microsoft.Web.Administration
너겟을 설치하십시오 .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"); } }
그리고 이것이 빌드 서버에서도 실행되는 경우 한 가지 더. 먼저 빌드 서버에 IIS Express 를 설치 해야 합니다 . 둘째, applicationhost.config
빌드 서버에서 를 만들어야 합니다. 아래의 개발 상자에서 복사 할 수 있습니다 C:\Users\<User>\Documents\IISExpress\config\
. 빌드 서버가 실행중인 사용자의 해당 경로에 복사해야합니다. 시스템으로 실행중인 경우 경로는입니다 C:\Windows\System32\config\systemprofile\Documents\IISExpress\config\
.
여기 내 해결책도 있습니다. 숨겨진 창으로 IIS Express를 실행합니다. 관리자 클래스는 여러 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();
}
}
}
참조 URL : https://stackoverflow.com/questions/4772092/starting-and-stopping-iis-express-programmatically
'IT TIP' 카테고리의 다른 글
컴퓨터가 절전 모드에서 다시 시작될 때 데스크톱 브라우저가 감지 할 수 있습니까? (0) | 2020.12.30 |
---|---|
C # 4.0 app.config에서 섹션을 어떻게 사용합니까? (0) | 2020.12.30 |
함수 인수를 최종적으로 선언하는 이유는 무엇입니까? (0) | 2020.12.30 |
SQL Server 기본 문자 인코딩 (0) | 2020.12.29 |
Git 저장소 외부에서`git diff --color-words`를 사용하는 방법은 무엇입니까? (0) | 2020.12.29 |