Access Datenbank verknüpfte Tabellenpfade ermitteln

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von Volker Bunge.

    Access Datenbank verknüpfte Tabellenpfade ermitteln

    Hallo zusammen,

    für ein kleines Datensicherungsprogramm suche ich eine Möglichkeit, eine Accessdatenbank bzw. deren Tabellen darauf hin zu untersuchen, ob es Tabellen gibt, die in einer anderen Datenbank stehen und in der Frontenddatenbank also nur verknüpft sind.
    Mit diesen Pfaden würde ich dann auch diese mit sichern und somit alle benötigten Datenbanken sichern können. Wenn es möglich ist, dann wäre mir ACCDB und MDB Datenbanken sehr recht.

    Ich habe hierzu schon einiges probiert, aber leider nie den Verknüpfungspfad der jeweiligen Tabellen gefunden.

    Die Lösung soll am Ende alle verknüpften Datenbankpfade mir einmalig anzeigen (das bekomme ich aber selbst hin).

    Was ich aber leider überhaupt nicht hinbekomme, ist das richtige Auslesen der Datenbankpfade

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Sub AccessDB_Verknuepfte_Tabellen(dbPath As String)
    2. ' Pfad zur Access-Datenbank
    3. ' Dim dbPath As String = "C:\Pfad\zu\deiner\Datenbank.accdb"
    4. ' Verbindung zur Access-Datenbank herstellen
    5. ' Verbindung zur Access-Datenbank herstellen
    6. Using connection As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};")
    7. connection.Open()
    8. ' SQL-Abfrage für die MSysObjects-Tabelle
    9. Dim query As String = "SELECT Database, Name, Type FROM MSysObjects WHERE Type=6" ' Type=1 steht für Tabellen
    10. Using command As New OleDbCommand(query, connection)
    11. Using reader As OleDbDataReader = command.ExecuteReader()
    12. ' Durchlaufen der Datensätze
    13. While reader.Read()
    14. Dim tableName As String = reader("Name").ToString()
    15. Dim tableType As Integer = Convert.ToInt32(reader("Type"))
    16. Console.WriteLine($"Tabelle: {tableName}, Typ: {tableType}")
    17. End While
    18. End Using
    19. End Using
    20. connection.Close()
    21. End Using
    22. Console.ReadLine()
    23. '' Verbindung zur Access-Datenbank herstellen
    24. 'Using connection As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};")
    25. ' connection.Open()
    26. ' ' Abfrage für die MSysObjects-Tabelle, um verknüpfte Tabellen zu finden
    27. ' Dim query As String = "SELECT Name, Connect FROM MSysObjects WHERE Type=6"
    28. ' Using command As New OleDbCommand(query, connection)
    29. ' Using reader As OleDbDataReader = command.ExecuteReader
    30. ' While reader.Read()
    31. ' Dim linkedTableName As String = reader("Name").ToString()
    32. ' Dim linkedDatabasePath As String = reader("Database").ToString()
    33. ' Console.WriteLine($"Verknüpfte Tabelle: {linkedTableName}")
    34. ' Console.WriteLine($" Verknüpft mit: {linkedDatabasePath}")
    35. ' End While
    36. ' End Using
    37. ' End Using
    38. ' connection.Close()
    39. 'End Using
    40. 'Console.ReadLine()
    41. End Sub


    Leider bekomme ich bei beiden Methoden immer in der Zeile Using reader As OleDbDataReader = command.ExecuteReader die Fehlermeldung "System.Data.OleDb.OleDbException: "Record(s) cannot be read; no read permission on 'MSysObjects'.""

    Grundsätzlich ist mir der Fehler schon bewusst, da dies ja eine verdeckte Systemtabelle ist. Ich habe auch eine Möglichkeit gefunden, die vorschlägt, eine Abfrage auf diese Tabelle zu legen und diese Abfrage dann abzufragen. Eine insofern schöne Lösung, da man hier sofort auf die verknüpften Datenbankpfade gruppieren könnte und somit nicht im VB.Net Programm dies erledigen müsste. Nachteil: Ich müsste dann jedes Mal diese Abfrage erstellen. Also habe ich mal versucht, mit diesem Code das zu automatisieren

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Sub AccessDB_Verknuepfte_Tabellen(dbPath As String)
    2. ' Pfad zur Access-Datenbank
    3. ' Dim dbPath As String = "C:\Pfad\zu\deiner\Datenbank.accdb"
    4. ' Verbindung zur Access-Datenbank herstellen
    5. ' Verbindung zur Access-Datenbank herstellen
    6. Using connection As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={dbPath};")
    7. connection.Open()
    8. ' 1. Erstellen und Speichern einer Abfrage
    9. Dim createQuery As String = "CREATE VIEW GetTablePath AS SELECT MSysObjects.Database FROM MSysObjects WHERE (((MSysObjects.Type)=6)) GROUP BY MSysObjects.Database"
    10. Using command As New OleDbCommand(createQuery, connection)
    11. Try
    12. command.ExecuteNonQuery()
    13. Console.WriteLine("Abfrage erfolgreich erstellt.")
    14. Catch ex As Exception
    15. Console.WriteLine($"Fehler beim Erstellen der Abfrage: {ex.Message}")
    16. ' Fehler beim Erstellen der Abfrage: Record(s) cannot be read; no read permission on 'MSysObjects'.
    17. End Try
    18. End Using
    19. ' 2. Die Informationen holen
    20. ' SQL-Abfrage für die MSysObjects-Tabelle
    21. Dim query As String = "SELECT * FROM GetTablePath" ' Type=1 steht für Tabellen
    22. Using command As New OleDbCommand(query, connection)
    23. Using reader As OleDbDataReader = command.ExecuteReader() ' Fehler: Ein Ausnahmefehler des Typs "System.Data.OleDb.OleDbException" ist in System.Data.dll aufgetreten.
    24. ' Record(s) cannot be read; no read permission On 'MSysObjects'.
    25. ' Durchlaufen der Datensätze
    26. While reader.Read()
    27. Dim tableName As String = reader("Database").ToString()
    28. Console.WriteLine($"Tabelle: {tableName}")
    29. End While
    30. End Using
    31. End Using
    32. ' 3. Löschen der gespeicherten Abfrage
    33. Dim dropQuery As String = "DROP VIEW GetTablePath"
    34. Using command As New OleDbCommand(dropQuery, connection)
    35. Try
    36. command.ExecuteNonQuery()
    37. Console.WriteLine("Abfrage erfolgreich gelöscht.")
    38. Catch ex As Exception
    39. Console.WriteLine($"Fehler beim Löschen der Abfrage: {ex.Message}")
    40. End Try
    41. End Using
    42. connection.Close()
    43. End Using
    44. Console.ReadLine()
    45. end sub


    Leider klappt das auch nicht (siehe Fehlermeldungen im Code). Das Erstellen habe ich auch mal kurz deaktiviert und den zweiten Teil zu testen, aber auch hier eine Fehlermeldung. Den dritten Teil, das Löschen der Abfrage, habe ich noch nicht probiert.

    Jetzt mal eine Frage an Euch: Wie bekomme ich die/den Datenbankpfad einer Frontend bzw. Backend Datenbankanwendung ausgelesen?

    Über kreative Lösungen würde ich mich sehr freuen.

    Gruß

    Volker

    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Hallo zusammen,

    habe jetzt eine Methode gefunden, die so halb funktioniert. Hier erst einmal der Code

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports Microsoft.Office.Interop.Access
    2. Imports Microsoft.Office.Interop.Access.DaoSub
    3. Sub AccessDB_Verknuepfte_Tabellen(dbPath As String)
    4. Dim accessApp As New Application()
    5. 'Dim dbPath As String = "C:\Pfad\zu\deiner\Datenbank.accdb" ' Pfad zur Access-Datenbank
    6. Dim tableName As String = "Abgeschlossene_Jahre" ' Name der verknüpften Tabelle
    7. Try
    8. accessApp.OpenCurrentDatabase(dbPath)
    9. Dim tableDef As TableDef = accessApp.CurrentDb.TableDefs(tableName)
    10. If tableDef.Connect <> "" Then
    11. Dim linkedDbPath As String = tableDef.Connect.Replace(";", "").Trim()
    12. Console.WriteLine("Der Dateipfad der verknüpften Tabelle ist: " & linkedDbPath)
    13. Else
    14. Console.WriteLine("Die Tabelle ist nicht verknüpft.")
    15. End If
    16. Catch ex As Exception
    17. Console.WriteLine("Fehler: " & ex.Message)
    18. Finally
    19. accessApp.CloseCurrentDatabase()
    20. accessApp.Quit()
    21. End Try
    22. end sub


    Es gibt hier nur noch ein paar kleine Probleme

    1. Durch accessApp.OpenCurrentDatabase(dbPath) wird die Datenbank geöffnet. Das will ich aber nicht (zu mindestens nicht optisch. Wenn sie im Hintergrund unsichtbar geöffnet werden muss, ok)
    2. Erst wenn ich die DB schließe, geht es mit meinem Code weiter. Vorher passiert da gar nichts. Soll natürlich auch nicht sein.
    3. Ich bekomme tatsächlich für die angegebene Tabelle den korrekten Dateipfad auf die verknüpfte Datenbankdatei zurück geliefert. Aber ich möchte ja alle vorhandenen Tabellen untersuchen, ohne deren Tabellennamen zu wissen.
    4. Zum Schluss wird zwar die Datenbank noch einmal geschlossen, aber das Accessprogramm an sich bleibt immer aktiv. Auch ein manuelles Schließen über das X klappt nicht. Datei Schließen ist nicht aktiv. Erst über Taskmanager kann man Access schließen.

    Was auffällt ist, dass die DB ohne Accessgrundfenster gestartet wird. Meine Testdatenbank öffnet ein Dialogformular, welches somit eigentlich mittig ausgerichtet angezeigt wird. Im Hintergrund ist aber das Accessprogramm mit dem X gar nicht zu sehen.

    Wenn Ihr mir helfen könntet, dieses doch wohl nicht so einfache Problem zu lösen, wäre das echt genial.

    Schönen Feiertag und Wochenende noch allen.

    Volker
    Hallo zusammen,

    ich habe jetzt eine wesentlich bessere Möglichkeit gefunden.

    Ursprung: Eigenschaften einer Access Tabelle ermitteln Antwort: 20

    Mit dieser Lösung ein wenig rumgespielt und dann auf den entscheidenden Punkt ".Connect" gekommen.

    Hier mal meine leicht modifizierte Lösung

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports Microsoft.Office.Interop.Access
    2. Imports Microsoft.Office.Interop.Access.Dao
    3. Module AccessDB_untersuchen
    4. Sub AccessDB_Verknuepfte_Tabellen(databaseName As String)
    5. Dim tbl As Dao.TableDef
    6. Dim s As String
    7. ' Dim databaseName As String = "E:\Cal.mdb"
    8. Dim dbEngine As New Dao.DBEngine()
    9. Dim db As Dao.Database = dbEngine.OpenDatabase(databaseName)
    10. With Startfenster.DataGridView1
    11. .Columns.Add("Tabelle", "Tabelle")
    12. .Columns.Add("Connect", "Connect") ' Diese Zeile ist neu
    13. .Columns.Add("DateCreated", "DateCreated")
    14. .Columns.Add("LastUpdated", "LastUpdated")
    15. .Columns.Add("Recordcount", "Recordcount")
    16. End With
    17. For Each tbl In db.TableDefs
    18. s = TabellenAttribute(tbl)
    19. If s <> "" Then ' Die Zeile habe ich auch umgedreht, denn s = "" funktioniert nur bei nicht verknüpften Tabellen
    20. With tbl
    21. Startfenster.DataGridView1.Rows.Add(.Name, Replace(.Connect,";DATABASE=",""), .DateCreated, .LastUpdated, .RecordCount) ' Das Attribut .Connect ist neu und gibt den Dateipfad wieder. Der Replace habe ich eingefügt, damit am Ende nur der reine Dateipfad übrig bleibt. Kann man natürlich auch später irgendwo einbauen.
    22. End With
    23. End If
    24. Next
    25. End Sub
    26. Public Function TabellenAttribute(tbl As Dao.TableDef) As String
    27. Dim strResult As String = Nothing
    28. With tbl
    29. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbSystemObject) Then
    30. strResult = "SystemObject "
    31. End If
    32. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbHiddenObject) Then
    33. strResult = strResult + "HiddenObject "
    34. End If
    35. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachExclusive) Then
    36. strResult = strResult + "AttachExclusive "
    37. End If
    38. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachSavePWD) Then
    39. strResult = strResult + "AttachSavePWD "
    40. End If
    41. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachedTable) Then
    42. strResult = strResult + "AttachedTable "
    43. End If
    44. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachedODBC) Then
    45. strResult = strResult + "AttachedODBC "
    46. End If
    47. End With
    48. TabellenAttribute = strResult
    49. End Function


    Das Geniale hierbei: Es geht super schnell und ist noch für andere Infos brauchbar.

    Ich habe zwar den Code noch nicht so ganz verstanden (insbesondere die Funktion) aber das ist jetzt erst einmal zweitrangig. Ich werde jetzt das Ganze noch einmal so umschreiben, dass ich am Ende einen String bekomme, der mir alle verknüpften Datenbankpfade einmalig zurück liefert. Wenn der Code fertig ist, dann melde ich mich noch einmal.



    Hier nun noch die versprochene Endlösung (zu mindestens die, die ich haben wollte).

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports Microsoft.Office.Interop.Access
    2. Imports Microsoft.Office.Interop.Access.Dao
    3. Public Function AccessDB_Verknuepfte_Tabellen_Dateipfad(databaseName As String, Optional Trennzeichen As String = ";") As String
    4. Dim Tbl As Dao.TableDef
    5. Dim Attribute As String
    6. Dim VorhDateipfad As String = "" ' Hier stehen später alle Dateipfade drin
    7. ' Baut eine Verbindung zur Datenbank auf
    8. Dim dbEngine As New Dao.DBEngine()
    9. Dim db As Dao.Database = dbEngine.OpenDatabase(databaseName)
    10. ' Es werden jetzt alle Tabellen durchlaufen
    11. For Each Tbl In db.TableDefs
    12. ' und die Tabellenattribute ermittelt
    13. Attribute = TabellenAttribute(Tbl)
    14. ' Wenn es Attribute gibt, dann handelt es sich auch um verknüpfte Tabellen
    15. If Attribute <> "" Then
    16. ' Jetzt die dynamischen Ellemente bearbeiten
    17. With Tbl
    18. ' Da am Ende nur einmalig die verknüpften Dateipfade raus kommen sollen, wir hier geprüft, ob es den aktuellen Dateipfad schon gibt
    19. If InStr(VorhDateipfad, Replace(.Connect, ";DATABASE=", "")) = 0 Then
    20. ' Gibt es den aktuellen Pfad noch nicht, dann wird er dem Endergebnis incl. eines Trennzeichens hinzugefügt
    21. VorhDateipfad = VorhDateipfad & Replace(.Connect, ";DATABASE=", "") & Trennzeichen
    22. End If
    23. End With
    24. Else
    25. ' für Tabellen, die nicht verknüpft sind, kann man hier das gleiche evtl. durchführen
    26. End If
    27. Next
    28. ' Jetzt liegen alle Dateipfade evtl. vor.
    29. If VorhDateipfad <> "" Then
    30. ' Das ggf. letzte Trennzeichen mus/sollte daher noch gelöscht werden
    31. VorhDateipfad = VorhDateipfad.Substring(0, VorhDateipfad.Length - 1)
    32. End If
    33. Return VorhDateipfad
    34. End Function
    35. Public Function TabellenAttribute(tbl As Dao.TableDef) As String
    36. Dim strResult As String = Nothing
    37. With tbl
    38. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbSystemObject) Then
    39. strResult = "SystemObject "
    40. End If
    41. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbHiddenObject) Then
    42. strResult = strResult + "HiddenObject "
    43. End If
    44. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachExclusive) Then
    45. strResult = strResult + "AttachExclusive "
    46. End If
    47. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachSavePWD) Then
    48. strResult = strResult + "AttachSavePWD "
    49. End If
    50. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachedTable) Then
    51. strResult = strResult + "AttachedTable "
    52. End If
    53. If CBool(.Attributes And Dao.TableDefAttributeEnum.dbAttachedODBC) Then
    54. strResult = strResult + "AttachedODBC "
    55. End If
    56. End With
    57. TabellenAttribute = strResult
    58. End Function


    Viel Spaß

    Volker

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Volker Bunge“ ()

    Hi Volker,
    dein Ansatz(...FROM MSysObjects.....) in Post#1 war schon richtig, du musst aber eine Abfrage vorschalten und eine System.MDW verwenden um zugriff zu erahlten sonst kommt halt die Fehlermeldung


    "System.Data.OleDb.OleDbException: "Record(s) cannot be read; no read permission on 'MSysObjects'.""


    hier ein Bsp. mit SystemMDW

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Data.OleDb
    3. Public Class Form1
    4. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    5. If IO.File.Exists(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) & "\Microsoft\Access\System.MDW") Then
    6. DataGridView1.DataSource = GetLinkedTables()
    7. End If
    8. End Sub
    9. Public Function GetLinkedTables() As DataTable
    10. Dim con As New OleDb.OleDbConnectionStringBuilder With
    11. {
    12. .Provider = "Microsoft.ACE.OLEDB.12.0",
    13. .DataSource = "D:\DbCSV_Linked.accdb"
    14. }
    15. con.Add(
    16. "Jet OLEDB:System Database",
    17. System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) &
    18. "\Microsoft\Access\System.MDW"
    19. )
    20. Using cn As New OleDb.OleDbConnection With {.ConnectionString = con.ConnectionString}
    21. Using cmd As New OleDb.OleDbCommand With {.Connection = cn}
    22. cmd.CommandText = "GRANT SELECT ON TABLE MSysObjects TO PUBLIC"
    23. Dim dt As New DataTable
    24. cn.Open()
    25. cmd.ExecuteNonQuery()
    26. cmd.CommandText = "SELECT MSysObjects.Name As [Tabellenname], MSysObjects.Database As [Pfad] " & _
    27. "FROM MSysObjects " & _
    28. "GROUP BY MSysObjects.Name, MSysObjects.Database " & _
    29. "HAVING MSysObjects.Database Is Not Null;"
    30. dt.Load(cmd.ExecuteReader)
    31. Return dt
    32. End Using
    33. End Using
    34. End Function
    35. End Class


    hier noch ein Bild


    gutes gelingen mit deinem Projekt