Zitat:Aber egal, was ich anstelle, ich schaffe es nicht, dass bei der Verwendung von DAO aus Excel oder Word heraus überhaupt ein Access-Prozess gestartet würde.
Wenn mit Excel per DAO/ADO auf eine Access-Datenbankdatei zugegriffen wird, wird kein externer Prozess gestartet. Access wird dabei nicht verwendet und muß noch nicht mal auf dem Rechner installiert sein. Es kann also keinen separaten Prozess geben.
Zitat:... dass mehrere User auf die Datenbank zugreifen können sollen.
Die Access Datenbankdatei kann wie jede andere Datei auch, von mehreren Prozessen gleichzeitig geöffnet werden, solange diese Datei nicht exklusiv von einem Prozess geöffnet wird. Eine Datenbankdatei wird nur beim Öffnen mit .Opendatase() exklusiv geöffnet werden. ReadOnly zeigt in diesem Zusammenhang an, daß der öffnende Prozess nur Leseoperationen auf der Datenbank ausführt und bezieht sich nicht auf andere Prozesse.
Zitat:Mittlerweile geht es um den Type des Recordsets, der bestimmt meiner Kenntnis nach aber nur das Verhalten des Cursors und hat nichts mit ReadOnly oder einem übriggebliebenen Prozess zu tun.
Nicht ganz. Ein Recordset vom Typ Snapshot ist immer ReadOnly, alle anderen Typen können über Optionen als Readonly gekennzeichnet werden. Auch hat der Type Einfluß auf den Cursor, z.B. dbForwardOnly, der im Recordset verwendet wird.
Mit den Locking Parametern läßt sich wiederum das Verhalten bei gleichzeitigen, konkurrierenden Zugriffen steuern.
Das Thema ist aber so umfangreich, daß es den Rahmen eines Forums sprengen würde. Insgesamt kann man aber sagen, daß generell mit den Defaultwerten gearbeitet werden kann und damit ein gutes Ergebnis erzielt wird. Alles andere betrachtet Sonderfälle und um die richtig anzuwenden, sollte man sich schon mit Datenbanken auskennen, sonst geht das schief.
Ich hoffe, daß es damit etwas klarer und verständlicher geworden ist.
Grüße Ulrich
Folgende(r) 1 Nutzer sagt Danke an knobbi38 für diesen Beitrag:1 Nutzer sagt Danke an knobbi38 für diesen Beitrag 28 • Stefan1
07.11.2024, 13:56 (Dieser Beitrag wurde zuletzt bearbeitet: 07.11.2024, 13:58 von knobbi38.)
Hallo,
hier nochmal etwas Code:
Code:
Sub GetRows_Samples() Dim lst As MSForms.ListBox Dim dbe As DAO.DBEngine Dim dbs As DAO.DATABASE Dim rst As DAO.Recordset Dim vaRecords As Variant
Const DATABASE_NAME As String = "X:\Database1.accdb"
Set lst = Tabelle1.lstNamen
Set dbe = New DAO.DBEngine Set dbs = dbe.OpenDatabase(DATABASE_NAME) Set rst = dbs.OpenRecordset("SELECT Id, iif(isNull(Vorname),'',Vorname), Nachname FROM tblData", dbOpenSnapshot)
' Assign data to a list box If Not (rst.EOF And rst.BOF) Then rst.MoveLast rst.MoveFirst lst.ColumnCount = rst.Fields.Count lst.Column = rst.GetRows(rst.RecordCount) End If
' Assign data to a range If Not (rst.EOF And rst.BOF) Then rst.MoveLast rst.MoveFirst
vaRecords = rst.GetRows(rst.RecordCount) Range("Tabelle1!H2") _ .Resize(UBound(vaRecords, 2) + 1, UBound(vaRecords, 1) + 1) _ .Value = WorksheetFunction.Transpose(vaRecords) End If
' Assign data to a range with CopyFromRecordset rst.MoveFirst Tabelle2.Range("A1").CopyFromRecordset rst
' Cleanup rst.Close: Set rst = Nothing dbs.Close: Set dbs = Nothing Set dbe = Nothing End Sub
Da, wie bereits erwähnt, Transpose() nicht mit NULL umgehen kann, wird das vorsorglich im Select beim Feld Vorname in einen Leerstring umgewandelt. .CopyFromRecordset() kann übrigens mit NULL umgehen.
Gruß Knobbi38
Folgende(r) 1 Nutzer sagt Danke an knobbi38 für diesen Beitrag:1 Nutzer sagt Danke an knobbi38 für diesen Beitrag 28 • Stefan1
(07.11.2024, 12:26)knobbi38 schrieb: Ich hoffe, daß es damit etwas klarer und verständlicher geworden ist.
Deine Beschreibung entspricht genau meinem bisherigen Verständnis. Und genau deshalb verstehe ich das Problem von Stefan nicht. Ich sehe seine Anfragen als zusammenhängend an. Einen Access-Prozess zu killen ist unnötig, weil DAO keinen eigenen Prozess startet (Thread: https://www.clever-excel-forum.de/Thread...r-herunter ). Den Mehrfachzugriff ohne Schreibrechte zu erstellen ist letztlich trivial, wenn man spätestens das Recordset mit ReadOnly öffnet (dieser Thread, erste Antwort: https://www.clever-excel-forum.de/Thread...#pid295861 ). Der Cursor-Typ ist doch bei Access-Zugriffen in aller Regel irrelevant, weil -zumindest meiner Erfahrung nach- die Performance nicht merklich beeinträchtigt ist. Wenn ich das richtig verstanden habe, will der TE ja "nur" den Inhalt einer Abfrage als Array erhalten. .GetRows() ist der Cursor doch egal (ohne das genau zu wissen: mindestens solange kein .MoveNext() ausgeführt wurde).
Viele Grüße derHöpp
Folgende(r) 1 Nutzer sagt Danke an derHoepp für diesen Beitrag:1 Nutzer sagt Danke an derHoepp für diesen Beitrag 28 • Stefan1
zumindest sollte der TE jetzt genug Informationen haben, um sein Problem selber lösen zu können, aber vielleicht war für die Mitleser ja das ein oder andere interessante mit dabei.
Vielen Dank an alle Unterstützer Ich habe viel gelernt und werde vor allem mit den guten Beispielen jetzt weiterkommen. Inzwischen glaube ich auch nicht mehr an das "Herunterhämmern" von Access, weil ich zwar mutwillig nachgestellt mit dem VBA-Code das machen konnte, anderseits ich trotzdem feststellen musste, dass bei einem fehlerhaften Abbruch von VBA maximal ein völlig leeres Access ohne Datenbank (quasi das Fenster) habe, dass in der Taskliste sichtbar und nur über dieses geöffnet wird und nur mühsam geschlossen werden kann. Auf diese Weise entstandenen "leeren" Access-Instanzen sind zwar im Task-Manager sichtbar und dort auch abschiessbar, jedoch nicht mit dem hier eingangs vorgestellten VBA-Code. In der Zwischenzeit habe ich in den von einem Vorgänger geerbten VBA-Code (ein wahres Caos) mit vielen Access-Abfragen aufgrund eurer Hilfen gründlich saniert und es ist nicht nur spürbar schneller, sondern auch stabiler geworden. Obwohl viele Anwender tägich mehrfach darauf zugreifen und ich den "Hammer-Herunterfahren-Access"-Code protokolliere, wurde dieser bis jetzt kein einziges Mal ausgelöst und auch dieses Phänomen vom "leeren" Access-Fenster ist nicht mehr feststellbar, geschweige dem reproduzierbar.
Also nochmals vielen Dank für die sehr interessanten Code-Beispiele (ich werde mich gerne hier wieder bedienen) und Erläuterungen, welche einen echten Mehrwert geben und die man in dieser Deutlichkeit schwer finden kann. Ich fand die Diskussion sehr angenehm und lehrreich. Bravo für Euren grossen Einsatz. Gruss Stefan1
Folgende(r) 1 Nutzer sagt Danke an Stefan1 für diesen Beitrag:1 Nutzer sagt Danke an Stefan1 für diesen Beitrag 28 • derHoepp
07.11.2024, 22:59 (Dieser Beitrag wurde zuletzt bearbeitet: 07.11.2024, 23:01 von Warkings.)
(07.11.2024, 22:27)Stefan1 schrieb: ... dass bei einem fehlerhaften Abbruch von VBA maximal ein völlig leeres Access ohne Datenbank (quasi das Fenster) habe, dass in der Taskliste sichtbar und nur über dieses geöffnet wird und nur mühsam geschlossen werden kann. Auf diese Weise entstandenen "leeren" Access-Instanzen sind zwar im Task-Manager sichtbar und dort auch abschiessbar, jedoch nicht mit dem hier eingangs vorgestellten VBA-Code. ...
Deine Anforderung war, die Prozess-ID einer Access-Instanz mit einer bestimmten geöffneten Datenbank zu finden.
Alternativ kann man auch die Prozess-IDs aller laufenden Access-Instanzen ermitteln.
Code:
Option Explicit
Private Declare PtrSafe Function EnumProcesses Lib "psapi.dll" (lpidProcess As LongPtr, _ ByVal cb As Long, cbNeeded As Long) As Long Private Declare PtrSafe Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _ ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As LongPtr Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long Private Declare PtrSafe Function GetModuleFileNameEx Lib "psapi.dll" Alias "GetModuleFileNameExA" _ (ByVal hProcess As LongPtr, ByVal hModule As LongPtr, _ ByVal lpFilename As String, ByVal nSize As Long) As Long
Const PROCESS_QUERY_INFORMATION = &H400 Const PROCESS_VM_READ = &H10 Const MAX_PROCESSES As Long = 1024
Public Function ListAccessProcessIDs() As Collection Dim processIDs() As LongPtr Dim cbNeeded As Long Dim i As Long Dim hProcess As LongPtr Dim filePath As String Dim filePathLength As Long Dim accessProcessIDs As New Collection
ReDim processIDs(1 To MAX_PROCESSES)
If EnumProcesses(processIDs(1), MAX_PROCESSES * LenB(processIDs(1)), cbNeeded) = 0 Then MsgBox "Fehler beim Abrufen der Prozesse." Exit Function End If
For i = 1 To cbNeeded / LenB(processIDs(1)) hProcess = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, False, processIDs(i))
If hProcess <> 0 Then ' Holt den Dateipfad des Prozesses filePath = String(255, vbNullChar) filePathLength = GetModuleFileNameEx(hProcess, 0, filePath, Len(filePath)) filePath = Left(filePath, filePathLength)
If InStr(1, filePath, "MSACCESS.EXE", vbTextCompare) > 0 Then accessProcessIDs.Add processIDs(i) End If
CloseHandle hProcess End If Next i
Set ListAccessProcessIDs = accessProcessIDs End Function
Sub ShowAccessProcessIDs() Dim accessProcessIDs As Collection Dim processId As Variant
Set accessProcessIDs = ListAccessProcessIDs()
If accessProcessIDs.Count = 0 Then MsgBox "Keine MS Access-Prozesse gefunden." Else For Each processId In accessProcessIDs Debug.Print "MS Access-Prozess-ID: " & processId Next processId End If End Sub
Kleiner Hinweis: Mit taskkill /FI "IMAGENAME eq MSACCESS*" kann man alle laufenden Access Instanzen beenden. Und wenn man den Schalter /F spendiert, wird "heruntergehämmert".
Folgende(r) 1 Nutzer sagt Danke an Warkings für diesen Beitrag:1 Nutzer sagt Danke an Warkings für diesen Beitrag 28 • Stefan1
08.11.2024, 12:56 (Dieser Beitrag wurde zuletzt bearbeitet: 08.11.2024, 12:57 von knobbi38.)
Hallo Stefan,
Zitat:Auf diese Weise entstandenen "leeren" Access-Instanzen sind zwar im Task-Manager sichtbar und dort auch abschiessbar,
Daraus ergibt sich eindeutig, daß hier Access automatisiert wird und keineswegs "nur" per DAO ein Zugriff auf die Daten erfolgt. Ich hatte schon mehrfach darauf hingewiesen, daß eine Automatisierung von Access für die Datenabfrage nicht benötigt wird.
Du solltest das entweder besser kommunizieren wenn du Automatisierung einsetzen möchtest oder aber einfach mal die Ratschläge der Helfer annehmen und umsetzen. Ich gehe jetzt mal davon aus, daß dir der Unterschied zwischen Automatisierung und Zugriff per DAO Library bekannt ist.
08.11.2024, 22:19 (Dieser Beitrag wurde zuletzt bearbeitet: 08.11.2024, 22:19 von Stefan1.)
Guten Tag Knobbi38
Was Du unter "Automatisierung" meinst, kenne ich nicht. Ich kann nach seitenlangem Studium des VBA-Codes nur das eine sagen, das alles mit DAO Library programmiert ist und keine Daten geschrieben, sondern nur gelesen werden. Mein Vorgänger hat allerdings all die Anweisung wie folgt deklariert:
Set db = DBEngine.OpenDatabase(xxx) Set rs = db.OpenRecordset(s, dbOpenForwardOnly) <-als Beispiel Public db As DAO.Database Public rs As DAO.Recordset
Verweise (aktiviert): Microsoft DAO 3.6 Object Library (Es gibt da keine einzige Microsoft ADO ...-Aktivierung)
Wenn damit eine "Automatisierung" nach Deiner Meinung möglich ist, dann bitte melde mir auf welche Verweise/VBA-Programme ich da achten muss. Zudem gibt es keine einzige "Late-Binding"-Anknüpfung. Vielen Dank.
Mittlerweilen ordne ich das Problem vielmehr dem Umstand zu, dass die globle "db" und kaum set db = nothing um quasi durch mehrere Prozeduren zu kommen, problematisch sein können für dieses Problem. Denn ich konnte provozieren, dass ein fehlender "Error-Handler" (und von denen gab es viele) wie auch fehlende "set db = nothing" im "Error-Handler" (wenn es den einen gibt), sobald ein "set db = ..." einmal zugeteilt wurde und ein Debuggen/Stop im Programm geschieht, genau zu diesem Problem führen kann, dass eine quasi "leere" Access übrig bleibt. Auch ganze Prozeduren mit "Error resume next" ohne Not und dann vorzeitiges unkontrolliertes Beenden, führen nachweislich zu diesem Effekt. Ich habe also den ganzen Code stabiliseren müssen und möglichst sauberes eloquentes Error-Handlung und natürlich auch normal erwarteten Programmablauf mit konsequentem Zurückstellen von "db" und "rs" eingepflegt. Damit konnte ich einiges erreichen. Restlos vermeiden wird man es trotzdem nicht, doch wenn alles vernüftig abläuft, dann ist die Access-Instanz völlig im Hintergrund, sprich unsichtbar. Einfach gesagt, wird beim Beenden nie die "db" zurückgesetzt, kommt es zu diesem Effekt und auch erst dann erscheint ein leeres Access, welches sich nur "verkleinern" lässt bzw. widerwillig via Task-Manager oder gar Neustart beendet lässt. Wenn Du anderer Meinung bist, dann bin ich gespannt auf eine Begründung und Empfehlung zum konkreten Programmieren.
Wenn es noch andere Gründe gibt, dann bin ich gerne lernwillig .
Der Verweis: Microsoft DAO 3.6 Object Library ist veraltet und sollte durch Microsoft Office XX.0 Access database engine Object Library ersetzt werden. Mit dem Setzen des Verweises ist Early-Binding möglich und sollte zumindest während der Programmentwicklung genutzt werden. Später läßt sich das immer noch auf Late-Binding umstellen.
Zu deiner Programmierung und zu dem dort verwendeten Programmierstil kann ich natürlich nichts sagen, aber eine "sauber" und korrekte Fehlerbehandlung ist natürlich für eine professionelle Anwendung unablässig, insbesondere sollte man darauf achten, daß Ressourcen immer so früh wie möglich und auch im Fehlerfall wieder freigegeben werden. Eine "goldene" Regel dabei ist, wenn man selber Ressourcen belegt hat, z.B. mit Open(), sollten diese auch mit Close() wieder freigegeben werden! Ein geklontes Recordset würde z.B. nicht darunter fallen, weil dieses durch eine Bibliotheksmethode erzeugt wird. Hier muß nur sichergestellt werden, daß die Referenz auf das geklonte Recordset wieder vor dem Beenden freigeben wird, z.B. indem der Referenz der Wert NOTHING zugewiesen wird. Wenn mit den Ressourcen und Referenzen in Verbindung mit COM-Objekten nicht korrekt umgegangen wird, wird das COM-System instabil und produziert Fehler, welche oft nur schwer zu finden oder nachvollziehbar sind.
Ich denke, da hast du noch eine Menge Programmierarbeit vor dir, um das wieder ins Lot zu bringen. Das ist genau das was ich meine, daß das Refactoring beim Programmieren immer gleich mit berücksichtigt werden muß. Offensichtlich war das bei der Anwendung nicht der Fall.
Tip: Während der Programmentwicklung Logausgaben zu machen, die später für den Rollout per Compilerdirektive auskommentiert werden. Das eignet sich für Fehlerausgaben oder zur Ausgabe von Programmzuständen, um den Fortschritt im Programmablauf mitverfolgen zu können. Hierfür kannst du z.B. das Modul aus dem Anhang verwenden, womit Debug-Ausgaben bzw. Meldungen auf der SystemLog-Schnittstelle von Windows ausgegeben werden können. Diese kann dann mit dem Tool DebugView von Sysinternals zur Laufzeit angeschaut und aufgezeichnet werden, ohne das Excel oder eine andere Anwendung beeinflusst wird.