C# WebBrowser control: Clearing cache without clearing cookies

14,444

Solution 1

FiddlerCore exposes a method with exactly that signature. ClearCacheItems(bool clearCookies, bool clearFiles).

--RANT--

That kb article everyone links to has numerous errors in it (where the other answers' source code came from), and I've wasted ~2 days trying to get it to work in all necessary settings. It is copy pasted across the internet, and there are numerous bugs reported based on OS and IE version.

Fiddler was originally written by a Microsoft employee, and is powered by FiddlerCore.dll. Telerik (the current owners/maintainers/sellers) of Fiddler still update, maintain and give away FiddlerCore for free. If you don't want to add a reference to FiddlerCore, you can disassemble the dll, and it shows the CORRECT way to call all of these horribly documented WinINet functions, but I think posting it here would be a disservice to Telerik / plagarism.

Currently, Fiddlercore is hosted here: http://www.telerik.com/fiddler/fiddlercore

Solution 2

Check the documentation for the INTERNET_CACHE_ENTRY_INFOA, especially the part that describes the CacheEntryType:

A bitmask indicating the type of cache entry and its properties. The cache entry types include: history entries (URLHISTORY_CACHE_ENTRY), cookie entries (COOKIE_CACHE_ENTRY), and normal cached content (NORMAL_CACHE_ENTRY).

This member can be zero or more of the following property flags, and cache type flags listed below.

Below that you get a list of entry types, one is

COOKIE_CACHE_ENTRY

That seems to be what you want to check before you throw everything away?

Solution 3

The condition should be below, I use VB.

Dim icei As INTERNET_CACHE_ENTRY_INFO

......

If (icei.CacheEntryType And COOKIE_CACHE_ENTRY) <> COOKIE_CACHE_ENTRY And _
                (icei.CacheEntryType And NORMAL_CACHE_ENTRY) = NORMAL_CACHE_ENTRY Then

      cachefile = GetStrFromPtrA(icei.lpszSourceUrlName)

      Call DeleteUrlCacheEntry(cachefile)

End If

This will delete cached files but remain cookies.

Share:
14,444
Teekin
Author by

Teekin

Updated on June 05, 2022

Comments

  • Teekin
    Teekin almost 2 years

    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.

    I need to maintain cookies, but to throw away the cache.

    Of particular interest is this line:

    const int CACHEGROUP_SEARCH_ALL = 0x0;

    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.

    This code is originally taken from an MSDN article, but it doesn't even mention cookies or cache groups.

    You can see the MSDN article at the top of the 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
        }
    }
    
    

    Any and all help greatly appreciated.

  • Teekin
    Teekin over 14 years
    I ended up doing it by command line due to time restraints, but your post got me on the track nonetheless. Thanks!
  • imgen
    imgen almost 10 years
    And I really don't know why this only correct answer doesn't get much upvotes. Anyway, that gives me the edge of being the first.