{"id":55000504,"date":"2026-04-01T00:00:00","date_gmt":"2026-03-10T13:53:06","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=504"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"SQL_ServerVerbindungen_per_Backstage_verwalten","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/SQL_ServerVerbindungen_per_Backstage_verwalten\/","title":{"rendered":"SQL Server-Verbindungen per Backstage verwalten"},"content":{"rendered":"<p><b>Wer in Access mit verkn&uuml;pften SQL Server-Tabellen arbeitet, kennt das Problem: Die Verbindungszeichenfolge muss korrekt zusammengesetzt sein, Treiber und Authentifizierungsart m&uuml;ssen stimmen &#8211; und bei jeder neuen Datenbank f&auml;ngt man von vorn an. Dieser Artikel zeigt, wie Du einen eigenen Tab im Backstage-Bereich von Access einrichtest, der Dir das Zusammenbauen der Verbindungszeichenfolge abnimmt. <\/b><\/p>\n<h2>Beispieldatenbank<\/h2>\n<p>Die Beispieldatenbank zum Artikel enth&auml;lt die Tabelle <b>USysRibbons<\/b> mit dem XML f&uuml;r den Backstage-Tab sowie das Modul <b>mdlBackstage<\/b> mit allen Callback-Prozeduren. Du kannst die Datenbank direkt &ouml;ffnen und den Backstage-Tab sofort verwenden &#8211; oder den Code als Vorlage f&uuml;r Deine eigene Datenbank nutzen.<\/p>\n<h2>Was der Backstage-Tab leistet<\/h2>\n<p>Der Tab <b>SQL Server-Verbindung<\/b> erscheint im Backstage-Bereich von Access &#8211; also in dem Bereich, der sich &ouml;ffnet, wenn Du auf <b>Datei<\/b> klickst (siehe Bild 1). Er enth&auml;lt auf der linken Seite alles, was zum manuellen Aufbau einer Verbindungszeichenfolge n&ouml;tig ist:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_504_001.png\" alt=\"Der Backstage-Tab SQL Server-Verbindung in Access\" width=\"599,6265\" height=\"542,82\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Der Backstage-Tab SQL Server-Verbindung in Access<\/span><\/b><\/p>\n<ul>\n<li><b>Treiber-Auswahl:<\/b> Ein Dropdown mit den drei g&auml;ngigsten ODBC-Treibern f&uuml;r SQL Server &#8211; <b>ODBC Driver 17 for SQL Server<\/b>, <b>ODBC Driver 18 for SQL Server<\/b> und <b>SQL Server Native Client 11.0<\/b>.<\/li>\n<li><b>Servername mit History:<\/b> Eine ComboBox, die sowohl freie Eingabe als auch die Auswahl aus zuvor verwendeten Servernamen erlaubt. Neue Servernamen werden automatisch gespeichert. Ein <b>L&ouml;schen<\/b>-Button entfernt den aktuell eingetragenen Namen aus der History.<\/li>\n<li><b>Datenbanken laden:<\/b> Ein Button verbindet sich mit dem eingetragenen Server und liest alle verf&uuml;gbaren Datenbanken aus. Das Ergebnis erscheint als Auswahlliste in der Datenbankname-ComboBox.<\/li>\n<li><b>Authentifizierung:<\/b> Eine <b>RadioGroup<\/b> schaltet zwischen Windows-Authentifizierung und SQL Server-Authentifizierung um. Bei SQL Server-Authentifizierung werden die Felder f&uuml;r Benutzername und Kennwort automatisch aktiviert.<\/li>\n<li><b>Verbindungszeichenfolge:<\/b> Das Ergebnis aller Eingaben wird in Echtzeit als fertige Verbindungszeichenfolge angezeigt. Zwei Buttons erm&ouml;glichen das Testen der Verbindung und das Kopieren in die Zwischenablage.<\/li>\n<\/ul>\n<h2>So verwendest Du den Backstage-Tab<\/h2>\n<p>Nach dem Einrichten des Tabs &#8211; wie das geht, beschreibt der n&auml;chste Abschnitt &#8211; &ouml;ffnest Du ihn &uuml;ber <b>Datei|SQL Server-Verbindung<\/b>. Beim ersten Aufruf sind alle Felder leer.<\/p>\n<ul>\n<li><b>Schritt 1 &#8211; Treiber w&auml;hlen:<\/b> W&auml;hle im Dropdown <b>ODBC-Treiber<\/b> den auf Deinem System installierten Treiber aus. Meist ist das <b>ODBC Driver 17 for SQL Server<\/b>. Der <b>ODBC Driver 18 <\/b>erfordert zus&auml;tzliche Parameter f&uuml;r die verschl&uuml;sselte Verbindung, die automatisch erg&auml;nzt werden. Falls Du noch den &auml;lteren <b>Native Client <\/b>im Einsatz hast, steht auch <b>SQL Server Native Client 11.0<\/b> zur Verf&uuml;gung (siehe Bild 2).<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_504_002.png\" alt=\"Treiber-Auswahl im Dropdown\" width=\"424,6267\" height=\"151,713\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Treiber-Auswahl im Dropdown<\/span><\/b><\/p>\n<li><b>Schritt 2 &#8211; Servername eingeben:<\/b> Trage den Servernamen in die ComboBox <b>Servername<\/b> ein. Das kann ein Rechnername, eine IP-Adresse oder ein benannter Instanzname im Format <b>Rechnername\\Instanzname<\/b> sein. Sobald Du den Namen best&auml;tigst, wird er automatisch in der History gespeichert und beim n&auml;chsten Mal in der Auswahlliste angeboten (siehe Bild 3). Nicht mehr ben&ouml;tigte Eintr&auml;ge entfernst Du &uuml;ber den <b>L&ouml;schen<\/b>-Button rechts neben der ComboBox &#8211; nach einer R&uuml;ckfrage wird der aktuell angezeigte Name aus der Liste entfernt.<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_504_003.png\" alt=\"Servername-ComboBox mit History-Eintr&auml;gen\" width=\"424,6267\" height=\"195,5637\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Servername-ComboBox mit History-Eintr&auml;gen<\/span><\/b><\/p>\n<li><b>Schritt 3 &#8211; Authentifizierung festlegen:<\/b> W&auml;hle zwischen <b>Windows-Authentifizierung<\/b> und <b>SQL Server-Authentifizierung<\/b>. Bei Windows-Authentifizierung werden die Felder f&uuml;r Benutzername und Kennwort deaktiviert &#8211; die Anmeldedaten des aktuellen Windows-Benutzers werden automatisch verwendet. Bei SQL Server-Authentifizierung werden beide Felder aktiv und m&uuml;ssen ausgef&uuml;llt werden (siehe Bild 4).<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_504_005.png\" alt=\"Authentifizierungsoptionen\" width=\"424,6267\" height=\"134,6377\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Authentifizierungsoptionen<\/span><\/b><\/p>\n<li><b>Schritt 4 &#8211; Datenbanken laden:<\/b> Klicke auf <b>Datenbanken mit aktuellen Zugangsdaten laden<\/b>. Das Add-In baut eine Verbindung zum Server auf &#8211; ohne Datenbankangabe &#8211; und liest alle verf&uuml;gbaren Datenbanken aus. Das kann je nach Netzwerk und Server einige Sekunden dauern. Die gefundenen Datenbanken erscheinen anschlie&szlig;end in der ComboBox <b>Datenbankname<\/b> (siehe Bild 5). Du kannst den Datenbanknamen alternativ auch direkt eingeben, falls Du ihn kennst.<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_504_006.png\" alt=\"Datenbankname-ComboBox nach dem Laden der verf&uuml;gbaren Datenbanken\" width=\"424,6267\" height=\"267,6995\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 5: Datenbankname-ComboBox nach dem Laden der verf&uuml;gbaren Datenbanken<\/span><\/b><\/p>\n<li><b>Schritt 5 &#8211; Verbindungszeichenfolge pr&uuml;fen und testen:<\/b> Die fertig zusammengesetzte Verbindungszeichenfolge erscheint im unteren Bereich der linken Spalte. Sie wird nach jeder &Auml;nderung automatisch aktualisiert und zeilenweise dargestellt, damit sie gut lesbar ist (siehe Bild 6). &Uuml;ber <b>Verbindung testen<\/b> pr&uuml;fst Du, ob die Verbindung tats&auml;chlich aufgebaut werden kann. Bei Erfolg erscheint eine kurze Best&auml;tigungsmeldung, bei einem Fehler die Fehlerbeschreibung von ADODB &#8211; das hilft beim schnellen Eingrenzen von Problemen wie falschem Kennwort, nicht erreichbarem Server oder fehlendem Treiber.<\/li>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_02\/pic_504_007.png\" alt=\"Fertige Verbindungszeichenfolge mit zeilenweiser Darstellung\" width=\"424,6267\" height=\"171,7172\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 6: Fertige Verbindungszeichenfolge mit zeilenweiser Darstellung<\/span><\/b><\/p>\n<\/ul>\n<p>Mit <b>In Zwischenablage kopieren<\/b> &uuml;bernimmst Du die Zeichenfolge in einem Schritt &#8211; ohne die Darstellung im Label manuell markieren zu m&uuml;ssen. Von dort aus kannst Du sie zum Beispiel direkt in den VBA-Editor einf&uuml;gen oder in eine andere Anwendung &uuml;bertragen.<\/p>\n<h2>Einrichtung in der Datenbank<\/h2>\n<p>Der Backstage-Tab wird &uuml;ber die Systemtabelle <b>USysRibbons<\/b> in der jeweiligen Access-Datenbank eingerichtet. Die Tabelle muss einmalig angelegt werden &#8211; die Beispieldatenbank zum Artikel enth&auml;lt sie bereits. Sie besteht aus drei Feldern: <b>ID<\/b> (Prim&auml;rschl&uuml;sselfeld mit Autowert), Ribbon <b>RibbonName<\/b> (Text) und <b>RibbonXml<\/b> (Memo). In <b>RibbonXml<\/b> steht das vollst&auml;ndige XML, das den Tab beschreibt. In den Access-Optionen unter <b>Aktuelle Datenbank<\/b> tr&auml;gst Du im Feld <b>Name des Men&uuml;bands <\/b>den Wert aus <b>RibbonName<\/b> ein. Nach dem n&auml;chsten &Ouml;ffnen der Datenbank ist der Tab aktiv.<\/p>\n<p>Die Callback-Prozeduren befinden sich im Modul <b>mdlBackstage<\/b>. Die Verbindungseinstellungen werden in der Windows-Registry unter dem Schl&uuml;ssel <b>HKCU\\Software\\VB and VBA Program Settings\\amvSQLServerConnection\\SQLServer<\/b> gespeichert. Das hat den Vorteil, dass die Einstellungen datenbank&uuml;bergreifend gelten &#8211; Du musst Servernamen und Zugangsdaten nicht in jeder Datenbank neu eingeben.<\/p>\n<p>Die Servernamen-History wird unter dem Registry-Wert <b>Servernamen<\/b> als pipe-separierte Liste gespeichert, zum Beispiel:<\/p>\n<pre>194.163.171.28|SQLSERVER01|LAPTOP\\SQLEXPRESS<\/pre>\n<h2>XML-Definition f&uuml;r den Backstage-Bereich<\/h2>\n<p>Das XML beginnt mit dem &uuml;blichen <b>customUI<\/b>-Wurzelelement, gefolgt von <b><backstage><\/b> und einem <b><tab><\/b>-Element mit einer eigenen ID. Das Attribut <b>insertAfterMso=&#8221;TabInfo&#8221;<\/b> sorgt daf&uuml;r, dass der Tab hinter dem <b>Informationen<\/b>-Tab erscheint. Das Attribut <b>title<\/b> legt die &Uuml;berschrift fest, die im Backstage &uuml;ber dem Inhalt angezeigt wird:<\/p>\n<pre>&lt;customUI xmlns=\"http:\/\/schemas.microsoft.com\/office\/2009\/07\/customui\"\r\n    onLoad=\"customUI_OnLoad\"&gt;\r\n  &lt;backstage&gt;\r\n    &lt;tab id=\"tabSQLServer\"\r\n         label=\"SQL Server-Verbindung\"\r\n         insertAfterMso=\"TabInfo\"\r\n         title=\"SQL Server-Verbindungszeichenfolgen\"&gt;\r\n      &lt;firstColumn&gt;\r\n        ...\r\n      &lt;\/firstColumn&gt;\r\n    &lt;\/tab&gt;\r\n  &lt;\/backstage&gt;\r\n&lt;\/customUI&gt;<\/pre>\n<p>Das Attribut <b>onLoad<\/b> verweist auf eine Callback-Funktion, die beim Laden des Ribbons aufgerufen wird und eine Referenz auf das <b>IRibbonUI<\/b>-Objekt liefert. Diese Referenz wird in einer Modulvariablen gespeichert und sp&auml;ter ben&ouml;tigt, um das Backstage nach &Auml;nderungen neu zu zeichnen:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_ribbon<span style=\"color:blue;\"> As <\/span>IRibbonUI\r\n<span style=\"color:blue;\">Public Function <\/span>customUI_OnLoad(ribbon<span style=\"color:blue;\"> As <\/span>IRibbonUI) _\r\n       <span style=\"color:blue;\"> As <\/span>IRibbonUI\r\n    <span style=\"color:blue;\">Set<\/span> m_ribbon = ribbon\r\n    <span style=\"color:blue;\">Call<\/span> ServernamenLaden\r\n    <span style=\"color:blue;\">Call<\/span> DatenbanknamenLeeren\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Alle Callback-Prozeduren kommen in ein Standardmodul namens <b>mdlBackstage<\/b>. F&uuml;r die Registry-Speicherung verwendet die L&ouml;sung zwei Konstanten:<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>Const cStrAppName<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"amvSQLServerConnection\"\r\n<span style=\"color:blue;\">Public <\/span>Const cStrSection<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"SQLServer\"<\/pre>\n<p>Die Registry eignet sich hier gut als Speicherort, weil die Verbindungsdaten datenbank&uuml;bergreifend verf&uuml;gbar sein sollen &#8211; einmal konfiguriert, stehen sie in jeder Datenbank zur Verf&uuml;gung, die diesen Backstage-Tab verwendet.<\/p>\n<p>Klassen wie <b>IRibbonUI <\/b>sind in der Bibliothek <b>Microsoft Office 16.0 Object Library <\/b>definiert, daher m&uuml;ssen wir noch einen Verweis auf diese Bibliothek zu den Verweisen des VBA-Projekts hinzuf&uuml;gen.<\/p>\n<h2>Steuerelemente im Backstage-Tab<\/h2>\n<p>Der Tab verwendet eine einzelne Spalte (<b>firstColumn<\/b>), die in mehrere Gruppen unterteilt ist. Jede Gruppe enth&auml;lt ein <b>topItems<\/b>-Element, das die eigentlichen Steuerelemente aufnimmt. Im Backstage stehen nicht alle Steuerelemente des Ribbons zur Verf&uuml;gung &#8211; die wichtigsten f&uuml;r diese L&ouml;sung sind <b>dropDown<\/b>, <b>comboBox<\/b>, <b>editBox<\/b>, <b>radioGroup<\/b>, <b>labelControl<\/b>, <b>button<\/b> und <b>layoutContainer<\/b>.<\/p>\n<p>Ein wesentlicher Unterschied zum Ribbon: Backstage-Callbacks, die einen Wert zur&uuml;ckliefern, verwenden in VBA <b>ByRef<\/b>-Parameter statt Funktionsr&uuml;ckgabewerten. Die Signatur f&uuml;r einen Text-Callback sieht zum Beispiel so aus:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ebServername_GetText(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, _\r\n        ByRef text)\r\n    text = GetSetting(cStrAppName, _\r\n        cStrSection, \"Servername\", \"\")\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Falsche Signaturen f&uuml;hren zu stillen Fehlern &#8211; das Steuerelement zeigt einfach nichts an, ohne eine Fehlermeldung zu erzeugen.<\/p>\n<h2>Treiber-Auswahl<\/h2>\n<p>Die erste Gruppe enth&auml;lt ein <b>dropDown<\/b>-Element f&uuml;r die Auswahl des ODBC-Treibers. Die drei verf&uuml;gbaren Treiber sind im Code fest hinterlegt: <b>ODBC Driver 17 for SQL Server<\/b>, <b>ODBC Driver 18 for SQL Server <\/b>und <b>SQL Server Native Client 11.0<\/b>. Das XML f&uuml;r das Dropdown sieht so aus:<\/p>\n<pre>&lt;group id=\"grpTreiber\" label=\"Treiber\"&gt;\r\n  &lt;topItems&gt;\r\n    &lt;dropDown id=\"ddTreiber\" label=\"ODBC-Treiber\"\r\n              getItemCount=\"ddTreiber_GetItemCount\"\r\n              getItemLabel=\"ddTreiber_GetItemLabel\"\r\n              getItemID=\"ddTreiber_GetItemID\"\r\n              getSelectedItemIndex=_\r\n                  \"ddTreiber_GetSelectedItemIndex\"\r\n              onAction=\"ddTreiber_OnAction\"\/&gt;\r\n  &lt;\/topItems&gt;\r\n&lt;\/group&gt;<\/pre>\n<p>Die Callbacks f&uuml;r ein <b>dropDown<\/b> im Backstage folgen einem einheitlichen Muster. <b>GetItemCount<\/b> liefert die Anzahl der Eintr&auml;ge, <b>GetItemLabel<\/b> den anzuzeigenden Text f&uuml;r jeden Index, <b>GetItemID<\/b> eine eindeutige Zeichenkette je Eintrag und <b>GetSelectedItemIndex<\/b> den aktuell gew&auml;hlten Index. <b>OnAction<\/b> wird aufgerufen, wenn der Benutzer einen Eintrag ausw&auml;hlt (siehe Listing 1).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ddTreiber_GetItemCount(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef count)\r\n    count = 3\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ddTreiber_GetItemLabel(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>, ByRef label)\r\n    label = GetTreiberName(index)\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ddTreiber_GetItemID(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>, ByRef id)\r\n    id = \"treiber\" & index\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ddTreiber_GetSelectedItemIndex(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef selectedIndex)\r\n    selectedIndex = Val(GetSetting(cStrAppName, cStrSection, \"TreiberIndex\", \"0\"))\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ddTreiber_OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, selectedID<span style=\"color:blue;\"> As String<\/span>, selectedIndex<span style=\"color:blue;\"> As Integer<\/span>)\r\n    SaveSetting cStrAppName, cStrSection, \"TreiberIndex\", selectedIndex\r\n    <span style=\"color:blue;\">Call<\/span> VerbindungszeichenfolgeAktualisieren\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Callbacks f&uuml;r das Treiber-Dropdown<\/span><\/b><\/p>\n<p>Die Hilfsfunktion <b>GetTreiberName<\/b> liefert den Treibernamen f&uuml;r einen gegebenen Index und wird sp&auml;ter auch beim Zusammenbauen der Verbindungszeichenfolge verwendet:<\/p>\n<pre><span style=\"color:blue;\">Private Function <\/span>GetTreiberName(index<span style=\"color:blue;\"> As Integer<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    Select Case index\r\n        <span style=\"color:blue;\">Case <\/span>0\r\n            GetTreiberName = \"ODBC Driver 17 for SQL Server\"\r\n        <span style=\"color:blue;\">Case <\/span>1\r\n            GetTreiberName = \"ODBC Driver 18 for SQL Server\"\r\n        <span style=\"color:blue;\">Case <\/span>2\r\n            GetTreiberName = \"SQL Server Native Client 11.0\"\r\n        <span style=\"color:blue;\">Case Else<\/span>\r\n            GetTreiberName = \"ODBC Driver 17 for SQL Server\"\r\n    <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Um die Funktion <b>VerbindungszeichenfolgeAktualisieren <\/b>k&uuml;mmern wir uns weiter unten.<\/p>\n<h2>Servername mit History<\/h2>\n<p>F&uuml;r den Servernamen kommt eine <b>comboBox<\/b> zum Einsatz &#8211; sie erlaubt sowohl freie Eingabe als auch die Auswahl aus einer Liste zuvor verwendeter Servernamen. Die verwendeten Namen werden pipe-separiert in der Registry gespeichert. Daneben gibt es einen <b>L&ouml;schen<\/b>-Button, mit dem ein Servername aus der History entfernt werden kann. Beide Steuerelemente werden in einem <b>layoutContainer<\/b> nebeneinander angeordnet:<\/p>\n<pre>&lt;group id=\"grpServer\" label=\"Verbindung\"&gt;\r\n  &lt;topItems&gt;\r\n    &lt;layoutContainer id=\"lcServername\"\r\n                    layoutChildren=\"horizontal\"&gt;\r\n      &lt;comboBox id=\"cbServername\"\r\n                label=\"Servername\"\r\n                sizeString=\"WWWWWWWWWWWWWWWWWWWW\"\r\n                getText=\"cbServername_GetText\"\r\n                onChange=\"cbServername_OnChange\"\r\n                getItemCount=\"cbServername_GetItemCount\"\r\n                getItemLabel=\"cbServername_GetItemLabel\"\r\n                getItemID=\"cbServername_GetItemID\"\/&gt;\r\n      &lt;button id=\"btnServernameLoeschen\"\r\n              label=\"L&ouml;schen\"\r\n              imageMso=\"DeleteTable\"\r\n              onAction=_\r\n                  \"btnServernameLoeschen_OnAction\"\/&gt;\r\n    &lt;\/layoutContainer&gt;\r\n    ...\r\n  &lt;\/topItems&gt;\r\n&lt;\/group&gt;<\/pre>\n<p>Die <b>comboBox<\/b> kennt dieselben Item-Callbacks wie das <b>dropDown<\/b>, zus&auml;tzlich aber <b>getText<\/b> und <b>onChange<\/b> f&uuml;r den frei eingebbaren Text. <b>onChange<\/b> wird sowohl bei freier Eingabe als auch bei Auswahl aus der Liste aufgerufen. Der neue Servername wird dabei automatisch in der History gespeichert, sofern er noch nicht vorhanden ist.<\/p>\n<p>Bevor wir uns die Callbacks anschauen, werfen wir einen Blick auf die Prozeduren, mit denen wir die Servernamen laden und einen neu eingegebenen Servernamen speichern. Diese finden wir in Listing 2.<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_astrServernamen()<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_intServernamenAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>ServernamenLaden()\r\n    <span style=\"color:blue;\">Dim <\/span>strListe<span style=\"color:blue;\"> As String<\/span>\r\n    strListe = GetSetting(cStrAppName, cStrSection, \"Servernamen\", \"\")\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strListe) = 0<span style=\"color:blue;\"> Then<\/span>\r\n        m_intServernamenAnzahl = 0\r\n        ReDim m_astrServernamen(0)\r\n        <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    m_astrServernamen = <span style=\"color:blue;\">Split<\/span>(strListe, \"|\")\r\n    m_intServernamenAnzahl = <span style=\"color:blue;\">UBound<\/span>(m_astrServernamen) + 1\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>ServernameHinzufuegen(strName<span style=\"color:blue;\"> As String<\/span>)\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strName) = 0<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">Call<\/span> ServernamenLaden\r\n    For i = 0 To m_intServernamenAnzahl - 1\r\n        <span style=\"color:blue;\">If <\/span>LCase(m_astrServernamen(i)) = LCase(strName)<span style=\"color:blue;\"> Then<\/span>\r\n            <span style=\"color:blue;\">Exit Sub<\/span>\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    <span style=\"color:blue;\">If <\/span>m_intServernamenAnzahl = 0<span style=\"color:blue;\"> Then<\/span>\r\n        ReDim m_astrServernamen(0)\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        ReDim Preserve m_astrServernamen(m_intServernamenAnzahl)\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    m_astrServernamen(m_intServernamenAnzahl) = strName\r\n    m_intServernamenAnzahl = m_intServernamenAnzahl + 1\r\n    SaveSetting cStrAppName, cStrSection, \"Servernamen\", Join(m_astrServernamen, \"|\")\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: History der Servernamen laden und erg&auml;nzen<\/span><\/b><\/p>\n<p>Hier verwenden wir das Array <b>m_astrServernamen<\/b> zum Speichern der Servernamen und die Variable <b>m_intServernamenAnzahl <\/b>zum Speichern der Anzahl der enthaltenen Eintr&auml;ge.<\/p>\n<p>Die Prozedur <b>ServernamenLaden <\/b>holt mit <b>GetSetting <\/b>die aktuelle Liste der Servernamen aus der Registry und pr&uuml;ft, ob diese leer ist. In diesem Fall werden die Variablen auf die Anzahl <b>0 <\/b>eingestellt beziehungsweise das Array geleert. Anderenfalls lesen wir die Liste mit der <b>Split<\/b>-Funktion als Array in <b>m_astrServernamen <\/b>ein und ermitteln die Anzahl mit der <b>UBound<\/b>-Funktion.<\/p>\n<p>Die Prozedur <b>ServernameHinzufuegen <\/b>h&auml;ngt den als Parameter angegebenen Servernamen an die Liste der Servernamen an. Dazu l&auml;dt sie die Liste und durchl&auml;uft alle Eintr&auml;ge. Ist der Eintrag bereits vorhanden, wird er nicht erneut angelegt. Anderenfalls wird er hinten an die Liste angeh&auml;ngt. Danach werden die beiden Variablen <b>m_astrServernamen <\/b>und <b>m_intServernamenAnzahl <\/b>aktualisiert und die Liste der Servernamen wieder in der Registry gespeichert.<\/p>\n<h2>Callback-Funktionen f&uuml;r die Server-ComboBox<\/h2>\n<p>Nun folgen die <b>ComboBox<\/b>-Callbacks, die auf das Array zur&uuml;ckgreifen (siehe Listing 3):<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>cbServername_GetText(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef text)\r\n    text = GetSetting(cStrAppName, cStrSection, \"Servername\", \"\")\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbServername_OnChange(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, text<span style=\"color:blue;\"> As String<\/span>)\r\n    SaveSetting cStrAppName, cStrSection, \"Servername\", text\r\n    <span style=\"color:blue;\">Call<\/span> ServernameHinzufuegen(text)\r\n    <span style=\"color:blue;\">Call<\/span> DatenbanknamenLeeren\r\n    <span style=\"color:blue;\">Call<\/span> VerbindungszeichenfolgeAktualisieren\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbServername_GetItemCount(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef count)\r\n    <span style=\"color:blue;\">Call<\/span> ServernamenLaden\r\n    count = m_intServernamenAnzahl\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbServername_GetItemLabel(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>, ByRef label)\r\n    label = m_astrServernamen(index)\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbServername_GetItemID(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>, ByRef id)\r\n    id = \"server\" & index\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Callback-Funktionen f&uuml;r die ComboBox zur Auswahl des Servernamens<\/span><\/b><\/p>\n<ul>\n<li><b>cbServername_GetText <\/b>liest den Text f&uuml;r den aktuellen Eintrag direkt aus der Registry-Einstellung Servername ein.<\/li>\n<li><b>cbServername_OnChange <\/b>wird ausgel&ouml;st, wenn der Benutzer einen anderen Eintrag ausw&auml;hlt. Dies speichert den gew&auml;hlten Eintrag f&uuml;r die Einstellung Servername in der Registry. Au&szlig;erdem werden die drei Prozeduren <b>ServernameHinzufuegen<\/b>, <b>DatenbanknamenLeeren <\/b>und <b>VerbindungszeichenfolgeAktualisieren <\/b>aufgerufen, damit die &uuml;brigen Elemente aktualisiert werden &#8211; gefolgt vom Aufruf der <b>Invalidate<\/b>-Methode, um die Aktualisierungen im Backstage-Bereich sichtbar zu machen.<\/li>\n<li><b>cbServername_GetItemCount <\/b>ruft die Prozedur <b>ServernamenLaden <\/b>auf, was die aktuellen Servernamen aus der Registry einliest und die Anzahl der Eintr&auml;ge f&uuml;r das <b>ComboBox<\/b>-Element zur&uuml;ckgibt.<\/li>\n<li><b>cbServername_GetItemLabel <\/b>wird f&uuml;r jeden Eintrag einmal aufgerufen und tr&auml;gt den Text f&uuml;r den jeweiligen Server ein.<\/li>\n<li><b>cbServername_GetItemID <\/b>holt den passenden Index f&uuml;r die Eintr&auml;ge.<\/li>\n<\/ul>\n<h2>L&ouml;schen-Button f&uuml;r den Servernamen<\/h2>\n<p>Der <b>L&ouml;schen<\/b>-Button fragt vor dem Entfernen nach und schreibt die bereinigte Liste zur&uuml;ck in die Registry. Dazu verwenden wir die <b>OnAction<\/b>-Prozedur aus Listing 4. Er holt den aktuellen Namen aus der Registry. Wenn dieser nicht leer ist, folgt die R&uuml;ckfrage, ob der Eintrag gel&ouml;scht werden soll. Falls ja, werden alle Eintr&auml;ge geladen und nur die Eintr&auml;ge, die nicht mit dem zu l&ouml;schenden Eintrag &uuml;bereinstimmen, werden zum Array <b>asstrNeu <\/b>hinzugef&uuml;gt. Ist die Liste leer, wird der Eintrag <b>Servernamen <\/b>in der Registry auf eine leere Zeichenkette eingestellt, anderenfalls wird aus dem Array mit der Join-Funktion eine durch das Pipe-Zeichen getrennte Liste der Servernamen in der Registry gespeichert und die Anzeige aktualisiert.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>btnServernameLoeschen_OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n    <span style=\"color:blue;\">Dim <\/span>strName<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>astrNeu()<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>intNeu<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n    strName = GetSetting(cStrAppName, cStrSection, \"Servername\", \"\")\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strName) = 0<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">MsgBox<\/span> \"Kein Servername eingetragen.\", vbExclamation\r\n        <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">MsgBox<\/span>(\"Servername ''\" & strName & \"'' aus der Liste l&ouml;schen?\", vbQuestion + vbYesNo) = vbNo<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Call<\/span> ServernamenLaden\r\n    ReDim astrNeu(m_intServernamenAnzahl)\r\n    For i = 0 To m_intServernamenAnzahl - 1\r\n        <span style=\"color:blue;\">If <\/span>LCase(m_astrServernamen(i)) &lt;&gt; LCase(strName)<span style=\"color:blue;\"> Then<\/span>\r\n            astrNeu(intNeu) = m_astrServernamen(i)\r\n            intNeu = intNeu + 1\r\n        <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    <span style=\"color:blue;\">If <\/span>intNeu = 0<span style=\"color:blue;\"> Then<\/span>\r\n        SaveSetting cStrAppName, cStrSection, \"Servernamen\", \"\"\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        ReDim Preserve astrNeu(intNeu - 1)\r\n        SaveSetting cStrAppName, cStrSection, \"Servernamen\", Join(astrNeu, \"|\")\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">Call<\/span> ServernamenLaden\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: L&ouml;schen-Button f&uuml;r die Servername-History<\/span><\/b><\/p>\n<h2>Datenbanken laden<\/h2>\n<p>Nach der Eingabe des Servernamens und der Authentifizierungsdaten kann die Liste der verf&uuml;gbaren Datenbanken vom Server abgerufen werden. Da dieser Vorgang eine Netzwerkverbindung aufbaut und je nach Server mehrere Sekunden dauern kann, erfolgt er nicht automatisch, sondern erst auf expliziten Klick auf den Button &#8220;Datenbanken mit aktuellen Zugangsdaten laden&#8221;. Die Ergebnisse werden in einer <b>comboBox<\/b> f&uuml;r den Datenbanknamen angezeigt:<\/p>\n<pre>&lt;button id=\"btnDatenbankenLaden\"   label=\"Datenbanken mit aktuellen Zugangsdaten laden\"   imageMso=\"RefreshAll\"  onAction=\"btnDatenbankenLaden_OnAction\"\/&gt;\r\n&lt;comboBox id=\"cbDatenbankname\" label=\"Datenbankname\"\r\n  sizeString=\"WWWWWWWWWWWWWWWWWWWW\"  getText=\"cbDatenbankname_GetText\"\r\n  onChange=\"cbDatenbankname_OnChange\"\r\n  getItemCount=\"cbDatenbankname_GetItemCount\"\r\n  getItemLabel=\"cbDatenbankname_GetItemLabel\"\r\n  getItemID=\"cbDatenbankname_GetItemID\"\/&gt;<\/pre>\n<p>F&uuml;r die Datenbanknamen werden zwei Modulvariablen ben&ouml;tigt &#8211; ein Array f&uuml;r die Namen und ein Z&auml;hler. Die Prozedur <b>DatenbanknamenLeeren<\/b> setzt beide zur&uuml;ck und wird aufgerufen, sobald der Servername ge&auml;ndert wird &#8211; die Liste w&auml;re f&uuml;r den neuen Server nicht mehr g&uuml;ltig:<\/p>\n<pre><span style=\"color:blue;\">Private <\/span>m_astrDatenbanknamen()<span style=\"color:blue;\"> As String<\/span>\r\n<span style=\"color:blue;\">Private <\/span>m_intDatenbanknamenAnzahl<span style=\"color:blue;\"> As Integer<\/span>\r\n<span style=\"color:blue;\">Private Sub <\/span>DatenbanknamenLeeren()\r\n    m_intDatenbanknamenAnzahl = 0\r\n    ReDim m_astrDatenbanknamen(0)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der Callback <b>btnDatenbankenLaden_OnAction<\/b> baut eine Verbindungszeichenfolge ohne Datenbankangabe auf &#8211; nur Treiber, Server und Authentifizierung &#8211; und fragt per ADODB die Systemansicht <b>sys.databases<\/b> ab. Jede gefundene Datenbank landet im Array (siehe Listing 5).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>btnDatenbankenLaden_OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n    <span style=\"color:blue;\">Dim <\/span>cn<span style=\"color:blue;\"> As Object<\/span>, rs<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strServer<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strConnect<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>intAuth<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>intTreiber<span style=\"color:blue;\"> As Integer<\/span>\r\n    strServer = GetSetting(cStrAppName, cStrSection, \"Servername\", \"\")\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strServer) = 0<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">MsgBox<\/span> \"Bitte zuerst einen Servernamen\" & \" eingeben.\", vbExclamation\r\n        <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    intAuth = Val(GetSetting(cStrAppName, cStrSection, \"Authentifizierung\", \"0\"))\r\n    intTreiber = Val(GetSetting(cStrAppName, cStrSection, \"TreiberIndex\", \"0\"))\r\n    strConnect = \"Driver={\" & GetTreiberName(intTreiber) & \"};Server=\" & strServer & \";\"\r\n    <span style=\"color:blue;\">If <\/span>intAuth = 1<span style=\"color:blue;\"> Then<\/span>\r\n        strConnect = strConnect & \"UID=\" & GetSetting(cStrAppName, cStrSection, \"Benutzername\", \"\") & \";PWD=\" _\r\n            & GetSetting(cStrAppName, cStrSection, \"Kennwort\", \"\") & \";\"\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        strConnect = strConnect & \"Trusted_Connection=Yes;\"\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">If <\/span>intTreiber = 1<span style=\"color:blue;\"> Then<\/span>\r\n        strConnect = strConnect & \"Encrypt=yes;\" & \"TrustServerCertificate=yes;\"\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n    <span style=\"color:blue;\">Set<\/span> cn = CreateObject(\"ADODB.Connection\")\r\n    cn.Open strConnect\r\n    <span style=\"color:blue;\">Set<\/span> rs = cn.Execute(\"SELECT name FROM sys.databases ORDER BY name\")\r\n    <span style=\"color:blue;\">Call<\/span> DatenbanknamenLeeren\r\n    <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rs.EOF\r\n        <span style=\"color:blue;\">If <\/span>m_intDatenbanknamenAnzahl = 0<span style=\"color:blue;\"> Then<\/span>\r\n            ReDim m_astrDatenbanknamen(0)\r\n        <span style=\"color:blue;\">Else<\/span>\r\n            ReDim Preserve m_astrDatenbanknamen(m_intDatenbanknamenAnzahl)\r\n        <span style=\"color:blue;\">End If<\/span>\r\n        m_astrDatenbanknamen(m_intDatenbanknamenAnzahl) = rs.Fields(0).Value\r\n        m_intDatenbanknamenAnzahl = m_intDatenbanknamenAnzahl + 1\r\n        rs.Move<span style=\"color:blue;\">Next<\/span>\r\n    <span style=\"color:blue;\">Loop<\/span>\r\n    rs.Close\r\n    cn.Close\r\n    <span style=\"color:blue;\">Set<\/span> rs = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> cn = Nothing\r\n    m_ribbon.Invalidate\r\n    <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n    <span style=\"color:blue;\">MsgBox<\/span> \"Fehler beim Laden der Datenbanken:\" & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Description, <span style=\"color:blue;\">vbCr<\/span>itical\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Datenbanknamen vom Server laden<\/span><\/b><\/p>\n<p>Dazu holt sie zuerst den Servernamen, die Authentizifierungsmethode und den Index des aktuell selektierten Treibers aus der Registry und stellt damit in <b>strConnect <\/b>die Verbindungszeichenfolge zusammen &#8211; bei SQL Server-Authentifizierung mit Benutzer und Kennwort und bei Windows-Authentifizierung mit <b>Trusted_Connection=Yes<\/b>. Falls wir den <b>ODBC Driver 18 for SQL Server <\/b>nutzen, h&auml;ngen wir noch die Paraemter <b>Encrypt <\/b>und <b>TrustServerCertificate <\/b>jeweils mit dem Wert Yes an die Verbindungszeichenfolge an.<\/p>\n<p>Dann baut die Prozedur mit einem <b>Connection<\/b>-Objekt eine Verbindung zur Tabelle <b>sys.databases <\/b>auf und ermittelt alle enthaltenen Eintr&auml;ge in einem Recordset. Dieses durchl&auml;uft sie anschlie&szlig;end in einer <b>Do While<\/b>-Schleife und f&uuml;llt das Array <b>m_intDatenbanknamen <\/b>mit den Datenbanknamen.<\/p>\n<p>Nach <b>m_ribbon.Invalidate<\/b> fragt Access alle Callbacks neu ab &#8211; die ComboBox zeigt dann die geladenen Datenbanken an. Die f&uuml;nf Callbacks der Datenbankname-ComboBox sind analog zu denen der Servername-ComboBox aufgebaut (siehe Listing 6).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>cbDatenbankname_GetText(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef text)\r\n    text = GetSetting(cStrAppName, cStrSection, \"Datenbankname\", \"\")\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbDatenbankname_OnChange(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, text<span style=\"color:blue;\"> As String<\/span>)\r\n    SaveSetting cStrAppName, cStrSection, \"Datenbankname\", text\r\n    <span style=\"color:blue;\">Call<\/span> VerbindungszeichenfolgeAktualisieren\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbDatenbankname_GetItemCount(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef count)\r\n    count = m_intDatenbanknamenAnzahl\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbDatenbankname_GetItemLabel(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>, ByRef label)\r\n    label = m_astrDatenbanknamen(index)\r\n    <span style=\"color:blue;\">Debug.Print<\/span> label\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>cbDatenbankname_GetItemID(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, index<span style=\"color:blue;\"> As Integer<\/span>, ByRef id)\r\n    id = \"datenbank\" & index\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Callbacks f&uuml;r die Datenbankname-ComboBox<\/span><\/b><\/p>\n<h2>Authentifizierung<\/h2>\n<p>SQL Server unterst&uuml;tzt zwei Authentifizierungsarten: Windows-Authentifizierung, bei der die Anmeldedaten des aktuellen Windows-Benutzers verwendet werden, und SQL Server-Authentifizierung mit eigenem Benutzernamen und Kennwort. Eine <b>radioGroup<\/b> mit zwei <b>radioButton<\/b>-Elementen steuert die Auswahl. Die Eingabefelder f&uuml;r Benutzername und Kennwort werden &uuml;ber <b>getEnabled<\/b>-Callbacks dynamisch aktiviert und deaktiviert:<\/p>\n<pre>&lt;group id=\"grpAuthentifizierung\"\r\n        label=\"Authentifizierung\"&gt;\r\n  &lt;topItems&gt;\r\n    &lt;radioGroup id=\"rgAuthentifizierung\"\r\n        getSelectedItemIndex=\r\n          \"rgAuthentifizierung_GetSelectedItemIndex\"\r\n        onAction=\"rgAuthentifizierung_OnAction\"&gt;\r\n      &lt;radioButton id=\"rbWindows\"\r\n        label=\"Windows-Authentifizierung\"\/&gt;\r\n      &lt;radioButton id=\"rbSQL\"\r\n        label=\"SQL Server-Authentifizierung\"\/&gt;\r\n    &lt;\/radioGroup&gt;\r\n    &lt;editBox id=\"ebBenutzername\"\r\n      label=\"Benutzername\"\r\n      sizeString=\"WWWWWWWWWWWWWWWWWWWW\"\r\n      getText=\"ebBenutzername_GetText\"\r\n      onChange=\"ebBenutzername_OnChange\"\r\n      getEnabled=\"ebBenutzername_GetEnabled\"\/&gt;\r\n    &lt;editBox id=\"ebKennwort\"\r\n      label=\"Kennwort\"\r\n      sizeString=\"WWWWWWWWWWWWWWWWWWWW\"\r\n      getText=\"ebKennwort_GetText\"\r\n      onChange=\"ebKennwort_OnChange\"\r\n      getEnabled=\"ebKennwort_GetEnabled\"\/&gt;\r\n  &lt;\/topItems&gt;\r\n&lt;\/group&gt;<\/pre>\n<p>Ein wichtiges Detail bei der <b>radioGroup<\/b>: Der <b>onAction<\/b>-Callback erwartet den Index als <b>Long<\/b>-Parameter &#8211; nicht als <b>Integer<\/b>. Eine falsche Deklaration f&uuml;hrt zu einem stillen Fehler, der schwer zu diagnostizieren ist. Die beiden Callbacks f&uuml;r die RadioGroup sehen wir in Listing 7.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>rgAuthentifizierung_GetSelectedItemIndex(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef selectedIndex)\r\n    selectedIndex = Val(GetSetting(cStrAppName, cStrSection, \"Authentifizierung\", \"0\"))\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>rgAuthentifizierung_OnAction(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, selectedID<span style=\"color:blue;\"> As String<\/span>, selectedIndex<span style=\"color:blue;\"> As Long<\/span>)\r\n    SaveSetting cStrAppName, cStrSection, \"Authentifizierung\", selectedIndex\r\n    <span style=\"color:blue;\">Call<\/span> VerbindungszeichenfolgeAktualisieren\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Callbacks f&uuml;r die RadioGroup f&uuml;r die Authentifizierungsmethode<\/span><\/b><\/p>\n<p>F&uuml;r Benutzername und Kennwort ben&ouml;tigen wir jeweils die Callbacks f&uuml;r <b>getText<\/b>, <b>onChange <\/b>und <b>getEnabled<\/b>. Diese finden wir in Listing 8.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>ebBenutzername_GetEnabled(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef enabled)\r\n    enabled = (Val(GetSetting(cStrAppName, cStrSection, \"Authentifizierung\", \"0\")) = 1)\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ebBenutzername_OnChange(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, text<span style=\"color:blue;\"> As String<\/span>)\r\n    SaveSetting cStrAppName, cStrSection, \"Benutzername\", text\r\n    <span style=\"color:blue;\">Call<\/span> VerbindungszeichenfolgeAktualisieren\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ebBenutzername_GetText(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef text)\r\n    text = GetSetting(cStrAppName, cStrSection, \"Benutzername\", \"\")\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ebKennwort_GetText(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef text)\r\n    text = GetSetting(cStrAppName, cStrSection, \"Kennwort\", \"\")\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ebKennwort_OnChange(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, text<span style=\"color:blue;\"> As String<\/span>)\r\n    SaveSetting cStrAppName, cStrSection, \"Kennwort\", text\r\n    <span style=\"color:blue;\">Call<\/span> VerbindungszeichenfolgeAktualisieren\r\n    m_ribbon.Invalidate\r\n<span style=\"color:blue;\">End Sub<\/span>\r\n<span style=\"color:blue;\">Public Sub <\/span>ebKennwort_GetEnabled(control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef enabled)\r\n    enabled = (Val(GetSetting(cStrAppName, cStrSection, \"Authentifizierung\", \"0\")) = 1)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Callbacks f&uuml;r die Steuerelemente ebBenutzername und ebKennwort<\/span><\/b><\/p>\n<p>Die <b>getText<\/b>-Callbacks lesen jeweils den Benutzernamen und das Kennwort aus der Registry ein.<\/p>\n<p>Die <b>onChange<\/b>-Callbacks werden ausgel&ouml;st, wenn der Benutzer einen der Eintr&auml;ge &auml;ndert. Sie sorgen daf&uuml;r, dass die neuen Werte in der Registry gespeichert werden, und die Verbindungszeichenfolge und die Steuerelemente des Ribbons aktualisiert werden.<\/p>\n<p>Die <b>getEnabled<\/b>-Callbacks sorgen daf&uuml;r, dass die beiden Steuerelemente ebBenutzername und ebKennwort nur aktiviert werden, wenn als Authentifizierungsmethode <b>SQL Server-Authentifizierung <\/b>ausgew&auml;hlt ist.<\/p>\n<h2>Verbindungszeichenfolge<\/h2>\n<p>Die letzte Gruppe zeigt die fertig zusammengesetzte Verbindungszeichenfolge an und stellt zwei Buttons bereit. Das Attribut <b>style=&#8221;warning&#8221;<\/b> gibt der Gruppe einen farblichen Hintergrund und hebt sie optisch vom Rest ab. Ein <b>labelControl<\/b> zeigt den aktuellen Wert, ein <b>layoutContainer<\/b> ordnet die zwei Buttons nebeneinander an:<\/p>\n<pre>&lt;group id=\"grpVerbindungszeichenfolge\"\r\n   label=\"Verbindungszeichenfolge\" style=\"warning\"&gt;\r\n  &lt;topItems&gt;\r\n    &lt;labelControl id=\"lblVerbindungszeichenfolge\"\r\n      getLabel=        \"lblVerbindungszeichenfolge_GetLabel\"\/&gt;\r\n    &lt;layoutContainer id=\"lcVerbindung\"\r\n        layoutChildren=\"horizontal\"&gt;\r\n      &lt;button id=\"btnVerbindungTesten\"\r\n        label=\"Verbindung testen\"\r\n        imageMso=\"RefreshAll\"\r\n        onAction=\"btnVerbindungTesten_OnAction\"\/&gt;\r\n      &lt;button id=\"btnVerbindungszeichenfolgeKopieren\"\r\n        label=\"In Zwischenablage kopieren\"\r\n        imageMso=\"Copy\"\r\n        onAction=\r\n          \"btnVerbindungszeichenfolgeKopieren_OnAction\"\/&gt;\r\n    &lt;\/layoutContainer&gt;\r\n  &lt;\/topItems&gt;\r\n&lt;\/group&gt;<\/pre>\n<p>Die Prozedur <b>VerbindungszeichenfolgeAktualisieren<\/b> wird von allen Callbacks aufgerufen, die einen der Eingabewerte &auml;ndern. Sie liest alle Einzelwerte aus der Registry und setzt sie zur fertigen Zeichenfolge zusammen. F&uuml;r den ODBC Driver 18 werden zus&auml;tzliche Parameter f&uuml;r die verschl&uuml;sselte Verbindung erg&auml;nzt (siehe Listing 9).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>VerbindungszeichenfolgeAktualisieren()\r\n    <span style=\"color:blue;\">Dim <\/span>str<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>intAuth<span style=\"color:blue;\"> As Integer<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>intTreiber<span style=\"color:blue;\"> As Integer<\/span>\r\n    intAuth = Val(GetSetting(cStrAppName, cStrSection, \"Authentifizierung\", \"0\"))\r\n    intTreiber = Val(GetSetting(cStrAppName, cStrSection, \"TreiberIndex\", \"0\"))\r\n    str = \"Driver={\" & GetTreiberName(intTreiber) & \"};\" & \"Server=\" & GetSetting(cStrAppName, cStrSection, _\r\n        \"Servername\", \"\") & \";\" & \"Database=\" & GetSetting(cStrAppName, cStrSection, \"Datenbankname\", \"\") & \";\"\r\n    <span style=\"color:blue;\">If <\/span>intAuth = 1<span style=\"color:blue;\"> Then<\/span>\r\n        str = str & \"UID=\" & GetSetting(cStrAppName, cStrSection, \"Benutzername\", \"\") & \";\" & \"PWD=\" _\r\n            & GetSetting(cStrAppName, cStrSection, \"Kennwort\", \"\") & \";\"\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        str = str & \"Trusted_Connection=Yes;\"\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">If <\/span>intTreiber = 1<span style=\"color:blue;\"> Then<\/span>\r\n        str = str & \"Encrypt=yes;TrustServerCertificate=yes;\"\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    SaveSetting cStrAppName, cStrSection, \"Verbindungszeichenfolge\", str\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Verbindungszeichenfolge zusammenbauen<\/span><\/b><\/p>\n<p>Das Label zeigt die Zeichenfolge mit Zeilenumbr&uuml;chen nach jedem Semikolon an. Der <b>getLabel<\/b>-Callback ersetzt dazu alle Semikolons durch ein Semikolon gefolgt von einem Zeilenvorschub:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>lblVerbindungszeichenfolge_GetLabel( _\r\n        control<span style=\"color:blue;\"> As <\/span>IRibbonControl, ByRef label)\r\n    <span style=\"color:blue;\">Dim <\/span>str<span style=\"color:blue;\"> As String<\/span>\r\n    str = GetSetting(cStrAppName, _\r\n        cStrSection, \"Verbindungszeichenfolge\", \"\")\r\n    label = <span style=\"color:blue;\">Replace<\/span>(str, \";\", \";\" & <span style=\"color:blue;\">vbLf<\/span>)\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Der Verbindungstest &ouml;ffnet per ADODB eine Verbindung mit der gespeicherten Zeichenfolge und schlie&szlig;t sie sofort wieder. Bei Erfolg erscheint eine Best&auml;tigungsmeldung, bei einem Fehler die Beschreibung von ADODB:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>btnVerbindungTesten_OnAction( _\r\n        control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n    <span style=\"color:blue;\">Dim <\/span>cn<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n    <span style=\"color:blue;\">Set<\/span> cn = CreateObject(\"ADODB.Connection\")\r\n    cn.Open GetSetting(cStrAppName, _\r\n        cStrSection, \"Verbindungszeichenfolge\", \"\")\r\n    cn.Close\r\n    <span style=\"color:blue;\">Set<\/span> cn = Nothing\r\n    <span style=\"color:blue;\">MsgBox<\/span> \"Verbindung erfolgreich.\", vbInformation\r\n    <span style=\"color:blue;\">Exit Sub<\/span>\r\nFehler:\r\n    <span style=\"color:blue;\">MsgBox<\/span> \"Verbindung fehlgeschlagen:\" _\r\n        & <span style=\"color:blue;\">vbCrLf<\/span> & Err.Description, <span style=\"color:blue;\">vbCr<\/span>itical\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Das Kopieren in die Zwischenablage gelingt in VBA ohne externe Bibliotheken &uuml;ber ein tempor&auml;res <b>Forms.TextBox<\/b>-Objekt. Der Text wird eingetragen, vollst&auml;ndig markiert und per <b>Copy<\/b>-Methode &uuml;bertragen:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>btnVerbindungszeichenfolgeKopieren_OnAction( _\r\n        control<span style=\"color:blue;\"> As <\/span>IRibbonControl)\r\n    <span style=\"color:blue;\">Dim <\/span>str<span style=\"color:blue;\"> As String<\/span>\r\n    str = GetSetting(cStrAppName, _\r\n        cStrSection, \"Verbindungszeichenfolge\", \"\")\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(str) = 0<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">MsgBox<\/span> \"Die Verbindungszeichenfolge\" _\r\n            & \" ist leer.\", vbExclamation\r\n        <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    <span style=\"color:blue;\">With<\/span> CreateObject(\"Forms.TextBox.1\")\r\n        .MultiLine = <span style=\"color:blue;\">True<\/span>\r\n        .Text = str\r\n        .SelStart = 0\r\n        .SelLength = <span style=\"color:blue;\">Len<\/span>(str)\r\n        .Copy\r\n    End <span style=\"color:blue;\">With<\/span>\r\n    <span style=\"color:blue;\">MsgBox<\/span> \"In die Zwischenablage kopiert.\", _\r\n        vbInformation\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<h2>Schritte zum Implementieren der L&ouml;sung in eine eigene Datenbank<\/h2>\n<p>Um die hier vorgestellte L&ouml;sung in einer eigenen Datenbank zu verwenden, sind die folgenden Schritte n&ouml;tig:<\/p>\n<ul>\n<li>Hinzuf&uuml;gen der Tabelle <b>USysRibbons<\/b><\/li>\n<li>Einstellen des Men&uuml;bands der Anwendung auf den verwendeten Ribbon-Namen<\/li>\n<li>Hinzuf&uuml;gen des Moduls <b>mdlBackstage<\/b><\/li>\n<li>Hinzuf&uuml;gen eines Verweises auf die Bibliothek <b>Microsoft Office 16.0 Object Library<\/b><\/li>\n<\/ul>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Der Backstage-Bereich von Access bietet deutlich mehr Platz als das Ribbon und eignet sich gut f&uuml;r Konfigurationsdialoge, die dauerhaft erreichbar sein sollen. Die hier vorgestellte L&ouml;sung zeigt, wie sich mit &uuml;berschaubarem VBA-Code ein vollst&auml;ndiger Verbindungsassistent f&uuml;r SQL Server direkt in Access integrieren l&auml;sst &#8211; ohne separate Formulare und ohne externe Abh&auml;ngigkeiten.<\/p>\n<p>Zwei Details verdienen besondere Aufmerksamkeit: Alle Get-Callbacks im Backstage verwenden <b>ByRef<\/b>-Parameter statt Funktionsr&uuml;ckgabewerten &#8211; falsche Signaturen f&uuml;hren zu stillen Fehlern ohne Fehlermeldung. Und der <b>onAction<\/b>-Callback der <b>radioGroup<\/b> erwartet den Index als <b>Long<\/b>, nicht als <b>Integer<\/b>.<\/p>\n<p>Die L&ouml;sung l&auml;sst sich noch erweitern. Wir k&ouml;nnten zum Beispiel einen weiteren Bereich hinzuf&uuml;gen, indem wir alle in der aktuellen Datenbank verwendeten Verbindungen anzeigen und verwalten.<\/p>\n<p>Oder wir f&uuml;gen Elemente hinzu, mit denen wir die aktuell zusammengestellten Verbindungsinformationen speichern k&ouml;nnen, um diese anschlie&szlig;end wieder aufzurufen. Wenn wir alle Verbindungen gespeichert haben, k&ouml;nnen wir so von allen Datenbanken auf diese zugreifen, in denen wir diese Backstage-Definition integriert haben.<\/p>\n<p>Wir k&ouml;nnen auch noch Funktionen hinzuf&uuml;gen, mit denen wir einzelne oder auch alle Tabellen der aktuellen Verbindung als Tabellenverkn&uuml;pfung zur ge&ouml;ffneten Access-Datenbank hinzuf&uuml;gen.<\/p>\n<p>Und als kr&ouml;nenden Abschluss k&ouml;nnten wir die Backstage-Erweiterung, die wir hier in eine einzelne Access-Datenbank integriert haben, in Form eines COM-Add-Ins realisieren, damit die Funktionalit&auml;t fortan in allen Access-Datenbanken zur Verf&uuml;gung steht.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer in Access mit verkn&uuml;pften SQL Server-Tabellen arbeitet, kennt das Problem: Die Verbindungszeichenfolge muss korrekt zusammengesetzt sein, Treiber und Authentifizierungsart m&uuml;ssen stimmen &#8211; und bei jeder neuen Datenbank f&auml;ngt man von vorn an. Dieser Artikel zeigt, wie Du einen eigenen Tab im Backstage-Bereich von Access einrichtest, der Dir das Zusammenbauen der Verbindungszeichenfolge abnimmt. <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66022026,662026,44000024,44000026,44000006,44000031],"tags":[],"yst_prominent_words":[],"class_list":["post-55000504","post","type-post","status-publish","format-standard","hentry","category-66022026","category-662026","category-Berichte_und_Reporting","category-Outlook_programmieren","category-SQL_Server_und_Co","category-twinBASICProgrammierung"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000504","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000504"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000504\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000504"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000504"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000504"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000504"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}