Tak, można programowo wyliczyć i usunąć elementy z pamięci podręcznej jądra IIS.
Ostrzeżenia:
- nietrywialne parsowanie tekstu requred dla wyliczenia
- wiele brzydkich P/Invoke wymagane do usuwania
- Ponadto, trzeba co najmniej średnie Trust (i zapewne pełne zaufanie) robić rzeczy poniżej.
- Usunięcie nie działa w zintegrowanym trybie potoku usług IIS.
- Wyliczanie prawdopodobnie nie będzie działać na IIS6
liczby:
Jedyny sposób udokumentowane wiem wyliczyć cache jądra IIS to aplikacja wiersza poleceń dostępnych w IIS7 i powyżej (chociaż możesz być w stanie skopiować bibliotekę DLL pomocnika NETSH z V7 na system V6 - nie próbowałem go).
netsh http show cachestate
Aby uzyskać więcej informacji, zobacz MSDN Documentation of the show cachestate command. Możesz przekształcić to w "API", wykonując proces i analizując wyniki tekstowe.
Big Zastrzeżenie: Nigdy nie widziałem tej aplikacji wiersza polecenia faktycznie powrócić coś na moim serwerze, nawet dla aplikacji działających w trybie Classic. Nie wiem, dlaczego - ale aplikacja działa tak, jak widzę z innych wpisów online. (Np http://chrison.net/ViewingTheKernelCache.aspx)
Jeśli jesteś strasznie uczulony na proces tworzenia i uczucie ambitne netsh polecenia są realizowane przez DLL z udokumentowanym interfejs Win32, więc można napisać kod, który udaje, że to netsh.exe i stawia pod NETSH pomocnika IIS DLL bezpośrednio. Możesz użyć documentation on MSDN jako punktu wyjścia dla tego podejścia. Ostrzeżenie: podszywanie się pod NETSH jest nietrywialne, ponieważ interfejs jest dwukierunkowy: wywołania NETSH do biblioteki DLL i wywołanie DLL z powrotem do NETSH. I nadal będziesz musiał przeanalizować wynik tekstowy, ponieważ interfejs NETSH jest oparty na tekście, a nie na obiektach takich jak PowerShell lub WMI. Gdyby to był ja, zamiast tego odrodziłem proces NETSH. ;-)
Jest możliwe, że IIS7 PowerShell snapin może obsługiwać tę funkcję w przyszłości (co oznacza łatwiejszy dostęp programistyczny niż powyższe hacki), ale tylko AFAIK NETSH obsługuje tę funkcję już dziś.
Unieważnienie:
Mam dobrą i złą wiadomość dla ciebie.
Dobra wiadomość: gdy poznasz adres URL przedmiotu, który chcesz przeciągnąć z pamięci podręcznej jądra IIS, dostępny jest interfejs Win32 API do usunięcia go w usługach IIS6 i nowszych. Można to wywołać z C# poprzez P/Invoke (trudniejsze) lub przez umieszczenie wywołania w zarządzanej bibliotece DLL C++. Szczegółowe informacje można znaleźć na stronie MSDN pod numerem HSE_REQ_GET_CACHE_INVALIDATION_CALLBACK.
Wziąłem ukłucie przy wymaganym kodzie (załączonym poniżej). Ostrzeżenie: jest brzydki i nieprzetestowany - nie psuje mojego IIS, ale (patrz wyżej) Nie mogę wymyślić, jak uruchomić wyliczanie pamięci podręcznej, więc nie mogę go wywołać z poprawnym adresem URL, aby pobrać z pamięci podręcznej. Jeśli możesz sprawić, by wyliczenie działało, wtedy podłączenie poprawnego adresu URL (i przetestowanie tego kodu) powinno być łatwe.
Zła wiadomość:
- jak można się domyślić z próbki kodu, to nie będzie działać w trybie zintegrowanym rurociągu IIS7 jest tylko w trybie Classic (lub IIS6, oczywiście) gdzie przebiega ASP.NET jako ISAPI i ma dostęp do funkcji ISAPI
- brudząc z pól prywatnych jest duża siekać i może pęknąć w nowej wersji
- P/Invoke jest trudne do zniesienia i wymaga (wierzę) pełne zaufanie
Oto kod:
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");
}
}
}
Istnieje dalsza dyskusja na temat tego problemu tutaj: http://www.west-wind.com/weblog/posts/11379.aspx#121596 (patrz komentarze). Czy ktoś ma lepsze rozwiązanie niż to, co jest tam wspomniane? –