Beschreibung
Wie im Titel angegeben, führt WinInet FindNextUrlCacheEntry()
zu einem undokumentierten Fehlercode von 2 (ERROR_FILE_NOT_FOUND) anstelle von ERROR_NO_MORE_ITEMS=295. Kann jemand erklären, warum oder andere Untersuchungsmöglichkeiten vorschlagen?
Umfeld
- Windows 10
- IE 11
- VisualStudio 2006 (kann aber auf VS2010 neu erstellt werden)
Zusammenfassung
Einzelheiten dazu weiter unten, aber der Kern des Problems liegt darin, dass innerhalb einer Schleife dieses Codefragment:
/ Get the next entry
if (!FindNextUrlCacheEntry(hCache, *plpCacheEntry, &bufSize))
{
DWORD d = GetLastError();
ATLTRACE(_T("getNextEntry - FindNExtUrlCacheEntry returned %d: %s ==> %s"), d, (*plpCacheEntry)->lpszSourceUrlName, (*plpCacheEntry)->lpszLocalFileName);
// Are we done?
// <.... more error handling logic ....>
führt zu dieser Trace-Ausgabe:
>>[12536] getNextEntry - FindNExtUrlCacheEntry returned 2: (null) ==> (null)
Wenn wir unseren Code so anpassen, dass dieser Zustand ähnlich wie ERROR_NO_MORE_ITEMS behandelt wird, funktioniert alles wie erwartet. Wenn wir dies ignorieren und weiterhin versuchen, weitere Einträge zu erhalten, erhalten wir eine Endlosschleife.
Überblick
Hinweis: Für diesen Beitrag behandle ich den Fehlercode 2 als Synonym zu ERROR_FILE_NOT_FOUND, aber da es sich um einen nicht dokumentierten Code für den betreffenden API-Aufruf handelt, kann die 2 tatsächlich von etwas anderem stammen.
Wir haben ein C++ ActiveX-Steuerelement aus VisualStudio 2006, das im Internet Explorer ausgeführt wird. Sein Zweck besteht darin, eine teilweise Cachebereinigung des Internet Explorers für Dateien durchzuführen, die mit dem Hostnamen unseres Anwendungsservers übereinstimmen. (Diese Aktion wird durch den Vergleich der Versionsnummern zwischen einem Anforderungs-Cookie-Wert vom Server und dem zuletzt auf dem Client gespeicherten Wert gesteuert. Die Löschung erfolgt nur, wenn eine Versionsinkongruenz erkannt wird.)
Das ActiveX-Steuerelement hat viele Jahre lang einwandfrei funktioniert. Aber seit Kurzem treten, wie oben erwähnt, Fehler auf. Wir wissen nicht, ob es an einem KB-Windows-Update, aktuellen Sicherheitsinitiativen oder etwas anderem liegt. Unser Code behandelt den Fall ERROR_NO_MORE_ITEMS ordnungsgemäß und beendet seine Arbeit. Aber er behandelt den Fall 2 als Fehler und die App wird nicht fortgesetzt.
Die Dokumentation für FindNextUrlCacheEntry() finden Sie hier: https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-findnexturlcacheentrya Der Aufruf gibt „true“/„false“ zurück und setzt den Fehlercode entweder auf „ERROR_NO_MORE_ITEMS“ oder „ERROR_INSUFFICIENT_BUFFER“.
Unsere Logik:
Ich kann Einzelheiten liefern, zögere aber, proprietären Code zu veröffentlichen. Kurz gesagt folgt die Logik den empfohlenen Schritten hierfür. Ein Beispiel finden Sie hierhttps://support.microsoft.com/en-us/help/815718/how-to-clear-the-cache-when-your-application-hosts-a-webbrowser-controaußer dass wir uns nicht mit "Cache-Gruppen" befassen. Sie können den Code scrollen, der nach dem Kommentar beginnt
// Start to delete URLs that do not belong to any group.
Unsere Logik ist jedenfalls wie folgt:
- Holen Sie sich einen Handle für den Cache über
FindFirstUrlCacheEntry()
, verteilen Sie den Speicher nach Bedarf für den Eintragspuffer neu und überprüfen Sie,GetNextError()
ob er erfolgreich war. - Durchlaufen Sie diesen und die nachfolgenden Einträge über
FindNextUrlCacheEntry()
, wobei Sie den Speicher nach Bedarf für den Eintragspuffer neu zuweisen und auf den Rückgabewert „true“/„false“ prüfen. - Wenn wahr: Rufen Sie auf,
DeleteUrlCacheEntry()
wenn die Hostnamen übereinstimmen und der Eintrag nicht vom Typ ist:COOKIE_CACHE_ENTRY
oderHISTORY_CACHE_ENTRY
. Andernfalls überspringen Sie den Eintrag - Wenn falsch: Wir prüfen den Wert
GetNextError()
und führen Folgendes aus: ERROR_NO_MORE_ITEMS
(=259): Beenden Sie die Schleife und verlassen Sie den ActiveX-Einstiegspunkt erfolgreich.- Setzen Sie den Rückgabecode des ActiveX-Steuerelement-Einstiegspunkts auf den Fehlercode (in unserem Fall 2). Der JavaScript-Aufrufer testet auf 0 und stoppt alert()'s den Benutzer des Fehlers mit Anweisungen zum Aufrufen des Supports
Problemumgehung:
Die einzige Möglichkeit, die Anwendung funktionsfähig zu halten, besteht darin, den Benutzer den gesamten IE-Cache löschen zu lassen. An diesem Punkt findet FindFirstUrlCacheEntry() nichts und die Löschschleife wird nie betreten. (Eigentlich ist dies Spekulation, da wir diesen Fall nicht explizit testen können. Siehe Debugging-Hinweise unten.) In jedem Fall tritt der Fehler nicht auf
Debugging-Anstrengungen:
Wir können die Nichtübereinstimmung der Cookie-Version erzwingen, indem wir im JavaScript-Code einen Haltepunkt setzen und die Versionszeichenfolge manuell ändern, bevor der Vergleich durchgeführt wird. Dadurch wird der Code dazu gebracht, den Einstiegspunkt zum Löschen des ActiveX-Steuerelements aufzurufen.
Hier sind wir extrem eingeschränkt. Wir haben nur gelegentlich Zugriff auf einen einzigen PC, auf dem das Problem reproduziert wird. Versuche, einen VisualStudio-Debugger über msvsmon.exe remote anzuschließen, ermöglichen es uns, die ATLTRACE-Ausgabe (auch bekannt als printf) anzuzeigen, aber wir können keine Haltepunkte auslösen. Stattdessen sind wir auf ATLTRACE() und Sysinternals' DebugView beschränkt.
Was wir sehen, ist, dass sich ERROR_FILE_NOT_FOUND wie ERROR_NO_MORE_FILES verhält. Das heißt, wenn wir den C++-Code hacken, um die Find-Next/Delete-Schleife unter beiden Bedingungen zu beenden, löscht er immer einige erwartete Dateien und wird ohne Fehler abgeschlossen. Es ist jedoch schwierig zu wissen, ob es wirklich keine weiteren Dateien gibt oder nicht: Das manuelle Abfragen des IE-Cache ist bestenfalls schwierig, und die Sicherheitsmaßnahmen auf unseren PCs machen es noch schwieriger. Beispielsweise werden Unterordner C:\Users\<usr>\AppData\Local\Microsoft\Windows\INetCache\Low\IE\...
nicht in einem cmd- oder Power-Shell-Dir- oder ls-Befehl angezeigt. Dennoch scheint es, dass je mehr Seiten unserer App wir im Client aufrufen, desto mehr Dateien vom ActiveX-Steuerelement gelöscht werden, bevor es beendet wird. Ein funktionierender Fall davon sieht in DebugView folgendermaßen aus:
[12536] DeleteCacheEntries(10.3.31.28:9080) BEGIN: hr=0 pErrCode=0
[12536] deleteCacheEntriesFor(10.3.31.28:9080) BEGIN
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - getFirstEntry returned 0
[12536] deleteCacheEntryIf - (lpdErrCd now=0) deleting: http://10.3.31.28:9080/images/our.jpg ==> C:\Users\<user>\AppData\Local\Microsoft\Windows\INetCache\Low\IE\Q6HU0E35\our[1].jpg
....<lots more "deleting" ...>
[12536] getNextEntry - FindNExtUrlCacheEntry returned 2: (null) ==> (null)
[12536] getNextEntry - ERROR=2: (null) ==> (null)
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - getNextEntry returned 2. Breaking out of loop!
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - Complete. Closing URL cache handle current. errCd=0
[12536] deleteCacheEntriesFor(10.3.31.28:9080) - Done. Returning 0
Wir haben auch einen Hack ausprobiert, der davon ausgeht, dass ERROR_FILE_NOT_FOUND einfach bedeutet, dass dort ein Cache-Eintrag vorhanden ist, wir ihn aber irgendwie nicht sehen können. Das heißt, anstatt aus der Schleife auszubrechen, machen wir weiter in der Hoffnung, auf andere „auffindbare“ Einträge zu stoßen, die wir möglicherweise löschen können. Aber in diesem Fall wird die Schleife nie beendet. Hier bedeutet „nie“ mehrere Minuten und über 2 Millionen Fälle von:
[5712] getNextEntry - FindNExtUrlCacheEntry returned 2: (null) ==> (null)
[5712] deleteCacheEntriesFor(10.3.31.28:9080) - getNextEntry returned 2. CONTINUING!
...<forever>...
Spekulation:
Hier sind einige zugegebenermaßen schwache Spekulationen darüber, was vor sich gehen könnte
- Einige Sicherheitssoftware oder Windows KB-Patches greifen in die C++-Ausführung ein und führen dazu, dass SetLastError(2) aufgerufen wird, wodurch die dokumentierten Werte, die von FindNextUrlCacheEntry() gesetzt werden, zerstört werden.
- Unser Code ist mit älteren Versionen des VisualStudio SDK verknüpft und es kommt zu Code-Verzerrungen
- Wir dachten an eine Beschädigung des Stapels oder des Registers, aber dafür scheint das Verhalten zu reproduzierbar zu sein. Trotzdem...?
- Es gibt tatsächlich etwas für FindNextUrlCacheEntry(), aber die Sicherheit verhindert, dass es irgendwie sichtbar ist und sein „Cursor“ im Cache bewegt sich nie weiter
Antwort1
Dies scheint auf einen Defekt in der Microsoft WinINet API zurückzuführen zu sein. Ausführliche Informationen finden Sie im entsprechenden Thread im Devcommunity/VisualStudio-Forum.Hier. (Die Antwort finden Sie in meinen letzten Beiträgen unten.)