AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi Access-Datenbank gesperrt - Problem mit Windows-Update?
Thema durchsuchen
Ansicht
Themen-Optionen

Access-Datenbank gesperrt - Problem mit Windows-Update?

Ein Thema von berens · begonnen am 6. Okt 2020 · letzter Beitrag vom 8. Okt 2020
Antwort Antwort
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#1

Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 6. Okt 2020, 10:47
Datenbank: MS Access • Version: 2003 • Zugriff über: TAdoConnection
Hallo zusammen!

Ich verwende in meinem Programm TAdoConnection um auf eine Access-Datenbank zuzugreifen (.mdb-Datei).
Zitat:
Bevor ihr mich auffresst: Die Kunden können mittlerweile meine Software auch mit einem SQL-Server benutzen ( https://www.delphipraxis.net/204437-...g-welches.html ), allerdings lässt sich das nicht bei allen Kunden unmittelbar umstellen oder umsetzen, da es dort teilweise nicht gewünscht oder schlicht und einfach überdimensioniert wäre. Dementsprechend muss ich mich jetzt um die Bestandskunden(probleme) kümmern.
Ich rede jetzt im Nachfolgenden von Kunden, die seit einem Jahr kein Update meiner Software eingespielt haben, da alles einwandfrei läuft.
Seit Mai 2020 kommt es regelmäßig zu dem Problem, dass die Datenbankdatei beim Schreiben auch nur kleinster Änderungen korrumpiert wird (nachgewiesenes Windows-Update Problem). Nachdem das Verhalten mit den Windows-Updates vom August scheinbar größtenteils behoben wurde, erhalte ich von immer mehr Kunden (aktuell: 3) die Meldung über weitere Datenbankprobleme. Jedoch nicht, dass die Datenbank defekt ist, sondern dass die Software sich merkwürdig verhält. Ein Blick in die Logs offenbart folgendes:

TAdoQuery feuert beim Lesen von Werten oder beim Öffnen der Abfrage ("Select Top 1 * from") folgende Exceptions:
Code:
LoadFromDatabase.Exception: Zu viele aktive Benutzer
Code:
GetDB_Int.Exception: Die Datenbank wurde von Benutzer '' auf Computer '' in einen Status versetzt, in dem sie nicht geöffnet oder gesperrt werden kann
Anmerkung: Hier habe ich nicht aus Datenschutzgründen den Inhalt für "Benutzer" und "Computer" entfernt, die Werte sind tatsächlich leer!

Nun, die Fehlermeldungen an sich sind ja verständlich. Warum sie erscheinen, entbehrt jedoch jeglicher Grundlage.

1) Die Installation wurde seit einem Jahr nicht verändert. Die Benutzer arbeiten wegen den vorherigen Störungen aktuell "eher weniger" mit der Software, als mehr. Mit "zu viele Benutzer" meint das Programm ca... 3 Stück insgesamt? Die Zahl ist nicht höher als früher auch. Warum kommt es jetzt zu dem Problem?

2) "In einen Status versetzt" - ich nehme an, das soll heißen, sie ist Exklusiv geöffnet. Mein Programm macht das an keiner Stelle (absichtlich). Ich würde hier fast vermuten, dass z.B. Virenscanner oder Backupprogramm die Datei nach einem Schreibvorgang vor (weiteren) Änderungen schützen, bis sie komplett gesichert / gescannt wurde. Google-Suchen offenbaren viele Threads (größtenteils von vor vielen Jahren) zu dem Thema, meist auch mit den Aussagen, dass das Problem "irgendwann" auftaucht und nicht mehr weggeht. Dann wurden z.B. Backups wiederherstellt, die selben Daten eingetragen, dann trat das Problem nicht mehr auf. Das würde der Windows-Update- oder Virescanner-Theorie wiedersprechen. Die Datenbank bei den Kunden wurde schon in Access "komprimiert und repariert", ein veraltetes Backup von vor > 2 Wochen würde ich ungern einspielen müssen "nur um zu testen"...

Ist das was zu dem Thema bekannt?


Generell bin ich nun sehr verunsichert, was das Thema "Exklusiver Zugriff" angeht. Generell will ich keinerlei geschützten Zugriff auf die Daten in der Datenbank.

Verbindungsaufbau:
Delphi-Quellcode:
    db.Close;
    db.KeepConnection := HAL_Registry_GetBool('KeepConnection', True);
    db.LoginPrompt := False;
    db.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;'
                       + 'Data Source=' + _FileName + ';'
                       + 'Mode=Share Deny None;'
                       + 'Persist Security Info=False;';
    db.Open;
Aus der Recherche und Forensuche habe ich herausgefunden, dass sich die TAdoConnection unter bestimmten Bedingungen selbständig in den Exklusiven Modus schaltet:
1) Änderung an der Datenbankstruktur
2) ???
In der Tat ist es richtig, dass mein Programm auch die Datenbank nach einem Update auf das aktuelle Patchlevel anhebt (hinzufügen von Spalten oder sogar ganzen Tabellen) - laut Forum springt hier die AdoConnection in den exklusiven Modus. Dies geschieht jedoch nur einmalig nach dem Update, nicht bei jedem Programmstart. Und danach wird die TAdoConnection eh getrennt und neu verbunden - das soll angeblich helfen, damit Share Deny None wieder richtig greift.

-Sind andere Situationen bekannt, in denen ich mit ADO versehentlich eine exklusive Sperre auf eine Tabelle legen könnte?
-Spielt die CursorLocation eine Rolle? Diese wird von mit aktuell nicht explizit gesetzt und verwendet den Standardwert...
-Wird die Sperre sicher damit aufgehoben, dass ich einfach nur db.close; db.open; mache, oder muss ich nochmal explizit den ConnectionString neu zuweisen oder eine andere Eigenschaft der TAdoConnection ändern?
-Wonach kann ich noch schauen?

(M)eine aktuelle Empfehlung an die Kunden lautet, meine datenbank.mdb und datenbank.ldb von der Echtzeitprüfung im Virenscanner auszunehmen und die Umstellung auf SQL priorisiert in Betracht zu ziehen.

Sonst noch eine Idee?
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#2

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 7. Okt 2020, 16:22
Ich bitte vielmals um Entschuldigung für diesen Bump, aber es gibt wichtige Erkenntnisse:

Das hier ist mein Befehl zum Aufbau der Datenbankverbindung:
Delphi-Quellcode:
    db.Close;
    db.KeepConnection := HAL_Registry_GetBool('KeepConnection', True);
    db.LoginPrompt := False;
    db.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;'
                       + 'Data Source=' + _FileName + ';'
                       + 'Mode=Share Deny None;'
                       + 'Persist Security Info=False;';
    db.Open;
Vor db.open ist
Code:
db.ConnectionString=Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\test\db.mdb;Mode=Share Deny None;Persist Security Info=False;
NACH db.open ist
Code:
db.ConnectionString=Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source=C:\test\db.mdb;Mode=Share Deny None;Jet OLEDB:System database="";Jet OLEDB:Registry Path="";Jet OLEDB:Database Password="";Jet OLEDB:Engine Type=5;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database Password="";Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Don''t Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False;
d.h. Delphi, Ado (oder wer auch immer) schreibt mir die ganzen Attribute in den ConnectionString rein, bzw. listet alle Eigenschaften auf, die über diese Api für diese Datenbankverbindung verfügbar sind. Diese Attribute kann man ja regulär z.B. über
db.Properties.Item['Mode'].Value setzen oder auslesen.

Kann es nun sein, dass eines dieser Attribute für den exklusiven Zugriff sorgt?

Jet OLEDB:Database Locking Mode=1 habe ich hier gefunden: https://docs.microsoft.com/de-de/sql...l-server-ver15 , es wird wohl der Datentyp DBPROP_JETOLEDB_DATABASELOCKMODE verwendet:
Zitat:
Jet OLEDB:Database Locking Mode (DBPROP_JETOLEDB_DATABASELOCKMODE) Indicates the locking mode for this database. The first user to open the database determines the mode used while the database is open.
Leider wird nicht erklärt, was für Werte zulässig sind. Auf https://docs.microsoft.com/en-us/pre...723619(v=vs.85) wird DBPROP_JETOLEDB_DATABASELOCKMODE beschrieben:
Zitat:
Type: VT_I4

Typical R/W: R/W

Description: Jet OLEDB:Database Locking Mode

Scheme to use when locking the database.

Constants:

DBPROPVAL_DL_OLDMODE ? Old mode used in previous releases of Jet's storage engine

DBPROPVAL_DL_ALCATRAZ ? Alcatraz mode. Enables locking mode that allows for row-level locking. This does not preclude page locking.

A database can be open in only one mode at a time. The first user to open the database determines the locking mode used while the database is open.
Leider fehlen hier die Delphi-Entsprechungen.

Eine Google-Suche bringt mich hier hin:
Zitat:
DBPROPVAL_DL_OLDMODE = 0 - Mode used in previous versions of the Jet database
DBPROPVAL_DL_ALCATRAZ = 1 - Mode used in Jet 4 and later, allowing row level locking
Scheinbar wird also meine Jet-Verbindung automatisch im Modus "Jet OLEDB:Database Locking Mode= 1 = DBPROPVAL_DL_ALCATRAZ = Mode used in Jet 4 and later, allowing row level locking" aufgebaut. Bedeutet das jetzt, dass MEHR oder WENIGER gesperrt wird, als vorher? Da es ja "row level locking" heißt, und das andere "oldmode", nehme ich an, dieser Modus ist neuer=besser=weniger gesperrt. Oder nicht? Wenn nicht, wäre es die Erklärung, wieso die Software auf dem ersten PC läuft, aber auf den Folgenden nicht: die Datenbank wäre dann durch den ersten PC exklusiv geöffnet. Ich denke mal aber nicht, dass Modus 1 das bewirkt, oder?

Gibt es noch andere Parameter für den Verbindungsaufbau, den ich berücksichtigen oder ändern muss, damit die Datenbank nicht exklusiv geöffnet ist?


Gerade eben hatte ich auch (lokal, mit nur einem PC, aber dafür 2 offenen TAdoConnection innerhalb der .exe) diese Meldung:
Zitat:
Datei konnte nicht gesperrt werden / SELECT TOP 1 * FROM T_Integer WHERE ID=:pID
Wieso, weshalb, warum jetzt das? Das ist eine nur-lesen Operation. Werden da jetzt auch schon die Datensätze gesperrt?


Edit:
-Wie kann ich prüfen, ob sich die aktuelle TAdoConnection in einem Exklusiven Modus befindet?
-Gibt es für TAdoQuery eine Einstellung, die einen exklusiven Zugriff bewirken?
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit

Geändert von berens ( 7. Okt 2020 um 17:54 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.490 Beiträge
 
Delphi 7 Professional
 
#3

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 7. Okt 2020, 18:17
Könnte es sein, dass eines der in Deinem Connctionstring fehlenden Attribut die Fehlerursache ist?

Dein Connectionstring ist jedenfalls unvollständig und wird daher beim Verbindungaufbau mit Defaultwerten"bestückt". Das kann funktionieren, muss es aber nicht und schon garnicht dauerhaft.

Bitte bau mit dem Objektinspektor (o. ä.) einen Connectionstring auf und setzt diesen dann in Deinem Programm ein.

Welchen Wert hat z. B. OLEDB: Database Locking Mode, wenn es im Connectionstring nicht angegeben wurde? 0 oder 1. Damit hast Du dann ggfls. schonmal das erste Problem.

Es ist immer sinnvoll, wenn alle Programme, die auf eine Datenbank zugreifen, den gleichen und vollständigen Connectionstring nutzen und nicht einen, der mal so gerade eben (hoffentlich) den Minimalanforderungen gerecht wird.

Wenn das Problem dann weiterbesteht, müssen wir mal schauen, ob es auch eine "externe" Ursache für das Problem geben könnte.

Momentan bin ich mir nicht sicher, ob die von Dir gewählte Variante dauerhaft stabil funktionieren kann und dauerhaft stabil funktionieren muss.

Jedenfalls hab' ich, seit dem ich mir angewöhnt habe, ConnectionStrings grundsätzlich vollständig zu erstellen, keine Probleme mehr bei dem Zugriff auf Datenbanken über ADO gehabt.
  Mit Zitat antworten Zitat
jobo

Registriert seit: 29. Nov 2010
3.072 Beiträge
 
Delphi 2010 Enterprise
 
#4

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 7. Okt 2020, 18:48
Die Exclusivsperre und die Meldung "zu viele Nutzer" könnte auch die Folge von fehlerhaften oder falsch bewerteten Zugriffsversuchen sein.
Muster: Jeder Zugriff der 3 Nutzer wird (auf Dateiebene) nicht sauber beendet, jeder weitere Zugriff ist dann ggF. eine neue Verbindung.
Ich würde mal schauen, ob ich auf Seite des Dateiservers, der die Access Datei und die Lockdatei hostet, einen genauen Überblick über die (offenen) Dateihandles bekommen kann. Falls es immer mehr werden, stimmt ja die Meldung "zu viele Nutzer".
Gruß, Jo
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 7. Okt 2020, 21:10
Schon mal danke für die Rückmeldungen!

Zitat:
Es ist immer sinnvoll, wenn alle Programme, die auf eine Datenbank zugreifen, den gleichen und vollständigen Connectionstring nutzen und nicht einen, der mal so gerade eben (hoffentlich) den Minimalanforderungen gerecht wird.
Meine Herangehensweise war bei der aktuellen Umstellung: Auf Kompatibilität achten, nur die Werte explizit festlegen, die auch benötigt werden. Deine Argumentation im Sinne "Nichts dem Zufall überlassen" kann ich natürlich aus dieser Sichtweise heraus gut nachvollziehen. Ich habe mit einer TAdoConnection auf einem neuen Form beispielhaft eine Verbindung zu einer Access-Datenbank eingerichtet. Der vom Assistenten generierte String war fast identisch mit meinem. Erst beim Aufbau der Verbindung wurde der ConnectionString dieser Komponente so erweitert, das er letztendlich identisch ist als der im vorherigen Post genannte nach "db.open". Auf deinen Rat hin habe ich nun also meine Verbindungsaufbau-Prozedur (die von allen Programmteilen verwendet wird, sobald eine Verbindung zur einer Access-Datenbank hergestellt werden muss) angepasst:

Delphi-Quellcode:
    db.Close;
    db.Mode := cmShareDenyNone;
    db.KeepConnection := HAL_Registry_GetBool('KeepConnection', True);
    db.LoginPrompt := False;
    db.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;'
                       + 'User ID=Admin;'
                       + 'Data Source=' + _FileName + ';'
                       + 'Mode=Share Deny None;'
                       + 'Jet OLEDB:System database="";'
                       + 'Jet OLEDB:Registry Path="";'
                       + 'Jet OLEDB:Database Password="";'
                       + 'Jet OLEDB:Engine Type=5;'
                       + 'Jet OLEDB:Database Locking Mode=1;'
                       + 'Jet OLEDB:Global Partial Bulk Ops=2;'
                       + 'Jet OLEDB:Global Bulk Transactions=1;'
                       + 'Jet OLEDB:New Database Password="";'
                       + 'Jet OLEDB:Create System Database=False;'
                       + 'Jet OLEDB:Encrypt Database=False;'
                       + 'Jet OLEDB:Don''t Copy Locale on Compact=False;'
                       + 'Jet OLEDB:Compact Without Replica Repair=False;'
                       + 'Jet OLEDB:SFP=False;'
                       + 'Persist Security Info=False;';

// if db.CursorLocation <> clUseServer then begin
// db.CursorLocation := clUseServer;
// end;

    c1 := db.ConnectionString;
    db.Open;
    c2 := db.ConnectionString;
Beim Betrachten der TAdoConnection in diesem neuen Form ist mir wieder in den Sinn gekommen, dass ja viele Attribute direkt am Delphi-Objekt geändert werden können (z.b. db.Mode := cmShareDenyNone; ). Ob dies jetzt Vorrang vor dem 'Mode=Share Deny None;' aus dem ConnectionString hat, sich evtl. sogar wiederspricht oder ignoriert wird: Ich habe es vorsichtshalber nochmal explizit reingeschrieben, im notfall gilt hier: "Doppelt hält besser", da die Werte ja an beiden Stellen identisch sind.

Die Idee mit dem "Minimalismus" war und ist auch die Angst vor Exceptions beim Verbindungsaufbau. Falls mein Programm jetzt unter einer anderen Windows-Version eingesetzt wird, und ich ein Attribut mitgebe, was dort vielleicht nicht bekannt ist oder unterstützt wird: Jetzt geht hier gar nichts mehr (Verbindungsaufbau aller Datenbanken schlägt fehl), weil ein -von mir eh nicht "gewolltes"- übergebens Attribut nicht bekannt ist. Wahrscheinlich passiert gar nichts, und die Sorge ist unbegründet. Z.B. verwende ich seit Jahren 'Persist Security Info=False;' - der Assistent baut das Attribut auch beim Verbindungsaufbau nicht mit ein - zu stören scheint es aber auch nicht.

Was mir hier noch Sorgen macht, ist db.CursorLocation. Ich habe in der OnlineHilfe und im Internet schon einiges drüber gelesen, weiß aber nicht, "ob" und in "wie weit" das für mich relevant ist. Nehmen wir mal an, alle Abfragen bei allen Tabellen und Kunden liefern i.d.R. nicht mehr als 20 Datensätze zurück. Hat CursorLocation jetzt eine Auswirkung auf irgendwelche Datensatz- oder Tabellensperren? Auf die Geschwindigkeit? In der OH steht u.a. "Viele Server unterstützen außerdem nur Vorwärts-Cursor, bei denen der Satzzeiger in der Ergebnismenge nicht in Richtung Tabellenanfang bewegt werden kann." --> Unerwartetes Verhalten / Probleme beim Ausführen einfacher Operationen wie AdoQuery.First etc.? Hört sich gefährlich an... Generell würde ich den Wert ja "unverändert" auf clUseClient lassen.
-Spricht was dagegen?
-TAdoQuery hat auch die Eigenschaft "CursorLocation". Hier ebenfalls clUseClient?


Zitat:
Ich würde mal schauen, ob ich auf Seite des Dateiservers, der die Access Datei und die Lockdatei hostet, einen genauen Überblick über die (offenen) Dateihandles bekommen kann
Da gebe ich dir vollkommen recht. Ich denke, die fehlgeschlagenen Abfragen haben die Datenbankverbindung nicht ordentlich wieder abgebaut.

-Erzeugt nur jede neue TAdoConnection.Open ein zusätzliches Handle auf die .mdb-Datei oder auch schon z.B. jedes TAdoQuery mit .Open?

Bei der großen Umstellung (Link siehe Oben) wurde auch flächig der Zugriff der AdoQuerys überarbeitet. In der Tat habe ich schon bei vielen Kunden auf den FileServern gesehen, das es *schäm* sehr, sehr viele offene Handles auf die Datenbankdatei gegeben hat. Aus anderen Beiträgen hier im Forum habe ich entnommen, dass es nicht langt, ein AdoQuery einfach nur freizugeben (mit FreeAndNil), nein es MUSS explizit mit "Close" geschlossen werden. Bestätigung, Ja, Nein, andere Meinung?

Beispielhaft sieht mein Zugriff auf die Datenbank immer so oder ähnlich aus:

Delphi-Quellcode:
function TRecurrencePattern.SaveToDatabase(db: TAdoQuery): Boolean;
var
  i: integer;
  q: TADOQuery;
begin
  Result := False;

  q := NIL;
  try
    if not assigned(db) then begin
      Log('TRecurrencePattern.SaveToDatabase: db unassigned.');
      Exit;
    end;

    q := TADOQuery.Create(Self);
    q.Connection := db;
    CloseQueryAndClearSQLText(q);

    // diese vier Zeilen sind in eigenen Prozeduren mit dortiger Fehlerbehandlung, nur zum Verständnis hier direkt
    q.SQL.Add('SELECT * FROM T_SerienMuster WHERE refTermin=:ID');
    q.Parameters.ParamByName('ID').Value := _refTermin;
    q.Open;
    q.First;


      if q.Eof then q.Insert else q.Edit;

      q.FieldByName('refTermin').AsInteger := TerminID;
      q.FieldByName('blRecurrsJAN').AsBoolean := blRecurrsJanuar;
      q.FieldByName('blRecurrsFEB').AsBoolean := blRecurrsFebruar;
      q.FieldByName('blRecurrsMAR').AsBoolean := blRecurrsMaerz;
      q.FieldByName('blRecurrsAPR').AsBoolean := blRecurrsApril;
      q.FieldByName('blRecurrsMAY').AsBoolean := blRecurrsMai;
      // usw.
      q.Post;

    Result := True;
  except
    on E: Exception do begin
      Log('TRecurrencePattern.SaveToDatabase', M, E.Message);
    end;
  end;
  FreeAndNil(q);
-try..finally wäre natürlich innerhalb von try..except feiner, und so würde ich das heute auch machen. Aber so muss q auch ordentlich freigegeben werden: q wird ja am Anfang mit NIL initialisiert, und im schlimmsten Fall wird FreeAndNil(Nil) aufgerufen - es passiert nichts schlimmes. Im Falle einer Exception wird nun das wirklich erzeute q ordnungsgemäß freigegeben, da das Programm nach except..end weiterläuft, und diese Zeile somit in jedem Fall aufgerufen wird. Daran sollte es -wenn auch "ungewöhnlich" im vergleich zu einem eingebetteten try..finally- also nicht liegen, dass unnötige FileHandles auf dem Server offen bleiben.

-Diese Prozedur ist wieder ein Paradebeispiel für (meine) schlampige Arbeit: nach dem q.Post fehlt das q.Close. Meine frühere Erwartung an TAdoQuery waren schlicht und ergreifend: Wenn ich es freigebe, wird es schon selbst für eine ordentliche Beendigung sorgen (.Close), und nicht einfach "das Messer in der Sau stecken lassen". Wenn ich das richtig verstehe, ist/könnte aber genau das sein, und ich muss dem TAdoQuery noch explizit hinterherräumen. Wenn man's weiß ist ja gut - wenn man mit einer falschen Erwartung dran geht... naja - woher soll man's wissen. Auch hier spielt wieder die "Angst" vor einer Exception eine große Rolle. Wenn ich das Programm jetzt wie folgt um schreiben würde:
Delphi-Quellcode:
function TRecurrencePattern.SaveToDatabase(db: TAdoQuery): Boolean;
var
  i: integer;
  q: TADOQuery;
begin
  Result := False;

  q := NIL;
  try
    try
      if not assigned(db) then begin
        Log('TRecurrencePattern.SaveToDatabase: db unassigned.');
        Exit;
      end;

      q := TADOQuery.Create(Self);
      q.Connection := db;
      CloseQueryAndClearSQLText(q);

      // diese vier Zeilen sind in eigenen Prozeduren mit dortiger Fehlerbehandlung, nur zum Verständnis hier direkt
      q.SQL.Add('SELECT * FROM T_SerienMuster WHERE refTermin=:ID');
      q.Parameters.ParamByName('ID').Value := _refTermin;
      q.Open;
      q.First;

      if q.Eof then q.Insert else q.Edit;

      q.FieldByName('refTermin').AsInteger := TerminID;
      q.FieldByName('blRecurrsJAN').AsBoolean := blRecurrsJanuar;
      q.FieldByName('blRecurrsFEB').AsBoolean := blRecurrsFebruar;
      q.FieldByName('blRecurrsMAR').AsBoolean := blRecurrsMaerz;
      q.FieldByName('blRecurrsAPR').AsBoolean := blRecurrsApril;
      q.FieldByName('blRecurrsMAY').AsBoolean := blRecurrsMai;
      // usw.
      q.Post;

      Result := True;
    finally
      q.Close;
      FreeAndNil(q);
    end;
  except
    on E: Exception do begin
      Log('TRecurrencePattern.SaveToDatabase', M, E.Message);
    end;
  end;
Für FreeAndNil sollte es zunächst keinen Unterschied machen. Wenn es aber nun -aus welchen Gründen auch immer- vorkommen *könnte* dass die Erzeugung von q fehlschlägt, bzw. dieses im Laufe der Prozedur auf einen ungültigen Arbeitsspeicherbereich zeigt (jaja, Paranoia etc., aber spielen wir das Spiel mal zu Ende, bitte!) bzw. eine Exception zwischendrin ausgelöst wird, und finally nun angesteuert wird: q.Close würde/könnte hier eine *zusätzliche* Exception schmeißen (weil q ja ungültig oder der Close-Befehl "einfach so" eine Exception wirft), und dann wird FreeAndNil(q) ja gar nicht aufgerufen --> wirkliches MemoryLeak.
-Wie setzt man sowas korrekt um? Wie habt Ihr das gelöst?
-Ist
Delphi-Quellcode:
  finally
    try
      q.close;
    finally
      FreeAndNil(q);
    end;
  end;
genau richtig, falsch, übertrieben, ...?

Danke im vorraus!
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.490 Beiträge
 
Delphi 7 Professional
 
#6

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 8. Okt 2020, 11:50
Hilfe, soviele Fragen und so richtig kann ich das alles nicht beantworten

Hier erstmal ein Stück Code aus 'nem Programm, das Ado nutzt und auf diverse Datenbanken zugreifen können sollen muss
Delphi-Quellcode:
  // Hier den Datenbanktypen abfragen und die Werte für Cursor ... setzen.
  // CursorLocation bei SQLite muss wohl clUseClient sein,
  // bei Access aber clUseServer
  // Hier noch testen, was bei SQLite am sinnvollsten ist.
  if fDBIsSQLite then begin
    // ctUnspecified, ctOpenForwardOnly, ctKeyset, ctDynamic, ctStatic
    // Diese Variante spart Arbeitsspeicher und ist schnell.
    // Aber es gibt keine RecNo.
    // Änderungen von Datensätzen nicht möglich.
    // con.CursorLocation := clUseServer; // Dann gibt es keine RecNo :-(
    // qrySQL.CursorType := ctKeyset;
    // qrySQL.CursorType := ctStatic;
    // Benötigt viel Arbeitsspeicher, deutlich mehr, als die Datenbankgröße.
    // Ist sehr langsam.
    qrySQL.CursorLocation := clUseClient;
    qrySQL.CursorType := ctDynamic;
    // qrySQL.CursorType := ctStatic;
    // qrySQL.CursorType := ctKeyset;
    dbckOK.ValueChecked := 'T';
    dbckOK.ValueUnchecked := 'F';
  end else
  if fDBIsAccess then begin
    qrySQL.CursorLocation := clUseServer;
    qrySQL.CursorType := ctStatic;
    dbckOK.ValueChecked := 'Wahr';
    dbckOK.ValueUnchecked := 'Falsch';
  end else
  if fDBIsFirebird then begin
    qrySQL.CursorLocation := clUseClient;
    qrySQL.CursorType := ctDynamic;
    dbckOK.ValueChecked := '1';
    dbckOK.ValueUnchecked := '0';
  end else begin
    // Ansonsten?
    qrySQL.CursorLocation := clUseClient;
    qrySQL.CursorType := ctDynamic;
    dbckOK.ValueChecked := '1';
    dbckOK.ValueUnchecked := '0';
  end;
Abhängig vom Datenbanktyp unterscheiden sich die Werte für CursorLocation und CursorType. Ohne diese Unterscheidung gibt es garantiert Fehler (zumindest auf meinen Systemen).

Da das Ganze schon ein paar Jahre alt ist, weiß ich nicht mehr so genau, wie ich zu welchen Werten kam, aber seit dem scheint es rund zu laufen.

Mein Vorgehen wäre von daher zuerst mal mit CursorLocation und CursorType zu experimentieren, bis Dein Testprogramm, das Du zweimal gestartet hast, keinen Fehler mehr wirft oder Du garantiert alle Kombinationen der beiden Eigenschaften "durchhast" und weißt, dass sie alle zu Fehlern führen.

Mit dieser Funktion baue ich mir aus meinen Programmen heraus den Connectionstring auf (sofern wahlweise auf unterschiedliche Datenbanken zugegriffen werden soll).
Delphi-Quellcode:
function fnGetDataSourceString(Handle : THandle;
                               ADOConnection : TADOConnection;
                               sConnection : String = '') : Boolean;
begin
  ADOConnection.Connected := False;
  ADOConnection.ConnectionString := PromptDataSource(handle,sConnection);
  ADOConnection.Connected := True;
  Result := ADOConnection.Connected;
end;
Was kommt bei Deinem Testprogramm dabei raus? Stimmt das mit dem von Dir selbst erstellten Connectionstring überein oder mit dem vom Assistenten generierten? Bei meinen System stimmt es eher mit Deinem (im vorherigen Post) selbst erstellten Connectionstring überein.

Hier ein Beispiel:
Code:
Provider=Microsoft.Jet.OLEDB.4.0;User ID=Admin;Data Source=C:\Support\TipVerwaltung.mdb;Mode=Share Deny None;Extended Properties="";Jet OLEDB:System database="";Jet OLEDB:Registry Path="";Jet OLEDB:Database Password="";Jet OLEDB:Engine Type=5;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database Password="";Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False
Querys werden bei mir nach Gebrauch sofort per Close geschlossen. Allerdings erstelle ich sie nicht dynamisch, sondern hab' sie gewöhnlich auf 'nem TDataModule, auch wenn es da durchaus mal eine ganze Reihe für unterschiedliche Zwecke geben kann.

Zur Reihenfolge:

Weise bitte zuerst den Connectionstring zu und ändere erst dann (soweit erforderlich) die Eigenschaften der ADOConnection. Ich gehe mal davon aus, dass die von Dir gesetzten Werte beim Setzen des Connetionstring "überschrieben" werden. Machst Du die Änderungen nach der Zuweisung des Connectionstrings, werden "seine" Werte durch Deine überschrieben. Damit wärst Du dann quasi der Gewinner. So, wie Du es momentan zu machen scheinst, gewinnt jedoch der Connectionstring.

Als erste Idee (ohne Garantie für irgendwas):
Delphi-Quellcode:
    db.Close;
    db.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;'
                       + 'User ID=Admin;'
                       + 'Data Source=' + _FileName + ';'
                       + 'Mode=Share Deny None;'
                       + 'Jet OLEDB:System database="";'
                       + 'Jet OLEDB:Registry Path="";'
                       + 'Jet OLEDB:Database Password="";'
                       + 'Jet OLEDB:Engine Type=5;'
                       + 'Jet OLEDB:Database Locking Mode=1;'
                       + 'Jet OLEDB:Global Partial Bulk Ops=2;'
                       + 'Jet OLEDB:Global Bulk Transactions=1;'
                       + 'Jet OLEDB:New Database Password="";'
                       + 'Jet OLEDB:Create System Database=False;'
                       + 'Jet OLEDB:Encrypt Database=False;'
                       + 'Jet OLEDB:Don''t Copy Locale on Compact=False;'
                       + 'Jet OLEDB:Compact Without Replica Repair=False;'
                       + 'Jet OLEDB:SFP=False;'
                       + 'Persist Security Info=False;';
    db.KeepConnection := True; // HAL_Registry_GetBool('KeepConnection', True);
    db.LoginPrompt := False;
    db.CursorLocation := clUseServer;
    db.CursorType := ctStatic;
    db.IsolationLevel := ilReadCommitted;
    db.Mode := cmReadWrite;

    c1 := db.ConnectionString;
    db.Open;
    c2 := db.ConnectionString;
Damit scheinen bei mir mehrere Instanzen eines Programmes problemlos mit einer Accessdatenbank arbeiten zu können.
  Mit Zitat antworten Zitat
berens

Registriert seit: 3. Sep 2004
434 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 8. Okt 2020, 12:15
Vielen Dank für die detaillierte Antwort.

Ja, bei mir kommt auch der lange ConnectionString raus, in sofern verwende ich den jetzt auch.

Ich habe all meine Quelltextdatei überarbeitet:
-Es wird nun häufiger eine über Parameter übergebene TAdoConnection verwendet, anstatt selbst (temporär) eine neue zu Erstellen
-Überall wo eine temporäre AdoConnection erstellt wird, gebe ich paranoia-mäßig alles mit try..finally frei
Delphi-Quellcode:
      finally
        CloseQueryAndClearSQLText(q);
      end;
    finally
      db.Close;
    end;
  finally
    FreeAndNil(q);
    FreeAndNil(db);
  end;
Was mich mit deinem Post jetzt aufgeschreckt hat:
Bisher dachte ich, dass cmReadWrite letztendlich doch "Einzel-Benutzung" bedeutet, da das Wort "Share" fehlt. Ich verwende bis dato (imo problemlos?) immer cmShareDenyNone - "Alle Teilen, nichts verbieten". So wurde das glaube ich auch überall empfohlen und in den Beispielen verwendet. Ich bin eben fast vom Hocker gefallen, als ich in der OH nachgeschlagen habe:
Code:
cmReadWrite - Die Verbindung verfügt über Lese-/Schreibrechte.
cmShareDenyRead - Die Verbindung kann von anderen Benutzern nicht mit Leseberechtigung geöffnet werden.
cmShareDenyWrite - Die Verbindung kann von anderen Benutzern nicht mit Schreibberechtigung geöffnet werden.
cmShareDenyNone - Die Verbindung kann mit keiner Art von Zugriffsrecht von anderen Benutzern geöffnet werden.
cmShareDeny... : Der Benutzer kann alles, ausser Read-Lesen, Write-Schreiben, None-Nichts --> Der Benutzer kann alles, mit Ausnahme von nichts - ohne Ausnahme, siehe auch: https://www.delphipraxis.net/107684-post4.html
Zitat:
nein, MSAccess ist auch für einen Mehrbenutzermodus vorgesehen

der Verbindungsstring wird normalerweise mit den Zugangsberechtigungen "share deny none' also jeder kann gleichzeitig zugreifen
s.a. Connection.Mode cmShareDenyNone
Edit: Ebenfalls hier wird rauf gepocht, das cmReadWrite nicht im Netzwerk ausreicht, und cmShareDenyNone zu verwenden ist: https://www.delphipraxis.net/43690-a...ert-nicht.html


Hier bräuchte ich jetzt bitte echt schnell nochmal Rückmeldung:
Was Unterscheidet cmShareDenyNone von cmReadWrite? Ist cmReadWrite wirklich für Mehrbenutzer-Zugriff für Access-Datenbanken über Netzwerk geeignet? Können dabei (z.B. nach einem Update) [einmalig] Tabellen angepasst werden (Spalten hinzufügen, ...), oder schlagen irgendwelche Befehle fehl?
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit

Geändert von berens ( 8. Okt 2020 um 12:19 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.490 Beiträge
 
Delphi 7 Professional
 
#8

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 8. Okt 2020, 12:41
Ehrlich gesagt: Keine Ahnung.

Habe seinerzeit rumprobiert, bis ich zu dem Schluss kam, dass die oben von mir gepostete Variante (für meine Begriffe) ordentlich funktioniert.

Die Formulierung
Code:
cmShareDenyNone - Die Verbindung kann mit keiner Art von Zugriffsrecht von anderen Benutzern geöffnet werden.
klingt für mich so: Die Anderen dürfen nix.
Aber: Man kann die Formulierung durchaus auch anders interpretieren.

Für mich klingt das so: Egal mit welchen Rechten es die anderen versuchen, sie werden scheitern.

Aber man kann es auch so interpretieren: Egal welche Rechte die Anderen nutzen, sie können zugreifen.

Bei dieser Art von Hilfe bin ich immer hilflos, denn die Aussage entspricht in etwa einem "Entschiedenen sowoh als auch" (oder so ähnlich).

Viele AdoConnection = viele Handles, viele Querys = viele Handles.

Was ich nicht weiß, inwieweit die Handles beim Freigeben der AdoConnections bzw. der Querys sofort "verschwinden".

Von daher ist mein Vorgehen immer so:

Alles, was ich brauche, wird bereits in der Entwicklungsumgebung auf's Formular ... gepappt. Dabei gilt: Von allem so wenig wie möglich.

Mehrere AdoConnetions nur, wenn auch mehrere Datenbanken genutzt werden. Pro DB nur eine Connection.

Querys: Eine für Abfragen, eine für Insert, Update, Delete ... (also den Rest) (ggfls. pro DB).

Pro DBGrid, DBCtrlGrid, DataSource (o. ä.) (sofern welche zum Einsatz kommen) eine Query.

Connections verbinden sich beim Programmstart mit der Datenbank und beenden die Verbindung beim Programmende. Auch hier gilt: So wenig wie möglich.

Ob dieses Vorgehen nun den Regeln der Kunst entspricht, mit irgendwelchen Vorgehensmodellen, ... konform geht, keine Ahnung. Fahre mit diesem Vorgehen seit fast 2 Jahrzehnten gut und solange läuft auch schon ein Teil meiner Software ohne Mucken.

Geändert von Delphi.Narium ( 8. Okt 2020 um 12:42 Uhr) Grund: Schreibfehler
  Mit Zitat antworten Zitat
Benutzerbild von Dalai
Dalai

Registriert seit: 9. Apr 2006
1.682 Beiträge
 
Delphi 5 Professional
 
#9

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 8. Okt 2020, 13:30
Vielleicht lohnt sich ein Blick auf die Englische Erklärung:
Connect ModeMeaning
cmUnknownPermissions are not yet set for the connection or cannot be determined.
cmReadRead-only permissions are available to the connection.
cmWriteWrite-only permissions are available to the connection.
cmReadWriteRead/write permissions are available to the connection.
cmShareDenyReadPrevents others from opening connections with read permissions.
cmShareDenyWritePrevents others from opening connection with write permissions.
cmShareExclusivePrevents others from opening connection.
cmShareDenyNonePrevents others from opening connection with any permissions.
Klingt für mich danach, als müsste man cmShareDenyNone als cmShareDenyAll, cmShareDenyAny oder cmShareDenyEverything deuten. Und ich frage mich, was dann noch der Unterschied zwischen cmShareDenyNone und cmShareExclusive sein soll.

Sorry, wenn der Beitrag nicht (sonderlich) hilfreich gewesen sein sollte.

Grüße
Dalai
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.490 Beiträge
 
Delphi 7 Professional
 
#10

AW: Access-Datenbank gesperrt - Problem mit Windows-Update?

  Alt 8. Okt 2020, 13:55
Dein Beitrag besagt genau das, was ich vermute.

cmShareDenyNone = Außer mir darf keiner was.

Geändert von Delphi.Narium ( 8. Okt 2020 um 13:57 Uhr) Grund: Fehler behoben
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:53 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz