代码之家  ›  专栏  ›  技术社区  ›  Robert Claypool

有没有办法从ASP.NET访问IIS内核缓存?

  •  6
  • Robert Claypool  · 技术社区  · 15 年前

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

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

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

    澄清: 我想打印内核缓存中所有项目的密钥,作为一个额外的好处,我希望能够从C方法中清除内核缓存。

    2 回复  |  直到 9 年前
        1
  •  11
  •   Justin Grant    15 年前

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

    Caveats:

    • 枚举所需的重要文本分析
    • 移除时需要大量丑陋的P/Invoke
    • 此外,您至少需要中等信任(并且可能需要完全信任)才能执行以下操作。
    • 删除在IIS的集成管道模式下不起作用。
    • 枚举在IIs6上可能不起作用

    Enumeration:

    我所知道的枚举IIS内核缓存的唯一有文档记录的方法是在iis7和更高版本中提供一个命令行应用程序(尽管您可能能够将netsh helper dll从v7复制到V6系统上——但还没有尝试过)。

    netsh http show cachestate
    

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

    大警告:我从未见过这个命令行应用程序在我的服务器上实际返回任何东西,即使是在经典模式下运行的应用程序。不知道为什么——但是这个应用程序确实能像我在网上看到的那样工作。(例如 http://chrison.net/ViewingTheKernelCache.aspx )

    如果您对进程创建极度过敏,并且有雄心壮志,netsh命令由dll实现,带有一个有文档记录的win32接口,因此您可以编写代码,假装它是netsh.exe,并直接调用IIS的netsh helper dll。你可以使用 documentation on MSDN 作为这种方法的起点。警告:模拟netsh非常困难,因为接口是双向的:netsh调用dll,dll调用netsh。而且,由于netsh接口是基于文本的,而不是像powershell或wmi那样基于对象的,所以您仍然需要分析文本输出。如果是我,我只会生成一个netsh进程。;-)

    很可能 IIS7 PowerShell snapin 将来可能会支持此功能(意味着比上述黑客更容易进行编程访问),但目前只有Afaik-Only Netsh支持此功能。

    失效:

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

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

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

    坏消息是:

    • 正如您从代码示例中可以猜到的那样,它不会在iis7的集成管道模式下工作,只有在经典模式(或者iis6,当然)下,ASP.NET作为ISAPI运行,并且可以访问ISAPI函数。
    • 打乱私人领域是一个巨大的黑客行为,可能会在新版本中被打破。
    • 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
  •  0
  •   John Fisher    15 年前

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

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