【问题标题】:Caching and the WebBrowser control in .Net.Net 中的缓存和 WebBrowser 控件
【发布时间】: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。

【问题讨论】:

    标签: c# winforms webbrowser-control


    【解决方案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);
        

        【讨论】:

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