【问题标题】:Any way to access the IIS kernel cache from ASP.NET?有什么方法可以从 ASP.NET 访问 IIS 内核缓存?
【发布时间】:2010-11-19 13:19:58
【问题描述】:

这只会清除用户缓存中的项目:

    public static void ClearCache()
    {
        foreach (DictionaryEntry entry in HttpRuntime.Cache)
        {
            HttpRuntime.Cache.Remove(entry.Key.ToString());
        }
    }

还有什么方法可以访问内核缓存吗?

澄清:我想打印内核缓存中所有项目的键,作为奖励,我还希望能够从 C# 方法中清除内核缓存。

【问题讨论】:

标签: asp.net iis-7 caching iis-6


【解决方案1】:

是的,可以通过编程方式从 IIS 的内核缓存中枚举和删除项目。

注意事项:

  • 枚举需要非平凡的文本解析
  • 需要大量丑陋的 P/Invoke 才能删除
  • 此外,您至少需要中等信任(可能是完全信任)才能执行以下操作。
  • 在 IIS 的集成管道模式下无法删除。
  • 枚举可能在 IIS6 上不起作用

枚举:

我知道枚举 IIS 内核缓存的唯一记录方法是在 IIS7 及更高版本中可用的命令行应用程序(尽管您可能能够将 NETSH 帮助程序 DLL 从 V7 复制到 V6 系统——还没有尝试过它)。

netsh http show cachestate

请参阅MSDN Documentation of the show cachestate command 了解更多详情。您可以通过执行该过程并解析文本结果将其转换为“API”。

大警告:我从未见过此命令行应用程序实际上会在我的服务器上返回任何内容,即使是在经典模式下运行的应用程序也是如此。不知道为什么——但正如我从其他在线帖子中看到的那样,该应用程序确实有效。 (例如http://chrison.net/ViewingTheKernelCache.aspx

如果您对进程创建极度过敏并感到雄心勃勃,NETSH 命令由具有文档记录的 Win32 接口的 DLL 实现,因此您可以编写伪装成 NETSH.exe 的代码并直接调用 IIS 的 NETSH 帮助程序 DLL。您可以使用documentation on MSDN 作为此方法的起点。警告:模拟 NETSH 非常困难,因为接口是 2 路的:NETSH 调用 DLL,DLL 调用回 NETSH。而且您仍然必须解析文本输出,因为 NETSH 接口是基于文本的,而不是像 PowerShell 或 WMI 那样基于对象的。如果是我,我只会生成一个 NETSH 进程。 ;-)

IIS7 PowerShell snapin 将来可能会支持此功能(这意味着比上述黑客更容易编程访问),但目前只有 AFAIK 的 NETSH 支持此功能。

失效:

我有好消息和坏消息要告诉你。

好消息:一旦您知道要从 IIS 的内核缓存中提取的项目的 URL,就可以在 IIS6 及更高版本上使用 Win32 API 将其删除。这可以通过 P/Invoke(更难)从 C# 调用,或者通过将调用放在托管 C++ 包装 DLL 中。有关详细信息,请参阅 MSDN 上的 HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK

我尝试了所需的代码(附在下面)。警告:它丑陋且未经测试——它不会使我的 IIS 崩溃,但(见上文)我无法弄清楚如何让缓存枚举工作,因此我实际上无法使用有效的 URL 调用它以从缓存中提取。如果您可以使枚​​举工作,那么插入一个有效的 URL(并因此测试此代码)应该很容易。

坏消息:

  • 您可以从代码示例中猜到,它不能在 IIS7 的集成管道模式下工作,只能在 ASP.NET 作为 ISAPI 运行并可以访问 ISAPI 函数的经典模式(当然也可以是 IIS6)下工作李>
  • 弄乱私有字段是一个大技巧,可能会在新版本中中断
  • P/Invoke 很难处理,需要(我相信)完全信任

这里有一些代码:

using System;
using System.Web;
using System.Reflection;
using System.Runtime.InteropServices;

public partial class Test : System.Web.UI.Page
{
    /// Return Type: BOOL->int
    public delegate int GetServerVariable();

    /// Return Type: BOOL->int
    public delegate int WriteClient();

    /// Return Type: BOOL->int
    public delegate int ReadClient();

    /// Return Type: BOOL->int
    public delegate int ServerSupportFunction();

    /// Return Type: BOOL->int
    public delegate int EXTENSION_CONTROL_BLOCK_GetServerVariable();

    /// Return Type: BOOL->int
    public delegate int EXTENSION_CONTROL_BLOCK_WriteClient();

    /// Return Type: BOOL->int
    public delegate int EXTENSION_CONTROL_BLOCK_ReadClient();

