title: C# WebBrowser control: Clearing cache without clearing cookies
body: <p>I have this code that clears the cache in a C# WebBrowser control. The problem with it is that it also clears the cookies. I seem to be the only person on the entire internet that doesn't want that.</p>
  
  <p>I need to maintain cookies, but to throw away the cache.</p>
  
  <p>Of particular interest is this line:</p>
  
  <pre><code>const int CACHEGROUP_SEARCH_ALL = 0x0;</code></pre>
  
  <p>It seems to define which "cache groups" (whatever the heck those are) are cleaned out, and I'm hoping that cookies are a cache group that I can skip somehow. However, trying to find any information on this at all has produced nothing but a pounding headache.</p>
  
  <p>This code is originally taken from an MSDN article, but it doesn't even mention cookies or cache groups.</p>
  
  <p>You can see the MSDN article at the top of the code.</p>
  
  <pre><code>/**
   * Modified from code originally found here: http://support.microsoft.com/kb/326201
   **/
  
  using System;
  using System.Runtime.InteropServices;
  using System.Runtime.InteropServices.ComTypes;
  using System.Diagnostics;
  
  namespace Goop
  {
      // Class for deleting the cache.
      public static class WebBrowserHelper
      {
          #region Definitions/DLL Imports
          // 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 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
          /// 
          /// Clears the cache of the web browser
          /// 
          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() || cacheEntryInfoBufferSize == 0)
                  {
                      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
      }
  }
  </code>
  </pre>
  
  <p>Any and all help greatly appreciated.</p>