以编程方式确定代码是否在IIS Express下运行

31

我不确定这是否可能,但我希望可以得到一些线索,以确定当前正在运行的代码是否在IIS Express下运行。迄今为止,我的最佳近似值非常破解,肯定会在某个时刻失败/中断:

bool IsExpress = 
  Request.ServerVariables["SERVER_SOFTWARE"] == "Microsoft-IIS/7.5"
  && Int32.Parse(Request.ServerVariables["INSTANCE_ID"]) > 1000000000;
肯定有更好的方法。我仔细查看了Application、Server和Request对象,并没有发现任何能提供更好洞见的东西。也许我需要看看其他对象?
更新: 我真的很想知道是否有一种方法来检测这个 - 现在它确实是学术性的,我没有强烈的需要使用它。原始问题依然存在。但是出于回应评论的精神,特别是回答该网站上另一个问题/答案中的批评:如何搜索服务器的MIME映射。 批评是发布的答案对IIS Express不起作用,只适用于传统的IIS实例。IIS Express将MIME配置存储在applicationhost.config XML文件中,我想更新该答案,以提供一种返回IIS Express的信息的方法。 我肯定可以添加一些代码,从XML中获取适当的值(感谢LINQ to XML!),但我真的想让它更智能化。 明确一下,我不需要帮助解析该文件 - 只需要在尝试检测代码当前是否在IIS Express引擎中执行时提供一些更优雅的方法。
更新2: IIS 8.0 Express Beta 在本周发布,这进一步表明我问题中的方法脆弱且会破裂。虽然针对特定版本不是致命问题,但考虑到至少要确保代码能够与已知的版本一起使用还是很好的。

你的目标是什么?为什么要检测这个(也许有其他方法可以实现你的目标)? - Yahia
5
如果它在IIS Express中运行,你想要处理不同的内容吗?也许这是你需要问的问题,而不是如何检测IIS Express。例如:“当在调试模式下运行时,我该如何执行____而不是_____?” - Anthony Pegram
这个需求的核心是检查一些配置元素 - 使用DirectoryEntry("IIS:/localhost/W3SVC/")还是解析控制IIS Express的applicationhost.config中的XML。 - Goyuix
1
再次确认,你的目标是什么?应用程序为什么需要了解托管环境? - John Saunders
4个回答

33

检查当前进程名称是否可行?

bool isExpress = 
  String.Compare(Process.GetCurrentProcess().ProcessName,"iisexpress") == 0;

普通的 IIS 运行在内存中的 w3wp.exe 中。


1
这是针对IIS 7.5的w3wp -- 但我不确定不同版本的IIS是否相同。我知道旧版本的IIS有所不同... - debracey
1
@debracey 是的,对于旧版本来说,它将是 aspnet_wp.exe - Strelok
我遇到的唯一潜在问题是,这种方法需要完全信任的 System.Diagnostics.Process 类。否则,它似乎像魔法一样运行良好。 - Goyuix
4
我已经测试了这段代码,应该将其更改为: bool isExpress = String.Compare(Process.GetCurrentProcess().ProcessName, "iisexpress") == 0;。 - Alex Pop

3
< p >Oran Dennison 的答案在使用 dotnet core 时不再适用。 < p>我首先创建了一个可以获取父进程的方法,然后对其进行了扩展:
/// <summary>
/// A utility class to determine a process parent.
/// </summary>
/// <remarks>
/// From https://dev59.com/HnRC5IYBdhLWcg3wJNqf#3346055
/// </remarks>
public static class ParentProcessUtilities
{
    /// <summary>
    /// Gets the parent process.
    /// </summary>
    /// <param name="process">The process to get the parent of</param>
    /// <returns>The parent process.</returns>
    public static Process Parent(this Process process)
    {
        return GetParentProcess(process.Handle);
    }

    /// <summary>
    /// Gets the parent process of the current process.
    /// </summary>
    /// <returns>An instance of the Process class.</returns>
    public static Process GetParentProcess()
    {
        return Process.GetCurrentProcess().Parent();
    }

    /// <summary>
    /// Gets the parent process of a specified process.
    /// </summary>
    /// <param name="handle">The process handle.</param>
    /// <returns>The parent process.</returns>
    public static Process GetParentProcess(IntPtr handle)
    {
        ProcessInformation pbi = new ProcessInformation();

        // Note, according to Microsoft, this usage of NtQueryInformationProcess is 
        // unsupported and may change
        int status = NtQueryInformationProcess(
            handle, 0, ref pbi, Marshal.SizeOf(pbi), out _);
        if (status != 0)
        {
            throw new Win32Exception(status);
        }

        try
        {
            return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
        }
        catch (ArgumentException)
        {
            // not found
            return null;
        }
    }

    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(
        IntPtr processHandle, int processInformationClass,
        ref ProcessInformation processInformation, int processInformationLength, 
        out int returnLength);

    /// <summary>
    /// Used in the NtQueryInformationProcess call.
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct ProcessInformation
    {
        // These members must match PROCESS_BASIC_INFORMATION
        internal IntPtr Reserved1;
        internal IntPtr PebBaseAddress;
        internal IntPtr Reserved2_0;
        internal IntPtr Reserved2_1;
        internal IntPtr UniqueProcessId;
        internal IntPtr InheritedFromUniqueProcessId;

    }
}

然后,您可以进行如下操作:
public bool IsUnderIisExpress()
{
    var currentProcess = Process.GetCurrentProcess();
    if (string.CompareOrdinal(currentProcess.ProcessName, "iisexpress") == 0)
    {
       return true;
    }

    var parentProcess = currentProcess.Parent();
    if (string.CompareOrdinal(parentProcess.ProcessName, "iisexpress") == 0
        || string.CompareOrdinal(parentProcess.ProcessName, "VSIISExeLauncher") == 0)
    {
        return true;
    }

    return false;
}

FYI IWebHostBuilder.UseIIS()(https://github.com/dotnet/aspnetcore/blob/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs#L31)会检查当前进程是否已加载 aspnetcorev2_inprocess.dll. - Jeremy Lakeman

2
我们能否尝试一下,看看一个或多个IIS Express的限制是否有效,如果有效,则不是IIS Express。例如,IIS Express不支持SharePoint服务。

请问您能分享一下,您认为哪些特定(或其他)限制条件是您可以可靠地测试的? - Goyuix
某种类型的查询,比如环境变量%IISBin%可能是一个好的提示?或者查询SharePoint服务,如果结果是unsupported-exception,那么我们就知道我们在使用IIS Express。 - Rohit Sharma
测试 %IIS_BIN% 可能有效,看起来只有 IIS Express 设置了该变量。唯一需要注意的是 EnvironmentPermission 类可以设置为拒绝访问这些变量。我不知道你所说的“查询 SharePoint 服务”的意思 - SharePoint 是一个可以安装的产品,而不是 IIS 的本地功能。 - Goyuix

2

我研究了这个API,但是有两个顾虑:一是它被标记为“不打算直接从您的代码中使用” - 这不是致命问题,但也不太鼓舞人心。二是我不确定如何(甚至是否可以)将当前执行的代码连接到从API返回的实例。也许通过匹配URL或其他方式。 - Goyuix
不确定你的意思是什么,但我会在应用程序池启动时将IIS版本拉入您的代码中(可能是自己管理的“枚举”),然后只需将其存储在静态变量中 - 不要每次查询COM API。服务器类型显然无法更改而不重新启动应用程序。 - debracey
如果我新建 IISVersionManager 类,我可以获取机器上可用的 IIS 实例列表,但是我无法将该 IISVersion 接口映射到正在 IIS 实例内部执行的代码。我真的不知道这个 COM API 如何帮助我知道当前运行的代码是否在 IIS Express 内部 - 不仅仅是 IIS Express 是否可用。也许一个代码示例可以帮助澄清你所建议的内容? - Goyuix
哦,好的 - 是的 - 那是一个很好的观点...回到起点。 - debracey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接