【问题标题】:WebBrowser control caching issueWebBrowser 控件缓存问题
【发布时间】:2010-12-09 04:12:57
【问题描述】:

我在 Windows 窗体中使用 WebBrowser 控件来显示 PDF。

然而,每当重新生成 PDF 时,WebBrowser 控件只会显示其本地缓存版本,而不是来自服务器的更新版本。

我正在使用如下所示的 Refresh() 方法来尝试强制控件重新加载 PDF,但它不起作用:

_webBrowser.Navigate(pdfUrl);

_webBrowser.Refresh(WebBrowserRefreshOption.Completely)

我是否必须执行其他任何操作来强制刷新以从服务器重新加载 PDF?

【问题讨论】:

  • 如果您在 IE 中(在您的应用程序之外)加载相同的 URL,它会表现出相同的行为吗?

标签: c# caching controls browser


【解决方案1】:

除了 Serj Sagan,这里是简化了奇怪错误处理、删除了无限循环并且支持 32/64 位的代码。

/**
* Modified from code originally found here: http://support.microsoft.com/kb/326201
**/
public class WebBrowserHelper
{
    #region Definitions/DLL Imports
    /// <summary>
    /// For PInvoke: Contains information about an entry in the Internet cache
    /// </summary>
    [StructLayout(LayoutKind.Explicit)]
    public struct ExemptDeltaOrReserverd
    {
        [FieldOffset(0)]
        public UInt32 dwReserved;
        [FieldOffset(0)]
        public UInt32 dwExemptDelta;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct INTERNET_CACHE_ENTRY_INFOA
    {
        public UInt32 dwStructSize;
        public IntPtr lpszSourceUrlName;
        public IntPtr lpszLocalFileName;
        public UInt32 CacheEntryType;
        public UInt32 dwUseCount;
        public UInt32 dwHitRate;
        public UInt32 dwSizeLow;
        public UInt32 dwSizeHigh;
        public FILETIME LastModifiedTime;
        public FILETIME ExpireTime;
        public FILETIME LastAccessTime;
        public FILETIME LastSyncTime;
        public IntPtr lpHeaderInfo;
        public UInt32 dwHeaderInfoSize;
        public IntPtr lpszFileExtension;
        public ExemptDeltaOrReserverd dwExemptDeltaOrReserved;
    }

    // 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);
    #endregion

    /// <summary>
    /// Clears the cache of the web browser
    /// </summary>
    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;
        const int ERROR_INSUFFICIENT_BUFFER = 0x7A;

        // 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.            
        long groupId = 0;
        IntPtr enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
        if (enumHandle != IntPtr.Zero) {
            bool more;
            do {
                // Delete a particular Cache Group.
                DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero);
                more = FindNextUrlCacheGroup(enumHandle, ref groupId, IntPtr.Zero);
            } while (more);
        }

        // Start to delete URLs that do not belong to any group.
        int cacheEntryInfoBufferSizeInitial = 0;
        FindFirstUrlCacheEntry(null, IntPtr.Zero, ref cacheEntryInfoBufferSizeInitial);  // should always fail because buffer is too small
        if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) {
            int cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
            IntPtr cacheEntryInfoBuffer = Marshal.AllocHGlobal(cacheEntryInfoBufferSize);
            enumHandle = FindFirstUrlCacheEntry(null, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
            if (enumHandle != IntPtr.Zero) {
                bool more;
                do {
                    INTERNET_CACHE_ENTRY_INFOA internetCacheEntry = (INTERNET_CACHE_ENTRY_INFOA)Marshal.PtrToStructure(cacheEntryInfoBuffer, typeof(INTERNET_CACHE_ENTRY_INFOA));
                    cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize;
                    DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName);
                    more = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                    if (!more && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) {
                        cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial;
                        cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, (IntPtr)cacheEntryInfoBufferSize);
                        more = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, ref cacheEntryInfoBufferSizeInitial);
                    }
                } while (more);
            }
            Marshal.FreeHGlobal(cacheEntryInfoBuffer);
        }
    }
}

【讨论】:

  • 非常感谢您的版本!您能否简要解释一下问题是什么,它没有在 64 位架构上运行以及导致无限循环的原因?
  • @melodia,请参阅上面的 Sandra Walters 无限循环说明。我认为 64 位问题与显式 FieldOffset 属性有关。它们不是必需的,而且会弄乱 IntPtr 字段,在 64 位情况下需要 8 个字节。
  • 我也遇到了同样的问题,我试过你的答案,它只有在重新启动应用程序时才有效。我正在使用Office365 登录。我需要在我的应用程序中停止office365 缓存。你有什么想法或建议来摆脱这个问题。
  • @OliverBock 出于某种原因,它只删除了几个条目,并将大部分内容保留在 Temporary Internet FIles\Low\Content 子文件夹中。
  • @MihaMarkic,我只能建议您追踪它并尝试看看出了什么问题。
【解决方案2】:

Shengjiang 是对的——你需要以编程方式清除 IE 的缓存。这是显示如何在 c# 中执行此操作的示例代码: http://www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx

复制以防页面离线:

/**
* Modified from code originally found here: 
http://support.microsoft.com/kb/326201
**/

#region Usings
using System;
using System.Runtime.InteropServices;

#endregion

namespace Utilities.Web.WebBrowserHelper
{
    /// <summary>
    /// Class for clearing the cache
    /// </summary>
    public static class WebBrowserHelper
    {
        #region Definitions/DLL Imports
        /// <summary>
        /// For PInvoke: Contains information about an entry in the Internet cache
        /// </summary>
        [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 System.Runtime.InteropServices.ComTypes.FILETIME LastModifiedTime;
            [FieldOffset(40)]
            public System.Runtime.InteropServices.ComTypes.FILETIME ExpireTime;
            [FieldOffset(48)]
            public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
            [FieldOffset(56)]
            public System.Runtime.InteropServices.ComTypes.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);
        #endregion

        #region Public Static Functions

        /// <summary>
        /// Clears the cache of the web browser
        /// </summary>
        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);
        }
        #endregion
    }
}

它主要基于此处的 Microsoft 知识库文章: http://support.microsoft.com/kb/326201

为了抢先提出这个问题 - 是的,这是一个巨大的颈部疼痛,不,没有其他方法可以解决它。祝你好运!

【讨论】:

    【解决方案3】:

    这是对 Chris Clark 的回答的扩展......这段代码太重要了,我认为堆栈溢出是一个更安全的地方来保存它,另外我为你摆脱了行号。这里又是原文章的链接:http://www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx

    还有代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    
    namespace Utilities.Web.WebBrowserHelper
    {
        /**
        * Modified from code originally found here: http://support.microsoft.com/kb/326201
        **/
        public class WebBrowserHelper
        {
            #region Definitions/DLL Imports
            /// <summary>
            /// For PInvoke: Contains information about an entry in the Internet cache
            /// </summary>
            [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 System.Runtime.InteropServices.ComTypes.FILETIME LastModifiedTime;
                [FieldOffset(40)]
                public System.Runtime.InteropServices.ComTypes.FILETIME ExpireTime;
                [FieldOffset(48)]
                public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
                [FieldOffset(56)]
                public System.Runtime.InteropServices.ComTypes.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);
            #endregion
    
            #region Public Static Functions
    
            /// <summary>
            /// Clears the cache of the web browser
            /// </summary>
            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);
            }
            #endregion
        }
    }
    

    【讨论】:

    • 如果没有错误条件,ClearCache() 中的第一个“while(true)”循环似乎进入了无限循环; returnValue 为 true,GetLastWin32Error 返回 0,因此永远不会检索下一个 url 缓存组。我认为 DeleteUrlCacheGroup() 过去的行为与现在不同。
    • 也不适用于 64 位。请参阅下面的答案以获取更正的版本。
    • 不,真正的循环不是无限的......它只在有错误时循环,正如中断前的 if 语句条件清楚地看到的那样:ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error()
    • @SerjSagan 它是无限的。 Marshal.GetLastWin32Error() 总是返回 0,因此循环永远不会结束。
    • @MihaMarkic 这当然不是真的。 Marshal.GetLastWin32Error() 并不总是返回 0。现在,我不争论是否存在无限可能的情况,因为我猜 Marshal.GetLastWin32Error() 从不 == ERROR_NO_MORE_ITEMS 是可行的,但这与你的错误不同正在断言。
    【解决方案4】:

    由于WebBrowser(实际上是IE的Trident引擎)使用WinInet进行联网,所以可以在导航前使用WinInet的cache management APIs删除缓存文件。

    【讨论】:

      【解决方案5】:

      在 url 中添加一个随机 id,这样 url 每次都是唯一的

      【讨论】:

      • 强制重新加载外部样式表 (CSS) 等资源时,这对我不起作用。
      【解决方案6】:

      我们发现完全清除缓存需要很长时间。另一种似乎运作良好的替代方法(并解决了原始问题 Refresh not working 中的问题)是在文档完成加载后将调用移至 Refresh。

      webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(OnWebBrowserDocumentCompleted);
      
      // ...
      
      private void OnWebBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
      {
          if (cacheIsStale)
          {
              webBrowser.Refresh(WebBrowserRefreshOption.Completely);
          }
      }
      

      如何确定cacheIsStale 的值取决于您的应用程序。

      【讨论】:

        【解决方案7】:

        为了防止 CSS 缓存,您可以在包含 css 文件时使用一个偷偷摸摸的技巧:

        它会下载一个新的 css 副本,因为 ?date_stuff 每次都会改变。

        【讨论】:

        • 我想你误解了这个问题?
        • 一旦将“css”替换为“pdf”,这实际上是一个非常干净的答案。每次只生成一个新的文件名。
        【解决方案8】:

        PDF 是否嵌入了对象标签或其他内容?如果是这样,要求浏览器对象刷新不会有任何效果 - 您必须编写 PDF 查看器的脚本以执行刷新(例如,从 webbrowser 控件通过 ID 获取 PDF 查看器对象),因为这是下载它的内容.

        【讨论】:

          【解决方案9】:

          我认为 Vescan Petru 走在正确的轨道上。清除 IE 浏览器缓存非常繁琐,而且对用户不友好。调整由 Chris Clark 链接的建议 MS 代码以从缓存中清除特定文件看起来很有趣,但最简单的答案是获取一个新的临时文件名,然后复制目标 PDF 文件用那个名字。然后显示临时文件 - 退出时将其删除。

          这是我遇到同样问题时实施的工作解决方案。

          【讨论】:

            【解决方案10】:

            对于我们这些还需要编写VB.NET的人,我将上面Serj Sagan和Oliver Bock发布的代码移植到VB中,发现它运行良好。

            谢天谢地,这个话题存在,我真的为这个问题苦恼了很久。

                    '
                ' Modified from code originally found here: http://support.microsoft.com/kb/326201
                '
                Public Class WebBrowserHelper
            

            区域“定义/DLL 导入”

                    ''' <summary>
                    ''' For PInvoke: Contains information about an entry In the Internet cache
                    ''' </summary>
                    <StructLayout(LayoutKind.Explicit)>
                    Public Structure ExemptDeltaOrReserverd
                        <FieldOffset(0)>
                        Public dwReserved As UInt32
                        <FieldOffset(0)>
                        Public dwExemptDelta As UInt32
                    End Structure
            
                    <StructLayout(LayoutKind.Sequential)>
                    Public Structure INTERNET_CACHE_ENTRY_INFOA
                        Public dwStructSize As UInt32
                        Public lpszSourceUrlName As IntPtr
                        Public lpszLocalFileName As IntPtr
                        Public CacheEntryType As UInt32
                        Public dwUseCount As UInt32
                        Public dwHitRate As UInt32
                        Public dwSizeLow As UInt32
                        Public dwSizeHigh As UInt32
                        Public LastModifiedTime As FILETIME
                        Public ExpireTime As FILETIME
                        Public LastAccessTime As FILETIME
                        Public LastSyncTime As FILETIME
                        Public lpHeaderInfo As IntPtr
                        Public dwHeaderInfoSize As UInt32
                        Public lpszFileExtension As IntPtr
                        Public dwExemptDeltaOrReserved As ExemptDeltaOrReserverd
                    End Structure
            
                    ' 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 Shared Function FindFirstUrlCacheGroup(dwFlags As Integer, dwFilter As Integer, lpSearchCondition As IntPtr, dwSearchCondition As Integer, ByRef lpGroupId As Long,
                                        lpReserved As IntPtr) As IntPtr
                    End Function
            
                    ' 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 Shared Function FindNextUrlCacheGroup(hFind As IntPtr, ByRef lpGroupId As Long, lpReserved As IntPtr) As Boolean
                    End Function
            
                    ' 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 Shared Function DeleteUrlCacheGroup(GroupId As Long, dwFlags As Integer, lpReserved As IntPtr) As Boolean
                    End Function
            
                    ' For PInvoke: Begins the enumeration Of the Internet cache
                    <DllImport("wininet", SetLastError:=True, CharSet:=CharSet.Auto, EntryPoint:="FindFirstUrlCacheEntryA", CallingConvention:=CallingConvention.StdCall)>
                    Public Shared Function FindFirstUrlCacheEntry(<MarshalAs(UnmanagedType.LPTStr)> lpszUrlSearchPattern As String, lpFirstCacheEntryInfo As IntPtr,
                                                                  ByRef lpdwFirstCacheEntryInfoBufferSize As Integer) As IntPtr
                    End Function
            
                    ' For PInvoke: Retrieves the Next entry In the Internet cache
                    <DllImport("wininet", SetLastError:=True, CharSet:=CharSet.Auto, EntryPoint:="FindNextUrlCacheEntryA", CallingConvention:=CallingConvention.StdCall)>
                    Public Shared Function FindNextUrlCacheEntry(hFind As IntPtr, lpNextCacheEntryInfo As IntPtr, ByRef lpdwNextCacheEntryInfoBufferSize As Integer) As Boolean
                    End Function
            
                    ' 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 Shared Function DeleteUrlCacheEntry(lpszUrlName As IntPtr) As Boolean
                    End Function
            

            结束区域

                    ''' <summary>
                    ''' Clears the cache of the web browser
                    ''' </summary>
                    Public Shared Sub ClearCache()
                        ' Indicates that all of the cache groups in the user's system should be enumerated
                        Const CACHEGROUP_SEARCH_ALL As Integer = &H0
                        ' 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 CACHEGROUP_FLAG_FLUSHURL_ONDELETE As Integer = &H2
                        Const ERROR_INSUFFICIENT_BUFFER As Integer = &H7A
            
                        ' 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.            
                        Dim groupId As Long = 0
                        Dim enumHandle As IntPtr = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, groupId, IntPtr.Zero)
            
                        If (enumHandle <> IntPtr.Zero) Then
                            Dim more As Boolean
            
                            Do
                                ' Delete a particular Cache Group.
                                DeleteUrlCacheGroup(groupId, CACHEGROUP_FLAG_FLUSHURL_ONDELETE, IntPtr.Zero)
                                more = FindNextUrlCacheGroup(enumHandle, groupId, IntPtr.Zero)
                            Loop While (more)
                        End If
            
                        ' Start to delete URLs that do Not belong to any group.
                        Dim cacheEntryInfoBufferSizeInitial As Integer = 0
            
                        FindFirstUrlCacheEntry(Nothing, IntPtr.Zero, cacheEntryInfoBufferSizeInitial)  ' should always fail because buffer Is too small
            
                        If Marshal.GetLastWin32Error() = ERROR_INSUFFICIENT_BUFFER Then
                            Dim cacheEntryInfoBufferSize As Integer = cacheEntryInfoBufferSizeInitial
                            Dim cacheEntryInfoBuffer As IntPtr = Marshal.AllocHGlobal(cacheEntryInfoBufferSize)
            
                            enumHandle = FindFirstUrlCacheEntry(Nothing, cacheEntryInfoBuffer, cacheEntryInfoBufferSizeInitial)
            
                            If (enumHandle <> IntPtr.Zero) Then
                                Dim more As Boolean
                                Do
                                    Dim internetCacheEntry As INTERNET_CACHE_ENTRY_INFOA = CType(Marshal.PtrToStructure(cacheEntryInfoBuffer, GetType(INTERNET_CACHE_ENTRY_INFOA)), INTERNET_CACHE_ENTRY_INFOA)
            
                                    cacheEntryInfoBufferSizeInitial = cacheEntryInfoBufferSize
                                    DeleteUrlCacheEntry(internetCacheEntry.lpszSourceUrlName)
                                    more = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, cacheEntryInfoBufferSizeInitial)
            
                                    If Not more AndAlso Marshal.GetLastWin32Error() = ERROR_INSUFFICIENT_BUFFER Then
                                        cacheEntryInfoBufferSize = cacheEntryInfoBufferSizeInitial
                                        cacheEntryInfoBuffer = Marshal.ReAllocHGlobal(cacheEntryInfoBuffer, CType(cacheEntryInfoBufferSize, IntPtr))
                                        more = FindNextUrlCacheEntry(enumHandle, cacheEntryInfoBuffer, cacheEntryInfoBufferSizeInitial)
                                    End If
                                Loop While (more)
                            End If
            
                            Marshal.FreeHGlobal(cacheEntryInfoBuffer)
                        End If
                    End Sub
                End Class
            

            【讨论】:

              【解决方案11】:
              TimeSpan serialNoCache = DateTime.Now.TimeOfDay;
              string urlNocache = string.Format("{0}?v={1}", "https://www.google.com/", serialNoCache.ToString());
              this.webBrowser.Navigate(urlNocache);
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2010-11-19
                • 2018-12-09
                • 1970-01-01
                • 1970-01-01
                • 2011-04-11
                • 2011-03-26
                相关资源
                最近更新 更多