    /// Return Type: BOOL->int
    public delegate int EXTENSION_CONTROL_BLOCK_ServerSupportFunction();

    public static readonly int HSE_LOG_BUFFER_LEN = 80;

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
    public struct EXTENSION_CONTROL_BLOCK
    {
        /// DWORD->unsigned int
        public uint cbSize;

        /// DWORD->unsigned int
        public uint dwVersion;

        /// DWORD->unsigned int
        public uint connID;

        /// DWORD->unsigned int
        public uint dwHttpStatusCode;

        /// CHAR[HSE_LOG_BUFFER_LEN]
        [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 80 /*HSE_LOG_BUFFER_LEN*/)]
        public string lpszLogData;

        /// LPSTR->CHAR*
        public System.IntPtr lpszMethod;

        /// LPSTR->CHAR*
        public System.IntPtr lpszQueryString;

        /// LPSTR->CHAR*
        public System.IntPtr lpszPathInfo;

        /// LPSTR->CHAR*
        public System.IntPtr lpszPathTranslated;

        /// DWORD->unsigned int
        public uint cbTotalBytes;

        /// DWORD->unsigned int
        public uint cbAvailable;

        /// LPBYTE->BYTE*
        public System.IntPtr lpbData;

        /// LPSTR->CHAR*
        public System.IntPtr lpszContentType;

        /// EXTENSION_CONTROL_BLOCK_GetServerVariable
        public EXTENSION_CONTROL_BLOCK_GetServerVariable GetServerVariable;

        /// EXTENSION_CONTROL_BLOCK_WriteClient
        public EXTENSION_CONTROL_BLOCK_WriteClient WriteClient;

        /// EXTENSION_CONTROL_BLOCK_ReadClient
        public EXTENSION_CONTROL_BLOCK_ReadClient ReadClient;

        /// EXTENSION_CONTROL_BLOCK_ServerSupportFunction
        // changed to specific signiature for invalidation callback
        public ServerSupportFunction_HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK ServerSupportFunction;
    }
    /// Return Type: BOOL->int
    ///ConnID: DWORD->unsigned int
    ///dwServerSupportFunction: DWORD->unsigned int
    ///lpvBuffer: LPVOID->void*
    ///lpdwSize: LPDWORD->DWORD*
    ///lpdwDataType: LPDWORD->DWORD*
    [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
    public delegate bool ServerSupportFunction_HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK(
        uint ConnID, 
        uint dwServerSupportFunction, // must be HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK
        out Callback_HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK lpvBuffer, 
        out uint lpdwSize, 
        out uint lpdwDataType);

    public readonly uint HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK = 1040;

    // typedef HRESULT (WINAPI * PFN_HSE_CACHE_INVALIDATION_CALLBACK)(WCHAR *pszUrl);
    [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
    public delegate bool Callback_HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK(
        [MarshalAs(UnmanagedType.LPWStr)]string url);

    object GetField (Type t, object o, string fieldName)
    {
        FieldInfo fld = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
        return fld == null ? null : fld.GetValue(o);
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        // first, get the ECB from the ISAPIWorkerRequest
        var ctx = HttpContext.Current;
        HttpWorkerRequest wr = (HttpWorkerRequest) GetField(typeof(HttpContext), ctx, "_wr");
        IntPtr ecbPtr = IntPtr.Zero;
        for (var t = wr.GetType(); t != null && t != typeof(object); t = t.BaseType)
        {
            object o = GetField(t, wr, "_ecb");
            if (o != null)
            {
                ecbPtr = (IntPtr)o;
                break;
            }
        }

        // now call the ECB callback function to remove the item from cache
        if (ecbPtr != IntPtr.Zero)
        {
            EXTENSION_CONTROL_BLOCK ecb = (EXTENSION_CONTROL_BLOCK)Marshal.PtrToStructure(
                ecbPtr, typeof(EXTENSION_CONTROL_BLOCK));
            uint dummy1, dummy2;

            Callback_HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK invalidationCallback;
            ecb.ServerSupportFunction(ecb.connID,
                    HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK,
                    out invalidationCallback,
                    out dummy1,
                    out dummy2);

            bool success = invalidationCallback("/this/is/a/test");
        }
    }
}

【讨论】:

    【解决方案2】:

    从您提供的讨论链接中,缓存方法或属性似乎存在,但受保护或私有,因此您无法访问它。

    通常,您应该避免使用不属于公共 API 的方法,但如果您想访问它们,请使用反射。通过反射,您可以调用私有方法并获取或设置私有属性和字段。

    【讨论】:

      猜你喜欢
      • 2021-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多