Neuigkeiten:

Ist euer Problem gelöst, dann bitte den Knopf "Thema gelöst" drücken!

Mobiles Hauptmenü

Shell bricht nach 111 Vorgängen ab

Begonnen von crystal, Oktober 12, 2016, 18:16:42

⏪ vorheriges - nächstes ⏩

crystal

Hallo,
folgende Situation: in einem Verzeichnisbaum habe ich Filme gespeichert. Den Baum habe ich in eine Access-Tabelle importiert.

Nun möchte ich mittels Shell einige erweiterte Attribute der Dateien auslesen:


Private Sub Form_Current()
    Dim objShell  As Object
    Dim objFolder As Object
    Dim objFolderItem As Variant
   
    Dim sfolder As Variant
   
    sfolder = TopDir & "\" & SDir & "\" & FDir & "\"
   
    Set objShell = CreateObject("Shell.Application")
    Set objFolder = objShell.NameSpace(sfolder)
    'Schleife über alle Dateien
    For Each objFolderItem In objFolder.Items
        If objFolderItem.Name = MovieFileName Then
            MovieFileType = Right(objFolder.GetDetailsOf(objFolderItem, 158), 3)
            MovieFileSize = objFolder.GetDetailsOf(objFolderItem, 1)
            MovieFileLength = objFolder.GetDetailsOf(objFolderItem, 27)
            MovieFileResX = objFolder.GetDetailsOf(objFolderItem, 306)
            MovieFileResY = objFolder.GetDetailsOf(objFolderItem, 308)
           
            Exit For
        End If
    Next
   
    Set objFolderItem = Nothing
    Set objFolder = Nothing
    Set objShell = Nothing
   
    DoCmd.GoToRecord , , acNext
   
End Sub


Dieser Code bricht nach jeweils exakt 111 Records ab (Sie können nicht zum angegebenen Datensatz springen).

Wenn ich die Fehlermeldung beende und 'Bild nach oben' tippe, läuft's weiter - bis wieder nach 111 Records die Fehlermeldung kommt.

Was kann ich noch tun? Alle Shell-Objekte werden doch am Ende auf Nothing gesetzt. Was bleibt da im Speicher zurück?



Wer Fehler in meinen Antworten findet, darf sie behalten, muss sie aber kommentieren. ;-)
Dies ist keineswegs arrogant gemeint, sondern soll nur unterstreichen, dass meine Antworten - natürlich - nicht immer fehlerfrei sind und sein können.
Devise: bitte immer erst selbst probieren!

Aus gesundheitlichen Gründen nur noch selten dabei...

ebs17

Es wäre zu wünschen, dass Du mal ebensoviel runden um Dein Haus laufen musst wie Du hier Schleifenrunden veranlasst. Dann würdest Du einiges verstehen.

Effizient wäre es,
- nur ein Shellobjekt für alles zu verwenden,
- das Verzeichnis genau einmal zu durchlaufen und dabei Dateinamen und sofort Dateiattribute zu erfassen. Deine Vorerfassung der Dateinamen ist da nicht viel wert.
Mit freundlichem Glück Auf!

Eberhard

daolix

Ein Form_Current und ein "DoCmd.GoToRecord , , acNext" beissen sich am ende irgendwie.

crystal

Hallo!

@Eberhard
Deine Teil-Antwort

ZitatEs wäre zu wünschen, dass Du mal ebensoviel runden um Dein Haus laufen musst wie Du hier Schleifenrunden veranlasst. Dann würdest Du einiges verstehen.

war ja wohl etwas daneben gegriffen, denn Ironie und Zynismus bringt uns substanziell nicht weiter.

Bei jedem Aufruf meiner Sub wird das Verzeichnis gesetzt, in dem die Dateien gespeichert sind. Es wird dann 'nur' über diese Dateien geloopt (in 98% der Fälle genau eine, maximal 8 oder 10).

Besser wird's schon im zweiten Teil deiner Antwort. Auch wenn der Code eher schlecht und nicht optimal ist, bleibt doch die Frage, wieso das genau beim 111. Aufruf daneben geht.
Und es handelt sich übrigens um ca. 10.000 Verzeichnisse mit je einer bis ein paar wenigen Datei(en). Dummerweise liegt diese Liste bereits vor (mit verknüpften Daten in anderen Tabellen), so dass ich gezwungen bin, die zusätzlichen Attribute nachträglich hinzuzufügen.

