简体   繁体   English

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

[英]Any way to access the IIS kernel cache from ASP.NET?

This only clears items in the user cache: 这只清除用户缓存中的项目:

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

Is there any way to access the kernel cache as well? 有没有办法访问内核缓存?

Clarification: I want to print the keys of all items in the kernel cache, and as a bonus I'd like to be able to clear the kernel cache from a C# method as well. 澄清:我想打印内核缓存中所有项的键,作为奖励,我希望能够从C#方法中清除内核缓存。

Yep, it's possible to programmatically enumerate and remove items from IIS's kernel cache. 是的,可以通过编程方式枚举和删除IIS内核缓存中的项目。

Caveats: 注意事项:

  • non-trivial text parsing requred for enumeration 需要进行枚举的非平凡文本解析
  • lots of ugly P/Invoke required for removal 需要大量难看的P / Invoke才能删除
  • Also, you'll need at least Medium Trust (and probably Full Trust) to do the things below. 此外,您至少需要中等信任(可能还有完全信任)来执行以下操作。
  • Removal won't work in IIS's integrated pipeline mode. 删除在IIS的集成管道模式下不起作用。
  • Enumeration probably won't work on IIS6 枚举可能不适用于IIS6

Enumeration: 列举:

The only documented way I know to enumerate the IIS kernel cache is a command-line app available in IIS7 and above (although you might be able to copy the NETSH helper DLL from V7 onto a V6 system-- haven't tried it). 我知道枚举IIS内核缓存的唯一记录方式是IIS7及更高版本中可用的命令行应用程序(尽管您可以将NETSH帮助程序DLL从V7复制到V6系统 - 尚未尝试过)。

netsh http show cachestate

See MSDN Documentation of the show cachestate command for more details. 有关更多详细信息,请参阅show cachestate命令的MSDN文档 You could turn this into an "API" by executing the process and parsing the text results. 您可以通过执行该过程并解析文本结果将其转换为“API”。

Big Caveat: I've never seen this command-line app actually return anything on my server, even for apps running in Classic mode. Big Caveat:我从未见过这个命令行应用实际上在我的服务器上返回任何内容,即使是在经典模式下运行的应用程序。 Not sure why-- but the app does work as I can see from other postings online. 不知道为什么 - 但应用程序确实有效,因为我可以在网上看到其他帖子。 (eg http://chrison.net/ViewingTheKernelCache.aspx ) (例如http://chrison.net/ViewingTheKernelCache.aspx

If you're horribly allergic to process creation and feeling ambitious, NETSH commands are implemented by DLL's with a documented Win32 interface, so you could write code which pretends it's NETSH.exe and calls into IIS's NETSH helper DLL directly. 如果你对进程创建非常过敏并且有野心勃勃,那么NETSH命令是由带有文档化的Win32接口的DLL实现的,因此你可以编写假装它的NETSH.exe并直接调用IIS的NETSH助手DLL的代码。 You can use the documentation on MSDN as a starting point for this approach. 您可以使用MSDN上文档作为此方法的起点。 Warning: impersonating NETSH is non-trivially hard since the interface is 2-way: NETSH calls into the DLL and the DLL calls back into NETSH. 警告:模拟NETSH是非常困难的,因为接口是双向的:NETSH调用DLL并且DLL调用回NETSH。 And you'd still have to parse text output since the NETSH interface is text-based, not object-based like PowerShell or WMI. 而且你仍然需要解析文本输出,因为NETSH接口是基于文本的,而不是基于对象的,如PowerShell或WMI。 If it were me, I'd just spawn a NETSH process instead. 如果是我,我只会产生一个NETSH流程。 ;-) ;-)

It's possible that the IIS7 PowerShell snapin may support this functionality in the future (meaning easier programmatic access than the hacks above), but AFAIK only NETSH supports this feature today. IIS7 PowerShell管理单元可能在未来支持此功能(意味着比上面的hacks更容易编程访问),但AFAIK今天只支持NETSH支持此功能。

Invalidation: 无效:

I've got good news and bad news for you. 我有好消息和坏消息。

The good news: Once you know the URL of the item you want to yank from IIS's kernel cache, there's a Win32 API available to remove it on IIS6 and above. 好消息:一旦你知道要从IIS的内核缓存中提取的项目的URL,就可以在IIS6及更高版本上使用Win32 API将其删除。 This can be called from C# via P/Invoke (harder) or by putting the call in a managed C++ wrapper DLL. 这可以通过P / Invoke(更难)从C#调用,也可以通过将调用放入托管C ++包装器DLL中调用。 See HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK on MSDN for details. 有关详细信息,请参阅MSDN上的HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK

I took a stab at the code required (attached below). 我接受了所需的代码(附在下面)。 Warning: it's ugly and untested-- it doesn't crash my IIS but (see above) I can't figure out how to get cache enumeration working so I can't actually call it with a valid URL to pull from the cache. 警告:它是丑陋且未经测试的 - 它不会使我的IIS崩溃但是(见上文)我无法弄清楚如何使缓存枚举工作,所以我实际上无法使用有效的URL来调用它来从缓存中提取。 If you can get enumeration working, then plugging in a valid URL (and hence testing this code) should be easy. 如果您可以使枚​​举工作,那么插入有效的URL(并因此测试此代码)应该很容易。

The bad news: 坏消息:

  • as you can guess from the code sample, it won't work on IIS7's integrated pipeline mode, only in Classic mode (or IIS6, of course) where ASP.NET runs as an ISAPI and has access to ISAPI functions 正如您可以从代码示例中猜到的那样,它不能在IIS7的集成管道模式下工作,只能在经典模式(当然是IIS6)中,其中ASP.NET作为ISAPI运行并且可以访问ISAPI函数
  • messing with private fields is a big hack and may break in a new version 搞乱私人领域是一个很大的问题,可能会在新版本中破坏
  • P/Invoke is hard to deal with and requires (I believe) full trust P / Invoke很难处理,需要(我相信)完全信任

Here's some code: 这是一些代码:

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");
        }
    }
}

From the discussion link you provided, it appears that the cache method or property exists but is protected or private so you can't access it. 从您提供的讨论链接看,缓存方法或属性似乎存在但受保护或私有,因此您无法访问它。

Normally, you should stay away from using methods that are not part of the public API, but if you want to access them, use Reflection. 通常,您应该远离使用不属于公共API的方法,但如果要访问它们,请使用Reflection。 With reflection, you can call private methods and get or set private properties and fields. 通过反射,您可以调用私有方法并获取或设置私有属性和字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM