.Net 中的缓存和 WebBrowser 控件
Posted
技术标签:
【中文标题】.Net 中的缓存和 WebBrowser 控件【英文标题】:Caching and the WebBrowser control in .Net 【发布时间】:2010-11-19 13:59:25 【问题描述】:我在 .Net 中使用 WebBrowser 控件来执行一些第 3 方联属网络营销转换。
我在数据库中有一个队列表,其中包含要执行的所有脚本/图像。我在带有 WebBrowser 控件的 WinForms 应用程序中循环浏览所有这些内容。执行完脚本/图像后,我处置 WebBrowser 控件,将其设置为 null,然后使用新的 WebBrowser 控件实例对其进行更新。
考虑这个网址:http://renderserver/RenderScript.aspx?id=1
RenderScript.aspx 显示一个带有 URL 的图像,例如:http://3rdparty/img.ashx?id=9343
我使用 Fiddler 查看所有请求和响应,当同一个 URL 执行两次时,它使用某种缓存。该缓存存在于 WebBrowser 控件本身之下。
这个缓存意味着img.ashx没有被调用。
我尝试使用 Internet Explorer 请求 URL:http://renderserver/RenderScript.aspx?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。
【问题讨论】:
【参考方案1】:不久前我(相当)遇到了同样的问题。 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()
在调用导航方法之前。
【讨论】:
它陷入了僵局,我认为应该在“if (!returnValue && ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())”这一行将 !returnValue 更改为 returnValue。【参考方案2】:Fiddler 使用与知识库文章中基本相同的代码来清除 WinINET 缓存,我每天在 Win2k3 上使用它。
与其清除用户的整个缓存,正确的解决方法是设置正确的 HTTP 响应标头以禁止缓存。您可以在此处了解有关 WinINET 缓存的更多信息:http://www.enhanceie.com/redir/?id=httpperf
(或者,您可以简单地添加一个随机查询字符串参数;这样,每次控件遇到资源请求时,URL 都是不同的,因此会自动绕过缓存。)
【讨论】:
否定。它与缓存头无关。它们已经正确设置以避免缓存。正如我所指出的,其他浏览器没有缓存。 而且随机参数也不起作用。因为 ScriptRender 页面加载的第 3 方 html 完全不受我控制。所以我只能将参数添加到RenderScript页面。图片标签(有时是 IFrame,有时是 javascript)是使用静态 URL 加载的。 对不起,你错了。如果设置了适当的响应标头,WinINET/IE/WebOCs 将不会重用缓存的响应。 ASHX 发送的确切标头是什么?你能给我发一份网络捕获 (www.fiddlercap.com) 吗? 使用 IIS 7.5 传递 pdf 文件,没有缓存控制,WinINET 设置为自动绝对不会重新请求更新的 pdf 文件,并且至少从 IE 8 开始就被破坏了。 @NetMage——你很困惑。如果 no 缓存控制标头存在以禁止它,则标准指定响应可以被缓存和重用。【参考方案3】:试试这个...
[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;
【讨论】:
Utilities.Web.WebBrowserHelper.WebBrowserHelper.ClearCache();
定义在哪里?【参考方案4】:
每个人都链接到的那篇 kb 文章中有许多错误(所选答案的源代码来自哪里),我浪费了大约 2 天时间试图让它在所有必要的设置下工作。它是通过互联网复制粘贴的,根据操作系统和 IE 版本报告了许多错误。
Fiddler 最初由 Microsoft 员工编写,由 FiddlerCore.dll 提供支持。 Fiddler 的 Telerik(当前所有者/维护者/卖家)仍然免费更新、维护和赠送 FiddlerCore。如果您不想添加对 FiddlerCore 的引用,则可以反汇编 dll,它显示了调用所有这些可怕的 WinINet 函数的正确方法,但我认为在此处发布它会对 Telerik / plagarism 造成伤害。
目前,Fiddlercore 托管在这里:http://www.telerik.com/fiddler/fiddlercore
【讨论】:
【参考方案5】:原始代码来自 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);
【讨论】:
【参考方案6】:这应该可以解决问题:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
【讨论】:
以上是关于.Net 中的缓存和 WebBrowser 控件的主要内容,如果未能解决你的问题,请参考以下文章
从.net(C#)中的Webbrowser控件中检索选定的文本
C# WebBrowser 控件:清除缓存而不清除 cookie
在C# WinForm 里WebBrowser这个控件能清楚Cookie和浏览器里的缓存么?要代码 急急急急急
VB.NET WebBrowser 控件 OnClick 事件未触发