Sí, es posible enumerar y eliminar elementos del caché del kernel de IIS mediante programación.
Advertencias:
- análisis de texto no trivial requred para la enumeración
- un montón de feo P/Invoke necesarios para la eliminación
- Además, se necesitan al menos Medium Trust (Confianza y probablemente completa) para hacer las cosas a continuación.
- La eliminación no funcionará en el modo de canalización integrada de IIS.
- enumeración probablemente no funcionará en IIS6
enumeración:
La única manera que sé documentado para enumerar la caché del núcleo de IIS es una aplicación de línea de comandos disponible en IIS7 y por encima (aunque es posible que pueda copiar la DLL auxiliar de NETSH de V7 en un sistema V6, no lo he intentado).
netsh http show cachestate
Ver MSDN Documentation of the show cachestate command para más detalles. Puede convertir esto en una "API" ejecutando el proceso y analizando los resultados del texto.
Gran advertencia: nunca he visto esta aplicación de línea de comandos realmente devolver algo en mi servidor, incluso para aplicaciones que se ejecutan en modo clásico. No estoy seguro por qué, pero la aplicación funciona como puedo ver en otras publicaciones en línea. (Por ejemplo, http://chrison.net/ViewingTheKernelCache.aspx)
Si eres horriblemente alérgico a la creación de procesos y te sientes ambicioso, los comandos NETSH se implementan mediante DLL con una interfaz Win32 documentada, para que puedas escribir código que simule NETSH.exe y llame a NETSH helper de IIS DLL directamente Puede usar documentation on MSDN como punto de partida para este enfoque. Advertencia: hacerse pasar por NETSH no es trivialmente difícil ya que la interfaz es bidireccional: NETSH llama al DLL y el DLL vuelve a llamar a NETSH. Y aún tendría que analizar la salida de texto dado que la interfaz NETSH está basada en texto, no basada en objetos como PowerShell o WMI. Si fuera yo, crearía un proceso NETSH. ;-)
Es posible que IIS7 PowerShell snapin pueda admitir esta funcionalidad en el futuro (lo que significa acceso programático más sencillo que los hacks anteriores), pero AFAIK solo NETSH admite esta función en la actualidad.
invalidación:
Tengo buenas y malas noticias para usted.
Las buenas noticias: una vez que sepa la URL del elemento que desea extraer de la memoria caché del kernel de IIS, hay una API Win32 disponible para eliminarla en IIS6 y superior. Esto se puede invocar desde C# a través de P/Invoke (más difícil) o al poner la llamada en una DLL administrada de contenedor de C++. Vea HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK en MSDN para más detalles.
Tomé una puñalada en el código requerido (se adjunta a continuación). Advertencia: es feo y no probado: no interrumpe mi IIS, pero (ver más arriba) No puedo entender cómo funciona la enumeración de caché, así que no puedo llamarlo con una URL válida para extraer de la caché. Si puede lograr que la enumeración funcione, entonces conectar una URL válida (y por lo tanto probar este código) debería ser fácil.
La mala noticia:
- como se puede adivinar por el ejemplo de código, no va a funcionar en el modo de canalización integrada de IIS 7, sólo en el modo clásico (o IIS6, por supuesto), donde se ejecuta ASP.NET como un ISAPI y tiene acceso a las funciones ISAPI
- jugar con los campos privados es un gran truco y puede romperse en una nueva versión
- P/Invoke es difícil de tratar y requiere (creo) de plena confianza
Aquí hay algo de código:
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");
}
}
}
hay mayor discusión sobre este tema aquí: http://www.west-wind.com/weblog/posts/11379.aspx#121596 (ver comentarios). ¿Alguien tiene una solución mejor que la mencionada allí? –