Excel und RecordSet(SELECT ....) nur ReadOnly
#21
@derHöpp:
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:
  • Stefan1
Antworten Top
#22
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:
  • Stefan1
Antworten Top
#23
(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:
  • Stefan1
Antworten Top
#24
@ derHöpp

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.

VG
[-] Folgende(r) 3 Nutzer sagen Danke an knobbi38 für diesen Beitrag:
  • derHoepp, Stefan1, Stefan1
Antworten Top
#25
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:
  • derHoepp
Antworten Top
#26
(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:
  • Stefan1
Antworten Top
#27
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.

Gruß
Knobbi38
Antworten Top
#28
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  Dodgy.

Gruss Stefan1
Antworten Top
#29
Hallo Stefan,

Zitat:Was Du unter "Automatisierung" meinst, kenne ich nicht.

Der Begriff "Automatisierung" beschreibt eine Standardtechnologie in der Softwareentwicklung und entstammt aus dem COM-Objectmodell (Component Object Model). Hier wird kurz beschrieben, was damit gemeint ist:
https://learn.microsoft.com/de-de/office...automation
https://de.wikipedia.org/wiki/Component_Object_Model

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.

Grüße 
Ulrich


Angehängte Dateien
.zip   modWinDbg32.zip (Größe: 670 Bytes / Downloads: 2)
Antworten Top


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste