5

I'm using P/Invoke with C# to clear cache entries as follows. The code seems to work fine up to Windows 7 on 32 and 64 bit. On the Windows 8 Release Candidate, it hangs at the DeleteUrlsFromGroup call.

[DllImport(@"wininet",
    SetLastError = true,
    CharSet = CharSet.Auto,
    EntryPoint = "FindFirstUrlCacheGroup",
    CallingConvention = CallingConvention.StdCall)]
private 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)]
private 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)]
private 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)]
private 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)]
private 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)]
static extern bool DeleteUrlCacheEntry(string lpszUrlName);

/// <summary>
/// Clears the cache of the web browser
/// </summary>
[HandleProcessCorruptedStateExceptions]
public static void ClearCache(Uri hostName)
{
    if (hostName == null)
    {
        return;
    }

    long groupId = 0;
    try
    {
        // Delete the groups first.
        IntPtr enumHandle = FindFirstUrlCacheGroup(0, CACHEGROUP_SEARCH_ALL, IntPtr.Zero, 0, ref groupId, IntPtr.Zero);
        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.
            // Hangs on WIndows 8
            bool 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;
        }
        DeleteUrlsFromGroup(hostName); // this hangs on Windows 8
    }
    catch (AccessViolationException)
    {

    }
}

Any insights/references to changes in the Win API for Windows 8?

Thanks in advance.

jimbojones
  • 682
  • 7
  • 19

4 Answers4

2

also had the same trouble but for c++ (from this KB http://support.microsoft.com/kb/815718). I ended up just checking if the DeleteUrlCacheGroup() keeps on trying to delete the same groupId value.

I keep track of the previous groupId, if it matches the current one, I just break the loop.

mickeymicks
  • 637
  • 8
  • 13
2

@jimbojones : I am not sure if you are still looking for an answer, but I just came across the same problem and found a workaround. The problem is not with "DeleteUrlCacheGroup", its in your while loop

while (true)
        {
            if (ERROR_NO_MORE_ITEMS == Marshal.GetLastWin32Error() || ERROR_FILE_NOT_FOUND == Marshal.GetLastWin32Error())
            {
                break;
            }

            // Delete a particular Cache Group.
            // Hangs on WIndows 8
            bool 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;
        }

If your code is being called with groupdid '0' it will never return false and hence causing your loop to be infinite which is actually causing your app to hang. I was not getting this issue on Windows 8 but on Windows 7 itself. Scenario might be different but I think you are facing the same problem.

1

I don't see a pinvoke declaration for DeleteUrlsFromGroup, just DeleteUrlCacheEntry. I assume that is the method that is hanging?

If that is the case, it seems odd you are explicitly calling the ANSI version of the function:

EntryPoint = "DeleteUrlCacheEntryA"

But are also specifying:

CharSet = CharSet.Auto

That would indicate to me you are passing a Unicode string to a function that expects an ANSI string. From MSDN:

Automatically marshal strings appropriately for the target operating system. The default is Unicode on Windows NT, Windows 2000, Windows XP, and the Windows Server 2003 family; the default is Ansi on Windows 98 and Windows Me.

Either don't specify a function with the *A or *W extension as the pinvoke layer will map that for you, or call the *W versions of the functions since you are passing Unicode strings with CharSet.Auto. If you are going to explicitly call the ANSI/Unicode version of a function, I would be explicit with the matching CharSet as well.

joncham
  • 1,584
  • 10
  • 14
  • He does have `DeleteUrlCacheGroup`. `DeleteUrlsFromGroup` in the question appears to be a typo, as there is no such API. – Ben Voigt Oct 30 '12 at 15:20
  • I assumed it was DeleteUrlCacheEntry as that method at least matches the signature of the non-existent method he is calling, a single string parameter. – joncham Oct 30 '12 at 15:42
  • Thanks for the update. I will test this on our test Windows8 box and mark as accepted if it works. The reason there is the `CharSet.Auto` and the `*A` version of the import is because it was taken directly from http://support.microsoft.com/kb/326201 . I need to review Microsoft's code more carefully. – jimbojones Nov 01 '12 at 19:55
  • @jimbojones did you ever solve this? Having the same error with the same damn kb article – Robert Christ Feb 26 '14 at 22:07
1

That kb article everyone links to has numerous errors in it (where the other answer's 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

Robert Christ
  • 1,910
  • 2
  • 13
  • 19