C# WebBrowser 控件缓存问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1608543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WebBrowser control caching issue
提问by Gurpreet Singh
I am using the WebBrowser control inside a Windows Form to display a PDF.
我在 Windows 窗体中使用 WebBrowser 控件来显示 PDF。
Whenever the PDF is regenerated, however, the WebBrowser control only displays its local cached version and not the updated version from the server.
但是,每当重新生成 PDF 时,WebBrowser 控件仅显示其本地缓存版本,而不显示来自服务器的更新版本。
I am using the Refresh() method shown below to try and force the control to reload the PDF, but it doesn't work:
我正在使用如下所示的 Refresh() 方法来尝试强制控件重新加载 PDF,但它不起作用:
_webBrowser.Navigate(pdfUrl);
_webBrowser.Refresh(WebBrowserRefreshOption.Completely)
Do I have to do anything else to force the refresh to reload the PDF from the server?
我是否还需要做任何其他事情来强制刷新以从服务器重新加载 PDF?
采纳答案by Chris Clark
Sheng Jiang is correct - you need the programmatically clear IE's cache. Here is sample code showing how to do this in c#: http://www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx
生姜是正确的 - 您需要以编程方式清除 IE 的缓存。以下是显示如何在 c# 中执行此操作的示例代码:http: //www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx
Copy in case the page goes offline:
复制以防页面脱机:
/**
* 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
}
}
It is based heavily on the Microsoft KB article here: http://support.microsoft.com/kb/326201
它主要基于此处的 Microsoft KB 文章:http: //support.microsoft.com/kb/326201
And to pre-empt the question - yes this is a huge pain in the neck, and no, there isn't another way around it. Good luck!
先发制人的问题 - 是的,这是一个巨大的颈部疼痛,不,没有其他方法可以解决它。祝你好运!
回答by nitzmahone
Is the PDF being embedded with an object tag or something? If so, asking the browser object to refresh won't have any effect- you have to script the PDF viewer to do the refresh (eg, get ahold of the PDF viewer object by ID from the webbrowser control), since that's what downloaded it.
PDF是否嵌入了对象标签或其他东西?如果是这样,要求浏览器对象刷新将不会有任何效果 - 您必须编写 PDF 查看器的脚本来执行刷新(例如,通过 webbrowser 控件的 ID 获取 PDF 查看器对象),因为那是下载它的内容.
回答by u109919
Since WebBrowser (actually IE's Trident engine) use WinInet for networking, you can use WinInet's cache management APIsto remove the cached files before navigating.
由于 WebBrowser(实际上是 IE 的 Trident 引擎)使用 WinInet 进行联网,因此您可以在导航之前使用 WinInet 的缓存管理 API删除缓存的文件。
回答by Vescan Petru
Add to the url a random id so the url is unique every time
向 url 添加一个随机 id,以便 url 每次都是唯一的
回答by Mike
Vescan Petru is on the right track I think. Clearing the IE browser caches is pretty heavy handed and not user friendly. Adapting the suggested MS code linked by Chris Clark to just clear the specific file from the cache looks an interesting bit of code to work on but the simplest answer would be to grab a new temporary file name and then make a copy of the target PDF file with that name. Then display the temp file - deleting it upon exit.
我认为 Vescan Petru 走在正确的轨道上。清除 IE 浏览器缓存非常繁琐,而且对用户不友好。修改 Chris Clark 链接的建议 MS 代码以仅从缓存中清除特定文件看起来是一个有趣的代码,但最简单的答案是获取一个新的临时文件名,然后复制目标 PDF 文件用那个名字。然后显示临时文件 - 退出时删除它。
That is my working solution implemented when I hit the same problem.
这是我遇到同样问题时实施的工作解决方案。
回答by Clive
To Prevent CSS caching you can use a sneaky trick when including the css file:
为了防止 CSS 缓存,您可以在包含 css 文件时使用一个偷偷摸摸的技巧:
<link type="text/css" href="css/outlook.css?<?php echo date('l jS \of F Y h:i:s A'); ?>" rel="stylesheet" />
It downloads a new copy of the css because the ?date_stuff changes every time.
它下载 css 的新副本,因为 ?date_stuff 每次都会改变。
回答by Serj Sagan
This is an extension to Chris Clark's answer... this code is too important, I figure stack overflow is a more secure place to keep it, plus I got rid of the line numbers for you. Here's a link to the original article again: http://www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx
这是克里斯克拉克答案的扩展......这段代码太重要了,我认为堆栈溢出是一个更安全的地方来保存它,而且我为你摆脱了行号。这是原始文章的链接:http: //www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx
And the code:
和代码:
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
}
}
回答by Oliver Bock
Further to Serj Sagan, here is the code with bizarre error handling simplified, the infinite loop removed, and 32/64-bit capable.
在 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);
}
}
}
回答by x5657
We found that completely clearing the cache takes too long. An alternative that seems to work well (and fixes the problem in the original question of Refresh not working) is to move the call to Refresh after the document has finished loading.
我们发现完全清除缓存需要很长时间。一个看起来运行良好的替代方法(并修复了原始问题 Refresh not work 中的问题)是在文档加载完成后将调用移动到 Refresh。
webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(OnWebBrowserDocumentCompleted);
// ...
private void OnWebBrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (cacheIsStale)
{
webBrowser.Refresh(WebBrowserRefreshOption.Completely);
}
}
How to determine the value of cacheIsStale
will depend on your application.
如何确定 的值cacheIsStale
将取决于您的应用程序。
回答by Mark K
For those of us who still need to write VB.NET, I ported the code posted by Serj Sagan and Oliver Bock above to VB and found that it works well.
对于我们这些仍然需要编写VB.NET的人,我将上面Serj Sagan和Oliver Bock发布的代码移植到VB中,发现它运行良好。
Thank goodness this thread exists, I was really sweating this issue for a long while.
谢天谢地,这个线程存在,我真的很长一段时间都在为这个问题出汗。
'
' Modified from code originally found here: http://support.microsoft.com/kb/326201
'
Public Class WebBrowserHelper
Region "Definitions/DLL Imports"
区域“定义/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
End Region
结束区域
''' <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