Immerhin gebe ich dir prinzipiell Recht: es wäre besser, die Daten inkl. Namen in einem Rutsch zu erfassen. Aber was soll man machen, wenn die "Vorerfassung" bereits stattgefunden hat (und die Dateien auch noch auf Wechsel-Datenträgern liegen)?


@daolix
Wo liegt das Problem? Es ist eine einfache Technik, um durch alle Datensätze einer Tabelle oder Abfrage zu loopen und dabei im Formular beobachten zu können, was passiert - ohne immer auf "nächster Datensatz" klicken zu müssen. Und da Access bei Datensatz-Wechsel erstmal die Daten des bisherigen Datensatzes abspeichert, ist doch alles OK.
Ein Abbruch-Kriterium (z.B. ein Stop-Button) ist sicher hilfreich, hab ich aber hier nicht eingebaut, weil ich das über die Abfrage gesteuert habe (Select top n ...).


Es bleibt also die Frage, weshalb Access beim 111. Zugriff auf das Shell-Object abbricht, obwohl es jeweils gelöscht wird. Welche Daten "behält" die Shell (oder etwa Access) und wie kann ich sie selbst löschen? Wieso läuft es nach Beenden der Fehlermeldung weiter? Hat MS einfach nur vergessen, die Routine richtig zu initialisieren bzw. den Kontext zu löschen? Oder steht in einer versteckten Kurzdoku, dass man selbst löschen muss? Da ja vom Shell-Object gesprochen wird, ist vielleicht einfach nur der Destruktor fehlerhaft.


Wer Fehler in meinen Antworten findet, darf sie behalten, muss sie aber kommentieren. ;-)
Dies ist keineswegs arrogant gemeint, sondern soll nur unterstreichen, dass meine Antworten - natürlich - nicht immer fehlerfrei sind und sein können.
Devise: bitte immer erst selbst probieren!

Aus gesundheitlichen Gründen nur noch selten dabei...

daolix

ZitatEs bleibt also die Frage, weshalb Access beim 111. Zugriff auf das Shell-Object abbricht
ggf weil, je nach Recordsettyp, dein Formular 110 o. 111 Datensätze beinhaltet.

Auzug aus der Hilfe:
Das Current-Ereignis tritt ein, wenn der Fokus auf einen Datensatz gesetzt wird, der somit zum aktuellen Datensatz wird. Es tritt außerdem ein, wenn das Formular aktualisiert bzw. erneut abgefragt wird.

ein "DoCmd.GoToRecord , , acNext" geht zum nächsten Datensatz, setzt den Focus und löst das Current-Ereignis aus.

Was passiert wenn du am letzen Datensatz bist und ein DoCmd.GoToRecord , , acNext auslöst?





markusxy

Zitat von: crystal am Oktober 12, 2016, 18:16:42

Dieser Code bricht nach jeweils exakt 111 Records ab (Sie können nicht zum angegebenen Datensatz springen).

Wenn ich die Fehlermeldung beende

Bei einem unbehandelten Fehler, kann man ja auch debug auswählen.
Dann sieht man immerhin die Problemstelle im Code und kann im Lokal-Bereich - sofern man den with Operator meidet - den Zustand der Klassen und Variablen ansehen.
Das sollte in der Regel genügen um einen Fehler zu finden und keine falschen Schlussfolgerungen zu treffen, die ja das Hauptproblem bei der Fehlersuche sind.

LG Markus


ebs17

@crystal: Der erste Satz von mir sollte primär zum Nachdenken anregen. Wenn man etwas körperlich spürt, wird es oft sehr besser begreifbar. Vielleicht reicht es ja schon, etwas virtuell zu spüren. Zynismus kann ich da nicht erkennen, aber jeder ist frei in seinen Gedanken.

Bezüglich Shellobjekt: Wenn ich 10.000-mal im Eiltempo durch eine Tür renne, mache ich diese nicht nach jeder Durchquerung zu, um sie beim nächsten Mal wieder zu öffnen, sondern ich lasse sie offen. Also würde ich mir eine einmalige Instanz setzen analog der Lösung CurrentDbC von Michael Kaplan, und diese dann für alle Verzeichniszugriffe nutzen.

ZitatAber was soll man machen, wenn die "Vorerfassung" bereits stattgefunden hat (und die Dateien auch noch auf Wechsel-Datenträgern liegen)?
Auch da kann man einen einfachen einmaligen Durchlauf durch alle relevanten Verzeichnisse machen und dabei alles zu den Dateien erfassen sowie das Ergebnis in eine Tabelle schreiben. Diese Tabelle könnte man dann mit der Erfassungstabelle abgleichen und hätte da auch gleich Informationen zu Änderungen auf dem Datenträger.

Zu Deiner gewählten Variante: Ich würde nicht durch das Formular loopen, sondern durch das Recordset (des Formulars oder der zugrundeliegenden Tabelle). Da würde man rein Daten bewegen und nicht eine Bedienoberfläche, und mit dem üblichen Durchlauf bis EOF hätte man auch kein Next-Problem.

Man kann also auch sagen: Wenn man bestimmte Dinge gar nicht erst macht, weil es bessere Wege gibt, muss man sich auch nicht mit daraus erzeugten Fehlern auseinandersetzen.
Mit freundlichem Glück Auf!

Eberhard

crystal

Hallo,

Also ich gebe ja ehrlich zu, dass mein Ansatz etwas fragwürdig ist. Aber es bleibt die Tatsache, dass ich

1. eine Sub aufrufe
2. ein paar Variable dimensioniere und initialisiere
3. dann "irgendwas" (die shell) aufrufe und einige Daten hole
4. dann die Variablen sogar explizit zerstöre und
5. die Sub verlasse

Formal und logisch also doch alles OK - oder?
Würde ich bei Punkt 3 "irgendwas" anderes machen, würde Access ja vermutlich NICHT nach 111 Datensätzen stoppen, oder?

Es bleibt die Frage, warum das nur 111mal funktioniert (übrigens auch ohne gotorecord und mit manuellem Klicken auf nächster DS).

Es bleibt mein Verdacht, dass Microsoft hier einfach nicht sorgfältig genug programmiert hat, zumal auch manchmal ein Fehler "keine freien Ressourcen" auftritt.

@Eberhard
OK - Zynismus nehme ich zurück.
Es sollte m.E. aber nicht vorkommen, dass eine Tür "von sich aus" offenbleibt, obwohl ich sie explizit schließe.
Natürlich hast du Recht: wenn ich nicht programmiere, brauche ich keine Fragen stellen.
Immerhin gibst du einen Hinweis auf "CurrentDbC von Michael Kaplan", ohne jedoch einen Link beizufügen.
Ansonsten sind deine Bilder von Häusern, um die man läuft und Türen, die man 10000mal durchquert, ohne sinnvolle Aussage.

Lösen wir uns einmal vom Loop-Thema. Angenommen, ich habe eine Datenbank, in der viele Movies gespeichert sind und ich will "ab und zu" die Datei-Attribute herauslesen, weil sie vielleicht nicht stimmen oder einfach nur fehlen. Ich mache das, indem ich schlicht in meiner Movie-Tabelle navigiere und so manchmal falsche oder nicht komplette Daten entdecke.
Soweit doch alles legitim und anwendungs-entsprechend.
Bricht Acces jetzt auch bei jedem 111. Versuch ab? Und was kann ich programmtechnisch machen, um diesen Abbruch zu verhindern? Quasi:
if counter =110 then
   <neu initialisieren>
endif
Aber WAS soll ich neu initialisieren? Ich lösche doch jeweils die Instanz des Shell-Objects.


Vielleicht kann das jemand einfach mal testen und dabei feststellen, dass Access bzw. das Shell-Object Speicher nicht wieder freigibt oder was auch immer. Die bisherigen Antworten haben zur Lösung des eigentlichen Problems jedenfalls nichts beigetragen.

Wer Fehler in meinen Antworten findet, darf sie behalten, muss sie aber kommentieren. ;-)
Dies ist keineswegs arrogant gemeint, sondern soll nur unterstreichen, dass meine Antworten - natürlich - nicht immer fehlerfrei sind und sein können.
Devise: bitte immer erst selbst probieren!

Aus gesundheitlichen Gründen nur noch selten dabei...

markusxy

Zitat von: crystal am Oktober 14, 2016, 13:53:52
Die bisherigen Antworten haben zur Lösung des eigentlichen Problems jedenfalls nichts beigetragen.
Hast du meinen Vorschlag denn umgesetzt?

Gruss Markus

MaggieMay

Hi,

wie viele Datensätze enthält denn die Tabelle eigentlich?
Auf diese Frage bist du noch gar nicht eingegangen.
Irgendwann ist nämlich Schluss mit "GoToNext" und da wäre die genannte Fehlermeldung genau die passende gewesen.

Außerdem - neben allen bereits geäußerten Bedenken - halte ich das Current-Ereignis für äußerst fragwürdig für diese Aktion. Irgendwo schriebst du mal, dass du fehlende Daten ergänzen willst, dies tust du aber nun völlig bedingslos, also auch dort wo die Daten längst vorhanden sind.
Freundliche Grüße
MaggieMay

ebs17

#10
Vorschläge, die von crystal's durchdachten Lösungsweg auch nur geringfügig abweichen, sind offensichtlich unerwünscht und ihm unverständlich.

ZitatEs bleibt mein Verdacht, dass Microsoft hier einfach nicht sorgfältig genug programmiert hat, zumal auch manchmal ein Fehler "keine freien Ressourcen" auftritt.
Sub test_shell()
On Error GoTo ErrHandler
    Dim oSh As Object
    Dim i As Long
   
    For i = 1 To 9999
        Set oSh = CreateObject("Shell.Application")
        Set oSh = Nothing
    Next
    MsgBox "fertig"
Exit_shell:
    Exit Sub
ErrHandler:
    MsgBox i & " Durchläufe" & vbCrLf & Err.Description
    If Not oSh Is Nothing Then Set oSh = Nothing
    Resume Exit_shell
End Sub


Das läuft bei mir anstandslos durch. 9999 > 111. Also geht Deine geliebte Vermutung an den Tatsachen vorbei.

Mit freundlichem Glück Auf!

Eberhard

daolix

Zitat von: crystal am Oktober 14, 2016, 13:53:52
Würde ich bei Punkt 3 "irgendwas" anderes machen, würde Access ja vermutlich NICHT nach 111 Datensätzen stoppen, oder?
Probier es doch einfach aus, vorrausgesetzt die Recordsetmenge deines Formulars ist gross genug. In der Sub Form_Current nur eine Zeile ( "DoCmd.GoToRecord , , acNext) und schau was passiert.

MzKlMu

#12
Hallo,
ich halte hier Form_Current für völlig ungeeignet. Auch bei meinem Versuch mit dem Vorschlag von daolix wird nach ca. 300 DS mit der gleichen Fehlermeldung abgebrochen.

Hier sollte man wie bereits vorgeschlagen ein Recordset öffnen und die Datensätze im Recordset durchlaufen.
Nur mal spaßeshalber zum Testen:
Private Sub Befehl3_Click()
Dim rs As DAO.Recordset
Dim Z As Long, i As Long
Set rs = CurrentDb.OpenRecordset("TabellenName")
rs.MoveLast
Z = rs.RecordCount
rs.MoveFirst
For i = 1 To Z
        rs.Edit
        rs!Datum = Date
        rs.Update
        rs.MoveNext
Next i
Me.Requery
End Sub

Braucht auf einen Rutsch für 525.000 DS 18 Sekunden ohne Unterbrechung.
Ist jetzt nicht mit dem Code zum Ändern der Dateiattribute vergleichbar
Mit dem Code:
Private Sub Form_Current()
ZZ = ZZ + 1
    Me.Datum = Date
    DoCmd.GoToRecord , , acNext
End Sub
wir mit ZZ = 352 mit der Fehlermeldung abgebrochen.
Gruß Klaus

ebs17

Zitat von: crystal...  und dabei im Formular beobachten zu können, was passiert

Es kann sich nachteilig auswirken, wenn man Flipperfeeling anstrebt statt Datenverarbeitung mit nachträglicher Auswertung (SQL) zu betreiben, gerade wenn man mit erwartbar größeren Datenmengen arbeitet.
Mit freundlichem Glück Auf!

Eberhard

crystal

Hallo,
sorry, ich hatte leider keine Zeit, Tests zu machen und im Debugger tiefer zu suchen. Ich werde das aber nachholen.

Nochmal zu Klärung: es handelt sich um eine pragmatische, ja sogar "quick-and-dirty"-Lösung, die ich nur einmal auf vorhandene Datenbestände anwenden wollte, um die Attribute zu ergänzen.

Ein paar Anmerkungen zu Antworten.

@ebs
Vielleicht liebe ich Flipper und flackernde Bildschirme. (Unnütze Aussage, wie deine letzte Meldung.)

Dein Test hat zumindest einen Haken: es wird ein shell-Object nur angelegt und sofort wieder gelöscht. Dieser Test ist also nicht ausreichend durchdacht und abstrahiert das Problem erheblich zu stark. Das musst du ja wohl zugeben.

Ich bin alles andere als unfähig oder unwillig, Fehler zu korrigieren und andere Lösungen zu probieren. Du hast bisher aber nur gesagt, dass das Konzept falsch ist, ohne allerdings die Tatsache zu berücksichtigen, dass die Datensätze in der DB bereits existieren und zwar in einem komplexen Datenmodell und komplexen Anwendung. Ein einziger konstruktiver Lösungsvorschlag von dir war es, einen 'Lauf' über die Dateien zu starten und die Daten abschließend zusammen zu führen. Das ist ein guter Vorschlag und so werde ich es wohl auch machen.

Ansonsten hast du nur nebulöse Anregungen zum Nachdenken und Mutmaßungen über meine Lernfähigkeit gemacht, was ich etwas schade finde.

Es bleibt allerdings ein Problem: angenommen, ich hätte meine Daten komplett konsolidiert. Irgendwann später treffe ich auf einen Datensatz, bei dem die Attribute offensichtlich falsch sind oder fehlen oder aus einem anderen Grund aktualisiert werden müssen. Wie mache ich das dann ad-hoc? Vermutlich mit einer Sub, die so oder ähnlich gebaut ist, wie meine - und da beisst sich die Maus in den Schwanz.

Hätte ich gefragt, wie man das macht, hätte ich als Antwort eine Sub erhalten, die meiner Konstruktion nahe kommt. Wäre ja auch völlig logisch und ich habe diese Sub ja auch wirklich nach Hilfestellung aus diesem Forum gebaut.

Jetzt habe ich nur den Fehler gemacht, diese Sub in einem Loop aufzurufen. Tritt dieser Fehler dann auch in der ad-hoc-Variante auf, etwa über einen Button "Daten aktualisieren" gestartet? Muss ich dann den Anwender bitten, die Applikation neu zu starten, weil vielleicht irgendwelche Ressourcen nach Beendigung der Shell nicht freigegeben werden?

@MaggieMay
Die Tabelle enthält über 20.000 DS. Aber ich loope ja nicht über die ganze Tabelle, sondern selektiv über Teilmengen. Dabei lasse ich natürlich auch DS aus, bei denen die Werte schon existieren. Mein Formular ist also an eine schlichte Abfrage gebunden und nicht an die Tabelle selbst.


Es scheint sich also heraus zu kristallisieren, dass es deshalb "knallt", weil ich über ein Recordset "loope". Vielleicht ist die Konstruktion Current-goto-next die Fehlerursache. Aber wieso? Wenn ich in einem Formular zum nächsten DS wechseln möchte muss ich doch goto-next benutzen, oder? Liegt es an irgendeinem internen Puffer oder am DB-Cursor? Irgendwo habe ich gelesen, dass Access (je nach Recordset-Typ und Cursor) nicht den gesamten Recordset lädt, sondern nur häppchenweise.
Wer Fehler in meinen Antworten findet, darf sie behalten, muss sie aber kommentieren. ;-)
Dies ist keineswegs arrogant gemeint, sondern soll nur unterstreichen, dass meine Antworten - natürlich - nicht immer fehlerfrei sind und sein können.
Devise: bitte immer erst selbst probieren!

Aus gesundheitlichen Gründen nur noch selten dabei...