Кэширования и управления WebBrowser in.Net

Я использую элемент управления WebBrowser в .Net для выполнения некоторых сторонних партнерских маркетинговых преобразований.

У меня есть таблица очередей в базе данных со всеми сценариями/изображениями для выполнения. Я просматриваю все это в приложении WinForms с помощью элемента управления WebBrowser. После выполнения скрипта / образа я удаляю элемент управления WebBrowser, устанавливаю его в значение null и обновляю его новым экземпляром элемента управления WebBrowser.

рассмотрим этот URL: http://renderserver/RenderScript.аспн?id=1

RenderScript.aspx отображает изображение с URL-адресом, например:http://3rdparty/img.данного?id=9343

Я использую Fiddler для просмотра всех запросов и ответов, и когда один и тот же URL-адрес выполняется дважды, он использует какой-то кэш. Этот кэш существует под самим элементом управления WebBrowser.

этот кэш означает, что img.ashx не называется.

Я попытался использовать Internet Explorer для запрос на URL-адрес: http://renderserver/RenderScript.аспн?id=1 и нажмите F5. Тогда он запрашивается идеально.

но если я нажму адресную строку и нажму Enter, чтобы снова перейти к тому же URL - адресу-он не запрашивается. Когда я использую Firefox, будет запрашивать страницу и изображение каждый раз, независимо от того, использую ли я F5 или перемещаюсь из адресной строки.

Я нашел несколько вызовов Win32 API (http://support.microsoft.com/kb/326201), который смог очистить кэш. Он работал на моей локальной машине. Затем приложение было развернуто на сервере под управлением Windows Server 2003 Standard x64 (моя собственная машина-Vista x86).

и теперь вызовы API для очистки кэша не работают.

любые идеи о том, почему вызовы API не работают на Windows Server, но работает на Vista? Обе машины работают под управлением IE8.

6 ответов


у меня была та же проблема (очень) давно. У Microsoft есть страница, которая была очень любезна с этого:

http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q326/2/01.asp&NoWebContent=1

Я создал класс из образца Microsoft, однако мне также пришлось добавить пару операторов if, чтобы остановить обработку, когда больше нет элементов; прошло некоторое время, но я уверен, что это вызовет ошибку (см. ERROR_NO_MORE_ITEMS в коде ниже).

Я надеюсь, что это полезно!

 using System;
 using System.Runtime.InteropServices;

 // copied from: http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q326/2/01.asp&NoWebContent=1

 namespace PowerCode
 {
     public class IECache
     {
         // For PInvoke: Contains information about an entry in the Internet cache
         [StructLayout(LayoutKind.Explicit, Size = 80)]
         public struct INTERNET_CACHE_ENTRY_INFOA
         {
             [FieldOffset(0)]
             public uint dwStructSize;
             [FieldOffset(4)]
             public IntPtr lpszSourceUrlName;
             [FieldOffset(8)]
             public IntPtr lpszLocalFileName;
             [FieldOffset(12)]
             public uint CacheEntryType;
             [FieldOffset(16)]
             public uint dwUseCount;
             [FieldOffset(20)]
             public uint dwHitRate;
             [FieldOffset(24)]
             public uint dwSizeLow;
             [FieldOffset(28)]
             public uint dwSizeHigh;
             [FieldOffset(32)]
             public FILETIME LastModifiedTime;
             [FieldOffset(40)]
             public FILETIME ExpireTime;
             [FieldOffset(48)]
             public FILETIME LastAccessTime;
             [FieldOffset(56)]
             public FILETIME LastSyncTime;
             [FieldOffset(64)]
             public IntPtr lpHeaderInfo;
             [FieldOffset(68)]
             public uint dwHeaderInfoSize;
             [FieldOffset(72)]
             public IntPtr lpszFileExtension;
             [FieldOffset(76)]
             public uint dwReserved;
             [FieldOffset(76)]
             public uint dwExemptDelta;
         }

         // For PInvoke: Initiates the enumeration of the cache groups in the Internet cache
         [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindFirstUrlCacheGroup", CallingConvention = CallingConvention.StdCall)]
         public static extern IntPtr FindFirstUrlCacheGroup( int dwFlags, int dwFilter, IntPtr lpSearchCondition, int dwSearchCondition, ref long lpGroupId, IntPtr lpReserved );

         // For PInvoke: Retrieves the next cache group in a cache group enumeration
         [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindNextUrlCacheGroup", CallingConvention = CallingConvention.StdCall)]
         public static extern bool FindNextUrlCacheGroup( IntPtr hFind, ref long lpGroupId, IntPtr lpReserved );

         // For PInvoke: Releases the specified GROUPID and any associated state in the cache index file
         [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "DeleteUrlCacheGroup", CallingConvention = CallingConvention.StdCall)]
         public static extern bool DeleteUrlCacheGroup( long GroupId, int dwFlags, IntPtr lpReserved );

         // For PInvoke: Begins the enumeration of the Internet cache
         [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindFirstUrlCacheEntryA", CallingConvention = CallingConvention.StdCall)]
         public static extern IntPtr FindFirstUrlCacheEntry( [MarshalAs(UnmanagedType.LPTStr)] string lpszUrlSearchPattern, IntPtr lpFirstCacheEntryInfo, ref int lpdwFirstCacheEntryInfoBufferSize );

         // For PInvoke: Retrieves the next entry in the Internet cache
         [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "FindNextUrlCacheEntryA", CallingConvention = CallingConvention.StdCall)]
         public static extern bool FindNextUrlCacheEntry( IntPtr hFind, IntPtr lpNextCacheEntryInfo, ref int lpdwNextCacheEntryInfoBufferSize );

         // For PInvoke: Removes the file that is associated with the source name from the cache, if the file exists
         [DllImport(@"wininet", SetLastError = true, CharSet = CharSet.Auto, EntryPoint = "DeleteUrlCacheEntryA", CallingConvention = CallingConvention.StdCall)]
         public static extern bool DeleteUrlCacheEntry( IntPtr lpszUrlName );

         public static void ClearCache()
         {
             // Indicates that all of the cache groups in the user's system should be enumerated
             const int CACHEGROUP_SEARCH_ALL = 0x0;
             // Indicates that all the cache entries that are associated with the cache group
             // should be deleted, unless the entry belongs to another cache group.
             const int CACHEGROUP_FLAG_FLUSHURL_ONDELETE = 0x2;
             // File not found.
             const int ERROR_FILE_NOT_FOUND = 0x2;
             // No more items have been found.
             const int ERROR_NO_MORE_ITEMS = 259;
             // Pointer to a GROUPID variable
             long groupId = 0;

             // Local variables
             int cacheEntryInfoBufferSizeInitial = 0;
             int cacheEntryInfoBufferSize = 0;
             IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
             INTERNET_CACHE_ENTRY_INFOA internetCacheEntry;
             IntPtr enumHandle = IntPtr.Zero;
             bool returnValue = false;

             // Delete the groups first.
             // Groups may not always exist on the system.
             // For more information, visit the following Microsoft Web site:
             // http://msdn.microsoft.com/library/?url=/workshop/networking/wininet/overview/cache.asp            
             // By default, a URL does not belong to any group. Therefore, that cache may become
             // empty even when the CacheGroup APIs are not used because the existing URL does not belong to any group.            
             enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);

             // If there are no items in the Cache, you are finished.
             if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
                 return;
             }

             // Loop through Cache Group, and then delete entries.
             while (true) {
                 if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()) {
                     break;
                 }

                 // Delete a particular Cache Group.
                 returnValue = DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
                 if (!returnValue && ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()) {
                     returnValue = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
                 }

                 if (!returnValue && (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())) {
                     break;
                 }
             }

             // Start to delete URLs that do not belong to any group.
             enumHandle = FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);
             if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
                 return;
             }

             cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
             cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
             enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);

             while (true) {
                 internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));

                 if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
                     break;
                 }

                 cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
                 returnValue = DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);
                 if (!returnValue) {
                     returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                 }
                 if (!returnValue && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()) {
                     break;
                 }
                 if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize) {
                     cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
                     cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr)cacheEntryInfoBufferSize);
                     returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                 }
             }
             Marshal.FreeHGlobal(cacheEntryInfoBuffer);
         }
     }
 }

использовать его в коде, просто звоните:

IECache.ClearCache()

перед вызовом методов навигации.


Fiddler использует принципиально тот же код, что и в статье KB, чтобы очистить кэш WinINET, и я использую его на Win2k3 каждый день.

вместо того, чтобы стирать весь кэш пользователя, правильное исправление-установить правильный заголовок HTTP-ответа, чтобы запретить кэширование. Вы можете узнать больше о кэшировании WinINET здесь:http://www.enhanceie.com/redir/?id=httpperf

(альтернативно, вы можете просто добавить случайный параметр строки запроса, чтобы каждый раз элемент управления обнаруживает запрос на ресурс, URL-адрес отличается и кэш, таким образом, автоматически обходится.)


попробуйте это...

[DllImport("wininet.dll", SetLastError = true)]
        private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int lpdwBufferLength);
private const int INTERNET_OPTION_END_BROWSER_SESSION = 42;
private void clearCache()
{
    try
    {
        Utilities.Web.WebBrowserHelper.WebBrowserHelper.ClearCache();
        InternetSetOption(IntPtr.Zero, INTERNET_OPTION_END_BROWSER_SESSION, IntPtr.Zero, 0);
    }
    catch (Exception exception)
    {
        //throw;
    }

}

в этой статье kb, на которую все ссылаются, есть множество ошибок (откуда пришел исходный код выбранного ответа), и я потратил ~2 дня, пытаясь заставить его работать во всех необходимых настройках. Это копия, вставленная через интернет, и есть многочисленные ошибки, сообщенные на основе ОС и версии IE.

Fiddler был первоначально написан сотрудником Microsoft и питается от FiddlerCore.файл DLL. Telerik (текущие владельцы/сопровождающие / продавцы) Fiddler по-прежнему обновлять, поддерживать и отдайте FiddlerCore бесплатно. Если вы не хотите добавлять ссылку на FiddlerCore, вы можете разобрать dll, и он показывает правильный способ вызова всех этих ужасно документированных функций WinINet, но я думаю, что публикация его здесь будет медвежьей услугой Telerik / плагарист.

В настоящее время Fiddlercore размещен здесь:http://www.telerik.com/fiddler/fiddlercore


исходный код https://support.microsoft.com/en-us/kb/326201 кажется багги

проверка документации MSDN, а также версии VB здесь: https://support.microsoft.com/en-us/kb/262110

Я изменил код, как это, и теперь он работает для меня (проблема заключалась в выполнении FindNextUrlCacheGroup и FindNextUrlCacheEntry):

using System;
using System.Runtime.InteropServices;

namespace Q326201CS
{
    // Class for deleting the cache.
    public class DeleteIECache
    {
        // For PInvoke: Contains information about an entry in the Internet cache
        [StructLayout(LayoutKind.Explicit, Size=80)]
        public struct INTERNET_CACHE_ENTRY_INFOA
        {
            [FieldOffset(0)]  public uint dwStructSize;
            [FieldOffset(4)]  public IntPtr lpszSourceUrlName;
            [FieldOffset(8)]  public IntPtr lpszLocalFileName;
            [FieldOffset(12)] public uint CacheEntryType;
            [FieldOffset(16)] public uint dwUseCount;
            [FieldOffset(20)] public uint dwHitRate;
            [FieldOffset(24)] public uint dwSizeLow;
            [FieldOffset(28)] public uint dwSizeHigh;
            [FieldOffset(32)] public FILETIME LastModifiedTime;
            [FieldOffset(40)] public FILETIME ExpireTime;
            [FieldOffset(48)] public FILETIME LastAccessTime;
            [FieldOffset(56)] public FILETIME LastSyncTime;
            [FieldOffset(64)] public IntPtr lpHeaderInfo;
            [FieldOffset(68)] public uint dwHeaderInfoSize;
            [FieldOffset(72)] public IntPtr lpszFileExtension;
            [FieldOffset(76)] public uint dwReserved;
            [FieldOffset(76)] public uint dwExemptDelta;
        }

        // For PInvoke: Initiates the enumeration of the cache groups in the Internet cache
        [DllImport(@"wininet",
            SetLastError=true,
            CharSet=CharSet.Auto,
            EntryPoint="FindFirstUrlCacheGroup",
            CallingConvention=CallingConvention.StdCall)]
        public static extern IntPtr FindFirstUrlCacheGroup(
            int dwFlags,
            int dwFilter,
            IntPtr lpSearchCondition,
            int dwSearchCondition,
            ref long lpGroupId,
            IntPtr lpReserved);

        // For PInvoke: Retrieves the next cache group in a cache group enumeration
        [DllImport(@"wininet",
            SetLastError=true,
            CharSet=CharSet.Auto,
            EntryPoint="FindNextUrlCacheGroup",
            CallingConvention=CallingConvention.StdCall)]
        public static extern bool FindNextUrlCacheGroup(
            IntPtr hFind,
            ref long lpGroupId,
            IntPtr lpReserved);

        // For PInvoke: Releases the specified GROUPID and any associated state in the cache index file
        [DllImport(@"wininet", 
            SetLastError=true, 
            CharSet=CharSet.Auto, 
            EntryPoint="DeleteUrlCacheGroup", 
            CallingConvention=CallingConvention.StdCall)]
        public static extern bool DeleteUrlCacheGroup(
            long GroupId,
            int dwFlags,
            IntPtr lpReserved);

        // For PInvoke: Begins the enumeration of the Internet cache
        [DllImport(@"wininet",
            SetLastError=true,
            CharSet=CharSet.Auto,
            EntryPoint="FindFirstUrlCacheEntryA",
            CallingConvention=CallingConvention.StdCall)]
        public static extern IntPtr FindFirstUrlCacheEntry(
            [MarshalAs(UnmanagedType.LPTStr)] string lpszUrlSearchPattern,
            IntPtr lpFirstCacheEntryInfo,
            ref int lpdwFirstCacheEntryInfoBufferSize);

        // For PInvoke: Retrieves the next entry in the Internet cache
        [DllImport(@"wininet",
            SetLastError=true,
            CharSet=CharSet.Auto,
            EntryPoint="FindNextUrlCacheEntryA",
            CallingConvention=CallingConvention.StdCall)]
        public static extern bool FindNextUrlCacheEntry(
            IntPtr hFind,
            IntPtr lpNextCacheEntryInfo,
            ref int lpdwNextCacheEntryInfoBufferSize);

        // For PInvoke: Removes the file that is associated with the source name from the cache, if the file exists
        [DllImport(@"wininet",
            SetLastError=true,
            CharSet=CharSet.Auto,
            EntryPoint="DeleteUrlCacheEntryA",
            CallingConvention=CallingConvention.StdCall)]
        public static extern bool DeleteUrlCacheEntry(
            IntPtr lpszUrlName);


        public static void doDelete()
        {
            // Indicates that all of the cache groups in the user's system should be enumerated
            const int CACHEGROUP_SEARCH_ALL = 0x0;
            // Indicates that all the cache entries that are associated with the cache group
            // should be deleted, unless the entry belongs to another cache group.
            const int CACHEGROUP_FLAG_FLUSHURL_ONDELETE = 0x2;
            // File not found.
            const int ERROR_FILE_NOT_FOUND = 0x2;
            // No more items have been found.
            const int ERROR_NO_MORE_ITEMS = 259;
            // Pointer to a GROUPID variable
            long groupId = 0;

            // Local variables
            int cacheEntryInfoBufferSizeInitial = 0;
            int cacheEntryInfoBufferSize = 0;
            IntPtr cacheEntryInfoBuffer = IntPtr.Zero;
            INTERNET_CACHE_ENTRY_INFOA internetCacheEntry;
            IntPtr enumHandle = IntPtr.Zero;
            bool returnValue = false;

            // Delete the groups first.
            // Groups may not always exist on the system.
            // For more information, visit the following Microsoft Web site:
            // http://msdn.microsoft.com/library/?url=/workshop/networking/wininet/overview/cache.asp           
            // By default, a URL does not belong to any group. Therefore, that cache may become
            // empty even when the CacheGroup APIs are not used because the existing URL does not belong to any group.          
            enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
            // If there are no items in the Cache, you are finished.
            if (enumHandle != IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                return;

            // Loop through Cache Group, and then delete entries.
            while(true)
            {
                if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())
                {
                    break;
                }

                // Delete a particular Cache Group.
                returnValue = DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
                //if (returnValue || (!returnValue && ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()))
                //{ 
                    returnValue = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
                //}

                if (!returnValue && (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error()))
                    break;
            }

            // Start to delete URLs that do not belong to any group.
            enumHandle = FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);
            if (enumHandle == IntPtr.Zero && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                return;

            cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
            cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
            enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);

            while(true)
            {
                internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));

                if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                {
                    break;
                } 

                cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
                returnValue = DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);                
                //if (!returnValue)
                //{ 
                    returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                //}
                if (!returnValue && ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error())
                {
                    break;
                }           
                if (!returnValue && cacheEntryInfoBufferSizeInitial > cacheEntryInfoBufferSize)
                {
                    cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
                    cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr) cacheEntryInfoBufferSize);
                    returnValue = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);                 
                }
            }
            Marshal.FreeHGlobal(cacheEntryInfoBuffer);      
        }
    }
}

это должно сделать трюк:

Response.Cache.SetCacheability(HttpCacheability.NoCache);