infobase: EDV - MS-Access


Allgemein

Access beenden   Quelle: dmt   Datum: 01.2008   nach oben

Das BEENDEN einer Applikation per Application.Quit

kann zum mittleren Drama werden, wenn man edlerweise einen Dialog zwischenschalten will, der z.B. ein Datensicherungsmenü oder wenigstens eine Nachfrage anbietet und dies wahlweise per Schaltfläche, aber auch beim
<Strg+F4>-Schließen des Formulares ausgelöst werden soll.

Ruckzuck werden Ereignisse mehrfach aufgerufen, aller Versuche mit Formular-globalen Variablen scheitern, da diese beim Ereignis FORM_UNLOAD scheinbar vergessen werden, und übrig bleibt nur Frust.

Kommt Zeit, kommt Rat, aber meistens ist guter Rat teuer:

Die wohl beste Lösung zum Thema 'Schließen einer Anwendung' sieht aus wie folgt:


Einem Steuerelement 'pb_Ende' wird folgender Code zugeordnet:

Sub pb_Ende_Click ()

    On Error GoTo err_Ende

    DoCmd Close A_FORM, Me.Name

    Exit Sub


err_Ende:

    Me!pb_Ende.SetFocus

    Exit Sub

End Sub


Wenn bei Form_Unload das Schließen verneint wird ('Abbrechen' gewählt), tritt für die Close-Anweisung ein Fehler und oft auch ein Fokusverlust ein, der durch den Fehlerhandler behoben wird.

Fürs Entladen des Formulares gilt:

Sub Form_Unload (Cancel As Integer)

    Cancel = DontQuitApp()

End Sub

Die erweiterte Funktion DontQuitApp unterscheidet zwischen Runtime- und Entwicklungsumgebung und bietet im Access-Fall das vollständige Beenden oder das Verbleiben innerhalb von MS-Access an:

Private Function DontQuitApp () As Integer

    ' **** Beendet die Anwendung oder Access in Access- oder Runtime-Umgebung ****

    Beep

    If SysCmd(SYSCMD_RUNTIME) Then
       Select Case MsgBox("Wollen Sie die Anwendung beenden ?", 33, "Programmende")
          Case 1:       Application.Quit
          Case Else:    DontQuitApp = True
       End Select
    Else
       Select Case MsgBox("Wollen Sie die Anwendung komplett beenden oder in Access verbleiben?", 35, "Programmende")
          Case 6:       Application.Quit
          Case 7:       DontQuitApp = False
          Case Else:    DontQuitApp = True
       End Select
    End If

End Function

Das kann selbstverständlich auch so implementiert werden, daß sich die Anwendung in der Laufzeitumgebung kommentarlos schließt, während in der normalen Accessumgebung ein Fragedialog erscheint.

Diese Lösung beherrscht ALLE Varianten des Schließens wie über Schaltfläche, <Strg+F4>, <Alt+F4> sowie Doppelklicken des Systemmenü-Symboles im Formular und auch im Anwendungsfenster und ist somit anwendersicher !

Selbst wenn Windows beendet wird, obwohl die Access-Anwendung noch geöffnet ist, erscheint nach Bestätigen des Windows-Schließen-Dialoges der Access-Formular-Unload-Dialog. Wird das Schließen der Anwendung verneint, wird auch der Windows-Beenden-Vorgang abgebrochen. Genialer gehts nimmer.

Durch den Wert 33 werden 'OK' und 'Abbrechen' angezeigt; das ist auf den ersten Blick nicht so einleuchtend wie 'Ja' und 'Nein', aber es ermöglicht die komplette Steuerung des Vorganges per <Return> und <Esc>. 1 ist dann der
Rückgabewert für 'OK'. Im erweiterten Fall zeigt 35 'Ja', 'Nein' und 'Abbrechen' an, was immerhin den Rückschritt per <Esc> erlaubt.

Obendrein wird automatisch zwischen Runtime und Access unterschieden. Das Runtime-Modul wird geschlossen, in Access verbleibt man im Datenbankfenster (wenn man will). YES !

* * * *

Unterbinden von <Alt+F4> zum Beenden von Applikation bzw. Access:

Das Beispiel zeigt, wie bei einem gebundenen Formular der Hotkey <Alt+F4>, der u.a. das Ereignis Unload auslöst, durch setzen des Cancel-Parameters unwirksam gemacht werden kann. Ein dafür vorgesehener PushButton kann einen Flag setzen, der das Schließen doch noch erlaubt. Das funktioniert so gut, daß ich beim Probieren mehrfach per Windows-Applikations-Warmstart Access schließen mußte, das es aus dem Formular kein Entrinnen gab. Geil !

Sub Form_Unload (Cancel As Integer)

    On Error GoTo err_Form_Vollbild_Unload

    ' Beenden über  wird durch Cancel=true abgefangen.
    ' Damit es per pb noch geht, wird ein Flag Ende benutzt.

    If Ende = False Then Cancel = True

    If Cancel = True Then

       ' Der Bezug auf Screen.ActiveControl.Name erzeugt einen
       ' gewollten Fehler, der so extra behandelt werden kann.

       x% = IsNull(Screen.ActiveControl.Name)

    End If

    Exit Sub


err_Form_Vollbild_Unload:

    If Err = 2474 Then
       ' fokussiere das erste Steuerelement in der Auflistung
       Me(0).SetFocus
    Else
       Fehler "Form Vollbild Unload"
    End If

    Resume Next

End Sub


allgemein   Quelle: dmt   Datum: 03.2004   nach oben

Unter OA3 standen nach dem Ausführen einer Abfrage die Eigenschaften 'Anzahl der Daten-sätze' und 'Datensatznummer' sofort zur Verfügung. In Access kann das nur im Nachhinein mit Hilfe peinlicher Move-ereien ermittelt werden. Vereinzelt kam es aber bereits vor, daß die Anzahl der Recordset-Datensätze nach Evaluierung bekannt war.

Versucht man aber innerhalb eines Berichtes davon Gebrauch zu machen, muß man feststellen, daß das hier nicht möglich ist. Dafür werden andere (faule) Lösungen angeboten:
Der Ausdruck

=Anzahl([BNr]) & " von " & DomAnzahl("BNr";"Ventile") & " Ventildatensätzen"
ermittelt innerhalb eines Berichtes in der Eigenschaft Steuerelementinhalt eines Textfeldes die Anzahl der Datensätze des Berichtes, in denen kein NULL-Wert enthalten ist. Stark !

Und da wir gerade bei Berichten sind: Es ist nicht nur möglich, die Nummer der aktuellen Seite anzeigen zulassen, nein, sogar die Gesamtzahl der benötigten Seiten kann bereits auf der ersten Seite vermerkt werden !
Das ist wirklich eine Leistung !
Und das geht so:

Eigenschaft Steuerelementinhalt eines Textfeldes:

="Blatt " & [Seite] & " von " & [Seiten]

Wenn innerhalb des Modul-Programmier-Fensters in der Element-Auswahlliste ein bisher noch nicht mit Code versehenes Element gewählt wird, wird in der Regel eine Sub 'Element_BeforeUpdate' o.ä. geöffnet. Wählt man in der Ereignis-Auswahlliste ein anderes Ereignis, bleibt die leere, eingangs erzeugte Sub-Routine bestehen ! Im Gegensatz hierzu ist Visual Basic ist immerhin so intelligent, leere Routinen beim Speichern zu entfernen.

Aber das ist erst der Anfang einer langen, nicht enden wollenden Geschichte ...


Auflösung   Quelle: dmt   Datum: 03.2004   nach oben

Tja, und dann hätten wir da auch noch die Überprüfung der Auflösung z.B. beim Programmstart, um den Anwender evtl. darauf hinzuweisen, daß er zur Zeit nicht die für diese Anwendung vorgesehene Auflösung eingestellt hat.

Private Sub Check_Resolution ()

    On Error GoTo err_Check_Resolution

    ' **** Ist die aktuelle Auflösung 800*600 ? ****

    Dim CR As String, s As String
    Dim x As Integer, Y As Integer

    GetResolution x, Y

    If x = 800 And Y = 600 Then
       ' Gratulation, die optimale Auflösung ist eingestellt
    Else
       Beep
       CR = Chr$(13) & Chr$(10)
       s = "Diese Anwendung wurde für die Bildschirm-Auflösung 800*600 optimiert."
       s = s & CR & CR & "Sie können mit dieser Anwendung auch in anderen Auflösungen arbeiten."
       s = s & CR & CR & "Bei höheren Auflösungen werden die angezeigten Informationen "
       s = s & "kleiner dargestellt und bei einer geringeren Auflösung können Sie z. Bsp. "
       s = s & "die Felder des Kennlinien-Formulares nicht alle mit einem Blick erfassen."
       MsgBox s, 64, "Check_Resolution"
    End If

    Exit Sub


err_Check_Resolution:

    Fehler "Check_Resolution"
    Exit Sub

End Sub


Ausdrücke   Quelle: dmt   Datum: 03.2004   nach oben

BEZEICHNER IN AUSDRÜCKEN:

Im HP/VHP-Manager ergab es sich, daß ein Extra-Sortierfeld (Integer) eingeführt wurde, um bei großen Datenmengen eine schnelle Sortierung zu gewährleisten.
Leider mußte später festgestellt werden, daß in der Tabelle bei den meisten Datensätzen kein Wert zugewiesen wurde. Eine Laufzeit-Überprüfung mit '? Forms!Ventile_HRV1!Art_Sort' nennt den jeweiligen Wert. Eine Zuweisung im Code per 'Art_Sort = DLookup(...)' landet jedoch im Variablen-Nirwana. Der Ausdruck 'Forms!Ventile_HRV1!Art_Sort' müßte laut Definition auf ein Steuerelement 'Art_Sort' im genannten Formular verweisen, daß dort aber gar nicht enthalten
ist ! Wenn Access auf diese Anweisung trifft, erkennt es anscheinend automatisch, daß es sich hierbei nicht um ein Steuerelement, sondern um ein im Recordset ( SELECT * ) enthaltenes Feld handelt.

Wenn die Anweisung

       Art_Sort = DLookup("Nr", "Ventile_Arten", "Titel = '" & s$ & "'")

durch
       Forms(Me.Name).Art_Sort = DLookup("Nr", "Ventile_Arten", "Titel = '" & s$ & "'")

ersetzt wird, wird dem im Recordset des Formulares enthaltenen Feld 'Art_Sort' der richtige Wert zugewiesen.

Das ganze klappt auch mit den Bezeichnungsausdrücken 'Me.Art_Sort' sowie 'Me!Art_Sort', obwohl 'Art_Sort' weder ein Steuerelement noch eine Eigenschaft des Formulares ist.

Verstehe das, wer will. Am besten nix glauben und ALLES nachprüfen !

* * * *

In den meisten Fällen sind Ausdrücke im deutschsprachigen Eigenschaften-Fenster nur dann zu gebrauchen, wenn sie einfache Zusammenhänge enthalten. Dann funktioniert die Sache auch gut und schnell, da Access Steuerelement-eigenen
Eigenschaften auf eine nicht näher dokumentierte Art bevorzugt abzuarbeiten scheint.

Als Programmierer wird man jedoch meistens lieber einer Funktion schreiben, in der ausführlich auf Fallunterscheidungen etc. eingegangen werden kann.

Fairerweise muß zugunsten der Ausdrücke gesagt werden, daß u.U. auftretende Null-Werte und ähnliche Sauereien in Bezeichnern in Ausdrücken kommentarlos verdaut werden und somit manchmal einfacher gearbeitet werden kann, als mit
endlosen 'IF ISNULL(x) THEN' in selbstgeschrieben Routinen. Das deutet übrigens darauf hin, das Access die Inhalte von Steuerelementen quasi Variant-mäßig behandelt. Das macht zwar nur wenig Ärger, ist aber tempomäßig nicht das Gelbe
vom Ei.

Das folgende Beispiel ließ sich sogar NUR mit Hilfe eines Ausdruckes realisieren:

In dem i.d.R. ausgeblendetem Unterformular 'Leistungsrapport' des Formulares 'Termine' gibt es ein berechnetes Steuerelement "Dauer_brutto", das die Dauer einer Leistung, die durch die gebundenen Felder 'von' und 'bis' gegeben ist,
berechnen soll. Weil ein Termin manchmal über Mitternacht hinaus geht, kommt es wie seinerzeit bei AOS vor, das 'bis' kleiner ist als 'von'. Mit einer kleinen Routine ist das schnell erledigt, jedoch erzeugte die Zeile, in der dem
Steuerelement der korrekte Wert zugewiesen wurde, jedesmal einen Fehler ('Wert kann nicht zugewiesen werden'), obwohl (oder gerade weil ?) das Unterfomular noch nicht einmal sichtbar war. Erst als die relative einfache Bedingung in Form
eines Ausdruckes

'=Wenn([bis]<[von];1-([bis]-[von]);[bis]-[von])'
a'la Excel gesetzt wurde, klappt die Sache.

* * * *

AUSWERTEN VARIABLER WERTE in BEZEICHNER FÜR AUSDRÜCKE:

Access offenbart in seiner Hilfe zu 'Bezeichner in Ausdrücken', daß explizite Verweise a'la 'FORMS!QV!BNr.xyz' auch veränderliche Elemente enthalten kann, wenn Sie mit dem Zeichen '|' eingeschlossen sind, auswertet werden, bevor der
gesamte Ausdruck verarbeitet wird.

Das klappt, wenn überhaupt, aber nur innerhalb der pseudodeutschen Syntax im Makroentwurf oder Formulareigenschaften.

Meine Experimentierfreudigkeit ( oft Rettungsanker ) wurde mit einem schnellen Treffer belohnt. In Access-Basic sieht das dann so aus:

For c% = 0 To RSQ.Fields.Count - 1
    ZF(RSQ(c%).Name) = RSQ(c%)
Next c%

Hier werden den Steuerelementen des Zielformulars ZF, deren Namen gleich denen der zugeordneten Tabellenfelder sind, der Reihe nach die Feldinhalte des QuellRecordSets RSQ übergeben.

In Open Access konnte so etwas mit einem Griff ins Handbuch und 1 ! Zeile Code gelöst werden (Record-Anweisung).

Nachzulesen in HP/VHP.MDB, Modul 'Ventile', Sub 'Copy_Valve_Data'.


Dateien, Klein- und Großschreibung   Quelle: dmt   Datum: 03.2005   nach oben

Wenn Access 2.0 mittels Visual Basic als 16Bit-Software eine neue Datei anlegt, wird die Datei so im Dateisystem gespeichert, daß alle Buchstaben des Dateinamens als Großbuchstaben geschrieben werden.

Im reinen Windows-Betrieb ist das eher ein kosmetisches Problem, daß beim Betrachten der Verzeichnisinhalte im Windows Explorer (ich glaube, je nach Windows-Version nicht immer ganz einheitlich) auftritt.

Spaßfrei wird es aber, wenn z.B. in einem Content-Management-System solche Dateien mit entsprechenden Verlinkungen auf einem Webserver landen. Der läuft (wie es sich gehört) zu fast 100% unter einem UNIX-ähnlichen System und dort wird eben knallhart zwischen Großschreibung und Kleinschreibung unterschieden.

Was ist zu tun, wenn man nicht jedesmal die Schreibweise der betroffenen Dateien manuell korrigieren möchte ?

Wenn die Seiten, in denen verweisende Hyperlinks stehen, auch in der 16-Bit-Anwendung erzeugt werden, reicht es aus, die Verweise auf solche zwangs-groß geschriebenen Dateinamen eben z.B: per UCase() ebenfalls in Großschreibung anzulegen.

Interessanterweise tritt das Phänomen nicht auf, wenn eine Datei mit kleingeschriebenem Namen bereits existiert und durch die 16Bit-Anwendung neu geschrieben wird. So habe ich die Datei, die die manuell navigierbare Übersicht der infobase enthält (ibnavi.htm), in Kleinbuchstaben unter meinem NT4 auf einem Fat16-Laufwerk angelegt. Entsprechend landet sie auch beim ftp-Transfer auf dem Webspace, wo die entsprechenden Links mit korrekter Kleinschreibung darauf verweisen. Diese Datei wird aber bei jedem Export aus Access 2.0 heraus neu geschrieben, wobei die Schreibweise des Dateinamens unverändert bleibt.

Falls die Situation vertrackter sein sollte, würde ich versuchen, mittels irgendeines Freeware-Tools, daß (am besten Kommandozeilen-gesteuert) z.B. alle Dateien in bestimmten Verzeichnissen einheitlich kleinschreibt. Eine Google-Recherche sollte helfen.


Datenbank, Bibliothek   Quelle: dmt   Datum: 03.2004   nach oben

Die Sache mit BIBLIOTHEKs-Datenbanken / MDA-Dateien

ist sehr interessant und, wie nicht anders zu erwarten, nicht ohne Stolperfallen:

Wichtig zu wissen ist, daß die Anweisung 'Set DB=DBEngine.Workspaces(0).Databases(0)', in einer Bibliotheksdatenbank
ausgeführt NICHT auf die Bibliotheksdatenbank selbst verweist, sondern auf die Datenbank, die eigentlich geladen wurde. Siehe den Fall LaserJob, indem zuerst viel Verwirrung gestiftet wurde, als diese Anweisung in der mda-Datei nicht das
gewünschte Ergebnis erbrachte und statt dessen auf die mdb-Datei verwies, die selbst KEINEN Code enthält. In solchen Fällen muß die 'aktuelle' Bibliotheksdatenbank, in der der Code abgearbeitet wird, mit 'Set DB=CodeDB()' bestimmt werden.

Sind in dieser mda-Datei alle für die Anwendung relevanten Funktionen enthalten, das heißt, auch Tools a'la 'DataBase_Dir()', dann muß Sorge dafür getragen werden, daß in keiner Datenbank, die in dieser Sitzung unter dem aktiven
Access-Modul geladen wird, Code mit gleichlautenden Routinen-Namen enthalten ist. Das ist zwar nervig, wenn man eine Fehlermeldung nach der anderen enthält, hat aber immerhin zu einer abgespeckten transact.mdb geführt, die selbst kaum
noch Code enthält.

Nachdem zu diesem Thema Klarheit geschaffen wurde, mußten erneute Indifferenzen festgestellt werden:

Eine Abfrage der Makrodaten enthaltenden MSysmacros innerhalb einer Bibliotheksdatenbank fragt natürlich die bereits in der Bibliotheksdatenbank enthaltene Tabelle MSysmacros ab. Eine Dummy-Abfrage "FROM Adressen" findet aber zur Laufzeit sehr wohl und ganz ohne eingebundene Tabellen die Daten der Databases(0)-Datenbank, von der aus eine Bibliotheksfunktion aufgerufen wurde.
Wie einer Bibliotheks-internen Abfrage so etwas beigebracht werden sollte, kann ich mir z.Zt. nicht vorstellen, aber im Beispiel des DB_Info-Formulares in Lib_dmt.mda konnte alles über Code abgecheckt werden.


Datenbank, externe   Quelle: dmt   Datum: 05.2006   nach oben

EXTERNE DATENBANK:

Viele der Klimmzüge, die per SQL-Abfragen und Code Daten beherrschbar machen, lassen sich auch mit Daten, die in Tabellen externer Datenbanken stehen, durchführen.

Das Beispiel einer Aktualisierungsabfrage zeigt, wie einfach das geht:

UPDATE AD IN "g:\public\ad20\adback.mdb" SET Briefanrede="Mister" WHERE Briefanrede="Mr";

Entsprechend läuft das auch mit

SELECT * FROM Adressen IN '...'
.

Auch die Probleme mit den leidigen Domänen in den Domänen-Aggregat-Funktionen kann teilweise mit solchen Tricks behoben werden:

SELECT Max(Briefanrede) AS Maximum FROM AD IN "g:\public\ad20\adback.mdb";

liefert z.B. einen Datensatz mit einem Feld. Das kann aber genausogut auch als 'ein Wert' aufgefasst werden, der mit einer Anweisung a'la 'x=DMax("Briefanrede","AD")' partout nicht zu ermitteln ist, da 'AD' immer nur als internes Objekt aufgefasst wird.

Mit ein bißchen Quelltext läßt sich das Ergebnis aber auch per SQL ermitteln (ich liebe es immer mehr):

If Err = 3078 Then
   Get_MinValue = Get_AggregatValue_per_SQL("Min(" & FN & ")", DB.Name, TN)

fängt in einem Errorhandler das Mißlingen einer Domänen-Aggregat-Funktion auf, und versucht das Ergebnis von einer SQL-basierenden Funktion zu erhalten, an die die gewünschte Funktion als vollständiger String sowie die Namen von Datenbank,
Tabelle und Feld übergeben werden.

Private Function Get_AggregatValue_per_SQL (vFunktion As Variant, vDBName As Variant, vTName As Variant)

    On Error GoTo err_Get_AggregatValue_per_SQL

    Dim MyDB As Database, RS As Recordset

    Set MyDB = DBEngine.Workspaces(0).Databases(0)
    Set RS = MyDB.OpenRecordset("SELECT " & vFunktion & "(" & vFName & ") AS Wert FROM " & vTName & " IN '" & vDBName & "';")

    Get_AggregatValue_per_SQL = RS("Wert")

    RS.Close
    MyDB.Close

    Exit Function


err_Get_AggregatValue_per_SQL:

    Fehler "Get_AggregatValue_per_SQL"
    Exit Function

End Function

Da hier alles variabel zusammengebaut wird, ist das Ding ziemlich potent; mehr als einen ungültigen Funktions-String zu übergeben, kann eigentlich nicht passieren.

* * * *

IMPORTIEREN / EXPORTIEREN / TRANSFERTEXT / TRANSFERDATABASE / SPEZIFIKATIONEN:

Für den Austausch mit externen Daten / Informationen, die nicht gerade in einem geeignetem Standard-Applikationsformat vorliegen, ist oftmals ein TransferText flexibler als ein TransferDatabase.

Es können eigene Exportformate (-spezifikationen) definiert werden, wenn der Menüdialog Import/Export-Einstellungen oder aber Datei / Exportieren / Text (festgelegtes Format) bemüht wird. Diese Spezifikationen werden in der Tabelle
MSysIMEXSpecs hinterlegt, die dazugehörenden, feldbezogenen Informationen in MSysIMEXColumns. Dort können auch von Hand getürkte Sachen hinterlegt werden, aber so richtig glücklich bin ich damit trotzdem nicht geworden. So werden bei
EXPORTIEREN Daten über eine definierte Länge mit Blanks oder bei AUSGABE IN mit Pseudo-Ascii-Strichen aufgefüllt oder unterteilt, was eine Datei, die mit (unnötigen) Word-Steuerdatei-Zeichen wie ';' und '"' 80 kB groß ist, zu 240 kB
anschwellen läßt. Ein Code-mäßiges TransferText erzeugt erstmal eine 80 kB große, Word-kompatible Datei. EXPORTFIXED erzeugt die Dateien in festen Breiten, siehe oben. Für EXPORTDELIM muß keine Spezifikation angegeben, kann aber angegeben werden. Da können dann wunschgemäße Dinge eingestellt werden, das Feld-Trennzeichen kann aber leider nur 1 Zeichen lang sein. Der Versuch, mehrere Zeichen einzugeben, scheitert: es wird nur 1 Zeichen ausgelesen.


Datenbank, externe anlegen   Quelle: dmt   Datum: 02.2005   nach oben

Eine komplette DATENBANK ANLEGEN / INITIALISIEREN:

Sinnvoll, wenn z.B. regelmässig Analyse-Daten erzeugt werden, die von einer Access-Anwendung ausgelesen werden sollen.

Durch das wiederholte Einlesen neuer Daten schwillt die Datenbank, die die Analysedaten-Tabelle enthält, ständig an. Diese Datenbank müßte häufig komprimiert werden.

Eine einfachere Lösung besteht darin, in der Anwendungs-Datenbank eine Vorlage der Tabelle zu halten und mittels einer kleinen Routine im Anwendungsverzeichnis eine neue Datenbank anzulegen, in die die leere Tabellen-Vorlage hinein exportiert wird. Der Zugriff auf diese "externen" Daten erfolgt über eine eingebundene Tabelle. Solange die Anwendung nicht portiert wird, behält sogar der explizite Quellen-Verweis (z.B. "c:\anwendung") seine Gültigkeit und steht selbst nach Löschen und Neuanlegen der Arbeits-Datenbank im vollen funktionalen Saft.

Das folgende Beispiel (Access 97) bewältigt periphere Probleme mit z.B. einer bereits vorhandenen Arbeits-Datenbank sowie der Datenquelle eines Unterformulares, das zur Laufzeit geöffnet ist und auf eben diese Daten zugreift.

Die Datenquelle dieses Unterformulares wird in einer Variablen "gesichert", zurück- und am Ende der Routine wieder auf ihren ursprünglichen Wert gesetzt.

Function InitAnalData() As Boolean

    On Error GoTo err_InitAnalData

    ' Um ein ewiges Aufblasen der Datenbank zu vermeiden (wiederholtes Löschen und Füllen der Tabelle
    ' tag_anal mit div. Memofeldern) wird die ganze Scheiße in einer dynamisch angelegten Datenbank
    ' abgewickelt.

    Dim WS As Workspace, AnalDB As Database, sNewDB As String, sRecordSource As String

    Set WS = DBEngine.Workspaces(0)

    Meldung "Initialisierung ..."

    sNewDB = Database_Dir() & "\" & "webanal.mdb"
    sRecordSource = Me!UF_tag_anal.Form.RecordSource

    Me!UF_tag_anal.Form.RecordSource = ""

    Kill sNewDB                                                 ' bestehende Datenbank löschen

    Set AnalDB = WS.CreateDatabase(sNewDB, dbLangGeneral)       ' neue Datenbank anlegen

    ' Vorlagen-Tabelle für tag-Analyse-Daten exportieren

    DoCmd.TransferDatabase acExport, "Microsoft Access", sNewDB, acTable, "tag_anal_default", "tag_anal", True, False

    Me!UF_tag_anal.Form.RecordSource = sRecordSource

    InitAnalData = True

exit_InitAnalData:

    Set AnalDB = Nothing
    Set WS = Nothing
    Meldung ""
    Exit Function


err_InitAnalData:

    If Err = 53 Then
       Resume Next
    Else
       Fehler "InitAnalData"
       Resume exit_InitAnalData
    End If

End Function


Datenbank, externe, BETRIEVE   Quelle: dmt   Datum: 03.2004   nach oben

Novell-BETRIEVE-Tabellen können laut Doku mit MS-Access angefasst werden.

Die Hilfe dazu ist je nach Stichwortwahl sogar umfangreich; trotzdem brauchte es 2 (ZWEI!) Jahre, bis die mittlerweile Btrieve 6.x-Adressen-Daten der strobelschen Kuhnle-Schreiner-Anwendung eingelesen werden konnten.

Es hing an sehr vielem:

win.ini:
[btrieve]
Options=/u:2 /m:64 /p:4096 /b:16 /f:20 /l:40 /n:12
/t:e:\windows\system\BTRIEVE.TRN

msacc20.ini:
[Installable ISAMs]
Btrieve=E:\WINDOWS\SYSTEM\btrv200.dll
[Btrieve]
Filter=Btrieve (file.ddf)|file.ddf|
Extension=ddf
OneTablePerFile=No
IndexDialog=No
CreateDbOnExport=Yes
[Btrieve ISAM]
DataCodePage=OEM

Obendrein klappt's erst dann so einigermaßen, wenn die Dateien in meinem e:\access\btrieve-Verzeichnis auch alle im Windows-System-Verzeichnis stehen, unabhängig davon, welche Inis irgendwelche Pfadangaben auf Dateien enthalten.

Und dann müssen unglücklicherweise die btrieve-Beschreibungsdateien file.ddf UND fields.ddf herhalten. Speziell fields.ddf war im Hause Strobel nicht auffindbar (wird von der btrieve-Anwendung nicht benötigt), sondern wird laut Doku von speziellen Dienstprogrammen wie xtrieve erzeugt und konnte seinerzeit vom Anwendungs-hersteller direkt bezogen werden.

Das hat dann, nach dem beim x-ten Versuch neue Fehlermeldungen neue Maßnahmen anregten, auch zum Erfolg geführt.


Datenbank, reparieren   Quelle: access-home   Datum: 05.2005   nach oben

Im schlimmsten Falle muß evtl. ein professioneller Datenretter bemüht werden.

Hilfe sowie Tipps zur Selbsthilfe bei Datenbankproblemen gibt's bei www.access-rettung.de.

Von www.access-home.de kommen folgende Hinweise:

Restaurierung defekter Datenbanken:

Gerade bei Datenbanken die beim Starten Fehler melden oder gar nicht mehr anlaufen, können folgende Schritte helfen.

Durch diese Vorgehensweise werden nur die Objekte der Datenbank kopiert, die auch tatsächlich ausgewählt wurden. Interne, bereits gelöschte oder fehlerhafte Objekte werden nicht übernommen.
Durch diese Prozedur ist es auch möglich, einzelne defekte Objekte der Datenbank zu lokalisieren und ggf. durch eine Kopie aus einem Backup zu ersetzen.

Eine weitere Rettungsmöglichkeit ist das Konvertieren der Datenbank in vorherige oder neue Accessversion.

Weitere Informationen bei Microsoft: ACC2000: Wie Sie eine beschädigte Datenbank reparieren können.


Datenbankobjekte listen   Quelle: dmt   Datum: 03.2004   nach oben

Das folgende Beispiel nimmt einen Parameter a'la "Abfrage_*" entgegen und gibt für diesen Fall die Namen aller Abfragen zurück, die mit der Zeichenkette "Abfrage_" anfangen, um sie dem Anwender z.B. in einem gesonderten Kombinationsfeld zur Verfügung zu stellen. Der RowSource-String enthält die gerippten Namen und danach die Klartextnamen. Wird das Kombifeld auf 2-spaltig (mit Breite 2.Spalte=0cm) eingestellt, so sieht der Anwender nur die geschönten Namen, während die Applikation die verborgenen Klartextbegriffe auswerten kann.

Allgemein kann diese Routine auch benutzt werden für:

DATENBANKOBJEKTE AUFLISTEN:

Function List_Abfragen (sName As String) As String

    On Error GoTo err_List_Abfragen

    ' **** Füllt die Werte-Liste des übergebenen Kombifeldes ****
    ' **** mit den Namen der Abfragen der angegebenen Art    ****

    ' angezeigt werden die Zeichenketten nach sName, eine zweite
    ' Spalte enthält den vollständigen String, der bequem als
    ' Datenquelle eingestellt werden kann.

    Dim DB As Database, i As Integer, s As String, v As Variant

    v = SysCmd(SYSCMD_SETSTATUS, "lese Abfragenamen ein ...")

    Set DB = DBEngine.Workspaces(0).Databases(0)

    For i = 0 To DB.QueryDefs.COUNT - 1
        If DB.QueryDefs(i).Name Like sName Then
           s = s & Mid$(DB.QueryDefs(i).Name, Len(sName)) & ";" &
DB.QueryDefs(i).Name & ";"
        End If
    Next i

    s = Left$(s, Len(s) - 1)

    List_Abfragen = s

    v = SysCmd(SYSCMD_CLEARSTATUS)

    Exit Function


err_List_Abfragen:

    v = SysCmd(SYSCMD_CLEARSTATUS)

    Fehler "List_Abfragen"
    Exit Function

End Function


Der folgende Aufruf

    ' **** Datenquelle für das Filter-Abfragen-Kombifeld einstellen ****

    Me!Filterabfragen.RowSource = "alle Datensätze;'" & strSQLRecordSource &
"';" & List_Abfragen("Abfrage_*")

stellt der zu bildenden Liste sogar einen eigenen String voran.


DDE   Quelle: dmt   Datum: 05.2006   nach oben

DDE oder der Wahnsinn ist überall:

Zum Thema DDE werden hier per Zufall gefundene Dokus und eigene Routinen zusammengetragen.


ALLGEMEINES zu DDE:

Zusammenarbeit mit Microsoft Word und Microsoft Excel (Häufige Frage)

Frage: Wie arbeitet Microsoft Access bei der Verwendung von OLE, DDE oder ODBC mit Microsoft Word für Windows und Microsoft Excel zusammen?


Verwenden von OLE:

Microsoft Access kann als eine OLE-Container-Anwendung fungieren. Sie können OLE-Objekte in Tabellen speichern oder sie in den Entwurf eines Formulars oder Berichts einbetten bzw. mit diesem verknüpfen. Außerdem unterstützt Microsoft
Access die OLE-Automatisierung. Sie können jedoch Microsoft Access-Objekte nicht in andere OLE-Anwendungen einbetten oder mit diesen verknüpfen.


Verwenden von DDE:

Microsoft Access kann entweder selbst einen DDE-Dialog (als DDE-Client) starten oder (als DDE-Server) auf einen DDE-Client antworten. Als DDE-Client-Anwendung kann Microsoft Access DDE-Verknüpfungen in Abfragen, Formularen und Berichten, jedoch nicht in Tabellen, enthalten. Als DDE-Server-Anwendung unterstützt Microsoft Access die folgenden DDE-Themen:

- Das Thema "System".
- Der Name einer Datenbank, Tabelle oder Abfrage.
- Eine Microsoft Access SQL-Anweisung.


Der Microsoft Access Word-Seriendruckassistent verwendet DDE, um eine DDE-Verknüpfung zwischen Microsoft Access und Word für Windows herzustellen. Sobald die Verknüpfung hergestellt ist, können Sie jederzeit Ihr Dokument in Word für
Windows öffnen und unter Verwendung der aktuellen Daten in Microsoft Access neue Serienbriefe oder Etiketten drucken.


Verwenden von ODBC:

Microsoft Word für Windows, Microsoft Excel und Microsoft Access enthalten ODBC-Treiber, die die Arbeit mit Microsoft Access-Datenbanken ermöglichen. Obwohl Microsoft Access keine ODBC-Treiber für Microsoft Word für Windows oder
Microsoft Excel enthält, können Sie mit diesen Anwendungen zusammenarbeiten, indem Sie aus dem Menü Datei einen der Befehle Importieren, Exportieren oder Ausgabe in wählen.

Sie können Microsoft Excel-Dateien (.XLS) oder Textdateien (.TXT) importieren und exportieren sowie Microsoft Word für Windows-Seriendruckdateien exportieren. Verwenden Sie den Befehl Ausgabe in, um die Ausgabe einer Tabelle, einer
Abfrage, eines Formulars, eines Berichts oder eines Moduls in einer Datei im Microsoft Excel-Format, Textdateiformat oder .RTF-Format zu speichern. Sie können dann eine Datei im Microsoft Excel-Format in Microsoft Excel und Dateien
im Textdateiformat oder .RTF-Format in Microsoft Word für Windows öffnen.


Verwenden von Microsoft Access als DDE-Server:

Microsoft Access unterstützt dynamischen Datenaustausch (DDE) sowohl als Datenziel- (Client) als auch als Quellanwendung (Server). Die folgenden Themen werden von Microsoft Access als DDE-Server unterstützt:

- das Thema "System"
- der Name einer Datenbank (Thema Datenbank)
- der Name einer Tabelle (Thema Tabellenname)
- der Name einer Abfrage (Thema Abfragename)
- eine Microsoft Access SQL-Anweisung (Thema Sqlzeichenfolge)


Jeder DDE-Dialog ist auf ein bestimmtes Thema, gewöhnlich eine Datendatei, aufgebaut und beschränkt sich im weiteren Verlauf auf die Datenelemente, die mit diesem Thema verbunden sind. Wenn Sie zum Beispiel in Microsoft Word für Windows Daten aus einer bestimmten Microsoft Access-Datenbank in Ihr Word für Windows-Dokument einfügen möchten, leiten Sie einen DDE-Dialog mit Microsoft Access ein, indem Sie einen Kanal öffnen und den Namen der Datenbank als Thema angeben. Sie können dann diesen Kanal dazu verwenden, Daten aus der Datenbank abzurufen. Nachdem Sie einmal einen DDE-Dialog eingeleitet haben, können Sie die Anweisung DDEExecute dazu verwenden, einen Befehl von der Client- an die Serveranwendung zu senden. Wenn Microsoft Access als DDE-Server verwendet wird, erkennt es die folgenden Befehle als gültig an:

- Den Namen eines Makros in der gerade geöffneten Datenbank. Jede Aktion, die Sie in Access Basic mit der Anweisung DoCmd ausführen können.
- Die Aktionen OpenDatabase und CloseDatabase, die nur für DDE-Operationen verwendet werden.

(Beispiele zur Anwendung dieser Aktionen finden Sie im nachstehenden Beispiel.)


Anmerkung: Wenn Sie eine Aktion in einem DDEExecute-Befehl angeben, folgen die Aktion und alle Argumente der Syntax von DoCmd und müssen in eckige Klammern ([]) eingeschlossen werden. Anwendungen, die DDE unterstützen, erkennen jedoch
nicht eingebaute Konstanten in DDE-Operationen. Außerdem müssen Zeichenfolgenargumente, deren Zeichenfolge ein Komma enthält, in Anführungszeichen (" ") eingeschlossen sein. Mit Ausnahme dieser Fälle sind Anführungszeichen nicht notwendig.

Das folgende Beispiel zeigt, wie Sie ein Word für Windows WordBasic-Makro erstellen können, das Microsoft Access als DDE-Server verwendet. (Damit dieses Beispiel funktioniert, muß Microsoft Access entweder geöffnet sein oder in der
Umgebungsvariablen path in Ihrer Datei AUTOEXEC.BAT aufgeführt werden.)

Sub MAIN
' Öffnen von NWIND.MDB unter Verwendung des Themas "System".
' Die Datenbank muß geöffnet sein, bevor andere DDE-Themen verwendet werden
können.
Kanal1 = DDEInitiate("MSAccess", "System")
DDEExecute Kanal1, "[OpenDatabase C:\ACCESS\BEISPIEL\NWIND.MDB]"
' Abrufen aller Daten aus der Abfrage "Kundenliste".
Kanal2 = DDEInitiate("MSAccess", "NWIND;QUERY Kundenliste")
MeineDaten$ = DDERequest$(Kanal2, "All")
DDETerminate Kanal2
' Schließen der Datenbank.
DDEExecute Kanal1, "[CloseDatabase]"
DDETerminate Kanal1
' Einfügen der Daten in eine Textdatei.
Open "DATEN.TXT" For Append As #1
Print #1, MeineDaten$
Close #1
End Sub

Informationen über die Verwendung von Microsoft Access als DDE-Client finden Sie im Kapitel 19, "Verwenden von Bildern, Diagrammen und anderen Objekten" des Microsoft Access Benutzerhandbuchs oder im Kapitel 13, "Dynamischer Datenaustausch", des Handbuchs Erstellen von Anwendungsprogrammen.


Das Thema "System":

Das Thema "System" ist ein Standardthema für alle auf Microsoft Windows basierenden Anwendungen.
Es liefert Informationen zu den von der Anwendung unterstützten Themen. Das Thema "System" unterstützt die folgenden Microsoft Access-Datenelemente:

Element   liefert

SysItems  Eine Liste von Elementen, die vom Thema "System" in Microsoft Access unterstützt werden.
Formats   Eine Liste von Formaten, die Microsoft Access in die Zwischenablage kopieren kann.
Status    "Busy" oder "Ready".
Topics    Eine Liste aller geöffneten Datenbanken.

Beispiel:

' Einleiten eines DDE-Dialogs mit Microsoft Access in einem WordBasic-Makro.
Kanal1 = DDEInitiate("MSAccess", "System")
' Anfordern einer Liste der vom Thema "System" unterstützten Themen.
Ergebnis$ = DDERequest$(Kanal1, "SysItems")
' Durchführen der Aktion Open Database zum Öffnen von NWIND.MDB.
DDEExecute Kanal1, "[OpenDatabase C:\ACCESS\BEISPIEL\NWIND.MDB]"

Das Thema Datenbank:

Das Thema Datenbank ist der Dateiname einer vorhandenen Datenbank. Sie können entweder einfach den Namen der Datenbank (NWIND) oder den vollständigen Pfad mit der Erweiterung .MDB (C:\ACCESS\BEISPIEL\NWIND.MDB) eingeben. Nachdem Sie den
DDE-Dialog mit der Datenbank eingeleitet haben, können Sie eine Liste aller Objekte in der Datenbank anfordern.


Anmerkung: Sie können DDE nicht zum Abfragen der Systemdatenbank (SYSTEM.MDA) verwenden.


Das Thema Datenbank unterstützt die folgenden Datenelemente:

Element     liefert

TableList   Eine Liste der Tabellen
QueryList   Eine Liste der Abfragen
FormList    Eine Liste der Formulare
ReportList  Eine Liste der Berichte
MacroList   Eine Liste der Makros
ModuleList  Eine Liste der Module

Beispiel:

' Einleiten eines DDE-Dialogs mit NWIND.MDB in einem WordBasic-Makro.
' Die Datenbank muß geöffnet sein.
Kanal2 = DDEInitiate("MSAccess", "Nwind")
' Anfordern einer Liste von Formularen in NWIND.MDB.
Ergebnis$ = DDERequest$(Kanal2, "FormList")
' Durchführen der Aktion OpenForm einschließlich seiner Argumente zum Öffnen des
Formulars "Personal".
DDEExecute Kanal2, "[OpenForm Personal,0,,,1,0]"

Die Themen TABLE Tabellenname, QUERY Abfragename und SQL Sqlzeichenfolge:

Diese Themen verwenden die folgende Syntax:

Datenbankname; TABLE Tabellenname
Datenbankname; QUERY Abfragename
Datenbankname; SQL [Sqlzeichenfolge]


Argument         Beschreibung

Datenbankname    Der Name der Datenbank, in der sich die Tabelle oder Abfrage befindet oder auf die das SQL-Argument zutrifft, gefolgt von einem Semikolon (;). Der Datenbankname kann entweder nur der Name der Datenbank (NWIND) oder auch der vollständige Pfad einschließlich der Erweiterung .MDB (C:\ACCESS\BEISPIEL\NWIND.MDB) sein.

Tabellenname     Der Name einer vorhandenen Tabelle.

Abfragename      Der Name einer vorhandenen Abfrage.

Sqlzeichenfolge  Eine gültige SQL-Anweisung, nicht länger als 255 Zeichen und mit einem Semikolon am Ende. Wenn Sie mehr als 255 Zeichen austauschen möchten, lassen Sie dieses Argument aus, und verwenden Sie statt dessen eine Reihe von
DDEPoke-Anweisungen, um eine DDE-Anweisung zu erstellen.

Der folgende WordBasic-Code verwendet z.B. DDEPoke, um eine SQL-Anweisung zu erstellen und dann das Resultat einer Abfrage anzufordern:

Kanal1 = DDEInitiate("MSAccess", "NWIND;SQL")
DDEPoke Kanal1, "SQLText", "SELECT *"
DDEPoke Kanal1, "SQLText", " FROM Bestellungen"
DDEPoke Kanal1, "SQLText", " WHERE [Frachtkosten] > 100;"
Res$ = DDERequest$(Kanal1, "NextRow")
DDETerminate Kanal1

Die folgende Tabelle listet die gültigen Elemente der Themen TABLE Tabellenname, QUERY Abfragename und SQL Sqlzeichenfolge auf:

Element       liefert

All           Alle Daten in der Tabelle, einschließlich Feldnamen.
Data          Alle Daten in der Tabelle, ohne Feldnamen.
FieldNames    Eine einzeilige Liste der Feldnamen.
FieldNames;T  Eine zweizeilige Liste von Feldnamen (erste Zeile) und deren Datentypen (zweite Zeile).

Es folgen die gelieferten gültigen Werte und die Datentypen, für die sie stehen:

 0 Ungültig
 1 Wahr/Falsch (nicht-Null)
 2 Byte ohne Vorzeichen (Byte)
 3 2-Byte-Ganzzahl mit Vorzeichen (Integer)
 4 4-Byte-Ganzzahl mit Vorzeichen (Long)
 5 8-Byte-Ganzzahl mit Vorzeichen (Currency)
 6 4-Byte Gleitkomma einfacher Genauigkeit (Single)
 7 8-Byte Gleitkomma doppelter Genauigkeit (Double)
 8 Datum/Zeit (Datum als Ganzzahl, Zeit als Dezimalwert)
 9 Binäre Daten, maximal 255 Bytes
10 ANSI-Text, Groß-und Kleinschreibung nicht beachtet, maximal 255 Bytes (Text)
11 Long binary (OLE-Objekt)
12 Long text (Memo)

NextRow     Die Daten in der nächsten Zeile der Tabelle oder Abfrage. Beim Öffnen eines Kanals liefert NextRow die Daten in der ersten Zeile. Ist die aktuelle Zeile die letzte Zeile und Sie führen NextRow aus, so schlägt die Anfrage fehl.
PrevRow     Die Daten der vorhergehenden Zeile der Tabelle oder Abfrage. Wenn PrevRow als erste Anfrage auf einem neuen Kanal angegeben wird, werden die Daten der letzten Zeile der Tabelle oder Abfrage geliefert. Wenn die erste Zeile die
aktuelle Zeile ist, so schlägt die Anfrage fehl.
FirstRow    Die Daten in der ersten Zeile einer Tabelle oder Abfrage.
LastRow     Die Daten in der letzten Zeile einer Tabelle oder Abfrage.
FieldCount  Die Anzahl der Felder in einer Tabelle oder Abfrage.
SQLText     Eine SQL-Anweisung, die für die Tabelle oder Abfrage steht. Für Tabellen gibt dieses Element eine SQL-Anweisung im Formular "SELECT * FROM Tabelle;" zurück.
SQLText;n   Eine SQL-Anweisung in Abschnitten von n Zeichen, die für die Tabelle oder Abfrage steht, wobei n eine Ganzzahl bis 255 sein kann. Angenommen, für eine Abfrage steht beispielsweise die folgende SQL-Anweisung:


SELECT * FROM Kunden;"

Das Element "SQLText;7" gibt die folgenden, mit Tabulatoren getrennten Abschnitte zurück:

"SELECT "
"* FROM "
"Kunden;"

Beispiel:

Sub MAIN
' Abrufen von Daten mit einem WordBasic-Makro aus der Tabelle
' "Kategorien", der Abfrage "Katalog" und der Tabelle
' "Bestellungen" in NWIND.MDB.
' Die Datenbank muß geöffnet sein.
Kanal1 = DDEInitiate("MSAccess", "NWIND;TABLE Kategorien")
Kanal2 = DDEInitiate("MSAccess", "NWIND;QUERY Katalog")
Kanal3 = DDEInitiate("MSAccess", "NWIND;SQL SELECT * FROM Bestellungen")
Ergebnis1$ = DDERequest$(Kanal1, "All")
Ergebnis2$ = DDERequest$(Kanal2, "FieldNames;T")
Ergebnis3$ = DDERequest$(Kanal3, "FieldNames;T")
DDETerminate Kanal1
DDETerminate Kanal2
DDETerminate Kanal3

' Einfügen der Daten in eine Textdatei.
Open "DATEN.TXT" For Append As #1
Print #1, Ergebnis1$
Print #1, Ergebnis2$
Print #1, Ergebnis3$
Close #1
End Sub


DDE: Beispiele   Quelle: microsoft   Datum: 05.2006   nach oben

DDE-Beispiele für die Zusammenarbeit (?) mit anderen Anwendungen:

EXCEL

DDEExecute

Beispiel

DDEExecute Kanalnummer, Befehl$

Sendet einen Befehl oder eine Reihe von Befehlen an eine Anwendung über einen DDE-Kanal (DDE = Dynamik Data Exchange/Dynamischer Datenaustausch).

Argument     Erklärung

Kanalnummer  Die Nummer des Kanals für den DDE-Dialog, die von der Funktion DDEInitiate() beim Öffnen des Kanals als Ergebnis geliefert wird. Entspricht die Kanalnummer keinem offenen Kanal, tritt ein Fehler auf.

Befehl$      Ein Befehl oder eine Reihe von Befehlen, die von der Server-Anwendung erkannt werden. Sie können auch das unter SendKeys beschriebene Format verwenden, um Tastenfolgen zu senden. Wenn die Server-Anwendung den angegebenen
Befehl nicht ausführen kann, tritt ein Fehler auf.

In Microsoft Excel und vielen anderen Anwendungen, die DDE unterstützen, sollte Befehl$ aus einer oder mehreren Anweisungen oder Funktionen der Makrosprache der Anwendung bestehen. In Microsoft Excel z.B. lautet die Makroanweisung zum Erstellen einer neuen Tabelle NEW(1). Um diesen Befehl über einen DDE-Kanal zu senden, verwenden Sie folgende Anweisung:

DDEExecute Kanal, "[NEW(1)]"

Beachten Sie, daß einige Anwendungen, darunter Microsoft Excel, voraussetzen, daß jeder über einen DDE-Kanal gesendete Befehl in eckigen Klammern steht. Mit einer einzigen DDEExecute-Anweisung können Sie auch mehrere Befehle senden.
Durch folgende Anweisung wird Microsoft Excel beispielsweise angewiesen, eine Tabelle zu öffnen und wieder zu schließen:

DDEExecute Kanal, "[NEW(1)][FILE.CLOSE(0)]"


Beachten Sie, daß sich zwischen den in eckigen Klammern stehenden Befehlen keine Leerstelle befindet. Eine Leerstelle würde einen Fehler hervorrufen. Die obige Anweisung ist gleichbedeutend mit den beiden folgenden Anweisungen:

DDEExecute Kanal, "[NEW(1)]"
DDEExecute Kanal, "[FILE.CLOSE(0)]"


Viele Beispiele erfordern Argumente in Form von Zeichenfolgen, die in Anführungszeichen stehen müssen. Da Anführungszeichen jedoch in WordBasic den Anfang und das Ende von Zeichenfolgen kennzeichnen, müssen Sie Anführungszeichen in Befehlszeichenfolgen mit Chr$(34) einfügen. Durch folgende Anweisung wird Microsoft Excel angewiesen, die Datei BUDGET.XLS zu öffnen:

DDEExecute Kanal, "[OPEN(" + Chr$(34) + "BUDGET.XLS" + Chr$(34) + ")]"


Beispiel:

Option Compare Database   'Verwenden der Datenbank-Sortierreihenfolge beim Vergleich von Zeichenfolgen.

Sub DDE_Excel_Beispiel ()

    ' Ein Request der SysItems liefert grundsätzlich folgende Themen :

    ' - SysItems        diese Aufzählung
    ' - Topics          System bei leerem Excel oder Arbeitsblätter-Auflistung des aktiven Dokumentes
    ' - Status          Ready
    ' - Formats         XLTable Microsoft Excel 5.0-Format      BIFF4   Biff3 SYLK    Wk1     CSV     Text    Rich Text Format        DIF     Bitmap  Picture
    ' - Selection       leer oder markierter Bereich
    ' - Protocols       StdFileEditing  Embedding
    ' - EditEnvItems    StdHostNames    StdTargetDevice StdDocDimensions

    ' - System          leer

    ' Das folgende Beispiel ist wie üblich ein bißchen Scheiße, da
    ' z.B. der Befehl [New(1)] im deutschsprachigem Excel-Basic
    ' überhaupt nicht zur Verfügung steht, aber als DDEExecute
    ' sehr wohl funktioniert.

    ' Hier wurde nicht nur ein unnötiges Deutsch-Basic erfunden,
    ' sondern obendrein Teile der Software parallel mehrsprachig
    ' gehalten:

    ' DDEExecute Kanal, "[New(1)]" in der Access-Doku entspricht
    ' DDEExecute Kanal, "[AktiveArbeitsmappe.TabellenblattListe.Hinzufügen]",
    ' und kann wahlweise benutzt werden, wobei pikanterweise die
    ' 10-mal so große, deutsche Anweisung über die 4-fache Anzahl
    ' von Parametern verfügt, von denen scheinbar nur 'Anzahl' in
    ' beiden Versionen auftaucht.

    ' Syntax :  Objekt.Hinzufügen(Vor; Nachfolgend; Anzahl; Typ)

    ' So auch DDEExecute Kanal, "[Select(""R1C1:R1C10"")][New(2,2)]", das
    ' hier in diesem Beispiel einen Bereich markiert und ein Standard-Diagramm
    ' in einer neuen Mappe erstellt. Die Zusammenhänge dieser Anweisung bleiben
    ' im Dunkeln.

    ' Allgemeine Kurz-Info :

    ' - DDEInitiate(Name,Thema) : Name der Anwendung, meist Name der ausführbaren Datei; gültiges Thema; Rückgabe Kanalnummer
    ' - DDEExecute Kanalnummer, Befehl : Kanalnummer; zu sendender Befehl; keine Rückgabe
    ' - DDERequest(Kanalnummer, Element) : Kanalnummer; Informations-Element (häufig SysItems, Topics)
    ' - DDEPoke Kanalnummer, Element, Daten
    ' - DDETerminate Kanalnummer
    ' - DDETerminateAll

    ' Es gibt eine obskure DDE-Funktion, die als Steuerelement-
    ' inhalt von Formular-Elementen benutzt werden kann, wie auch
    ' das eigentlich interessante DDESenden, deren englisches Äqui-
    ' valent scheinbar DDEPoke ist.

    ' ***********************************************************

    ' Dieses Beispiel erzeugt eine DDE-Verknüpfung zu Microsoft Excel, stellt
    ' einige Werte in die erste Zeile einer neuen Tabelle und zeichnet die
    ' Werte in ein Diagramm. Zuerst sendet DDEExecute Microsoft Excel den
    ' Befehl zum Öffnen einer neuen Tabelle. Dann startet DDEInit
    ' (DDEInitiate) einen DDE-Dialog und DDEPoke sendet die Daten, die
    ' grafisch dargestellt werden sollen. DDEAnfrage (DDERequest) fragt
    ' Microsoft Excel nach dem Namen der neu erstellten Tabelle.
    ' DDETerminate beendet dann die DDE-Verknüpfung mit Microsoft Excel, und
    ' DDETerminateAll beendet zuletzt alle aktiven DDE-Verknüpfungen.

    On Error Resume Next    ' Fehlerbehandlungsroutine einrichten.

    Dim Kanal, TabName, I, Themen               ' Variablen deklarieren.

    Kanal = DDEInitiate("Excel", "System")      ' Verknüpfung zu Excel herstellen.

    If Err Then                                 ' Wenn Fehler, war Excel nicht gestartet.
       MsgBox Error
       Err = 0                                  ' Fehlercode zurücksetzen und Microsoft Excel starten.
       I = Shell("c:\windows\excel5\Excel", 1)
       If Err Then Exit Sub                     ' Bei erneutem Fehler Programmende.
       Kanal = DDEInitiate("Excel", "System")   ' Verknüpfung zu Excel herstellen.
    End If

    DDEExecute Kanal, "[New(1)]"                ' Neue Tabelle anlegen.
'    DDEExecute Kanal, "[AktiveArbeitsmappe.TabellenblattListe.Hinzufügen]"

    Themen = DDERequest(Kanal, "Selection")     ' Themenliste und Tabellenname erhalten.
    TabName = Left(Themen, InStr(1, Themen, "!") - 1)
    DDETerminate Kanal                          ' DDE-Verknüpfung beenden.
    Kanal = DDEInitiate("Excel", TabName)       ' Verknüpfung zu neuer Tabelle erstellen.

    For I = 1 To 10                             ' Einige Werte in erste Zeile eingeben.
        DDEPoke Kanal, "Z1S" & I, I
    Next I

    DDEExecute Kanal, "[Select(""R1C1:R1C10"")][New(2,2)]"  ' Diagramm erstellen.

exit_DDE_Excel_Beispiel:

    DDETerminateAll                             ' Alle Verknüpfungen beenden.

End Sub



Sub DDE_Word_Beispiel ()

    Dim Kanal, TabName, I, Themen, s As String

    Kanal = DDEInitiate("Winword", "System")' Verknüpfung zu Word herstellen.

    If Err Then                             ' Fehler, wenn Word nicht gestartet.
       Err = 0                              ' Fehlercode zurücksetzen und Microsoft Word starten.
       On Error GoTo err_DDE_Word_Beispiel
       I = Shell("c:\windows\winword6\winword", 1)
       Kanal = DDEInitiate("Winword", "System")  ' Verknüpfung zu Word herstellen.
    End If

    On Error GoTo err_DDE_Word_Beispiel

    ' Anfordern einer Liste der vom Thema "System" unterstützten Themen.

    Ergebnis$ = DDERequest(Kanal, "Topics")

    If InStr(Ergebnis$, "C:\WINDOWS\WINWORD6\TEMPO.DOC") > 0 Then
       MsgBox "Datei C:\WINDOWS\WINWORD6\TEMPO.DOC ist geöffnet"
       s = "[MarkierungErweitern " & """" & "U" & """" & "]"
       DDEExecute Kanal, s    ' Neue Tabelle anlegen.
       ' Themen = DDERequest(Kanal, "Selection") ' Themenliste und Tabellenname erhalten.
       'MsgBox Themen
    End If


exit_DDE_Word_Beispiel:

    DDETerminateAll
    Exit Sub


err_DDE_Word_Beispiel:

    MsgBox Error, , "DDE_Word_Beispiel"
    Resume exit_DDE_Word_Beispiel

End Sub


Grafiken & Dimensionen   Quelle: dmt   Datum: 05.2005   nach oben

Zum Thema Graficken (kein Tippfehler, hat auch nichts mit der Rechtschreibreform zu tun) gehören leider auch TWIPS, PUNKT und Pixel.

Kombiniert man die wie so oft wenig ergiebige Dokumentation mit meinen endlosen Bemühungen, so ergibt sich folgendes Bild:

Access-Basic gibt seine Koordinaten in sog. Twips an. Die Information, daß das Verhältnis von Twips zu Punkten 20:1 beträgt, ist quasi wertlos, da diese Punkte nichts mit den Pixeln zu tun haben, die die wahre Beschreibung der Positionen der Bildschirmpunkte beinhalten. Zu beachten ist daher das Verhältnis Twips zu Pixel 15:1.


Lizenz-Fehler   Quelle: dmt   Datum: 03.2004   nach oben

Apropos Office97 und Access97:

Sehr beliebt ist der Lizenz-Fehler von Access97, öfter auch mal im Zusammenspiel mit vorangegangenen Access2000-Installationen.

Behebbar durch Einspielen folgender reg-Datei (z.B. "acc_fuck.reg"):

REGEDIT4

[HKEY_CLASSES_ROOT\Licenses]
@="Licensing: Copying the keys may be a violation of established copyrights."

[HKEY_CLASSES_ROOT\Licenses\8CC49940-3146-11CF-97A1-00AA00424A9F]

[HKEY_CLASSES_ROOT\Licenses\8CC49940-3146-11CF-97A1-00AA00424A9F\Retail]
@="yubcdcprktpjtapmmfdacmupasbhscddncgp"

[HKEY_CLASSES_ROOT\Licenses\8B7FE740-50AC-101B-A3C9-08002B2F49FB]
@="mjgcqcejfchcijecpdhckcdjqigdejfccjri"

[HKEY_CLASSES_ROOT\Licenses\F4FC596D-DFFE-11CF-9551-00AA00A3DC45]
@="mbmabptebkjcdlgtjmskjwtsdhjbmkmwtrak"


Objekte manipulieren   Quelle: dmt   Datum: 10.2004   nach oben

DATENBANKOBJEKTE manipulieren:

Es ist wiederholt vorgekommen, per Code auf in Access enthaltene Datenbankobjekte zuzugreifen. Jedesmal mußte dies mit enervösem Zeitaufwand bezahlt werden. Deshalb hier eine Routine, die als Parameter Name und Container-Typ eines Datenbankobjektes erwartet und eine später benutzte Variable iObjectType in Abhängigkeit von Containers-Typ zuweist.
Es werden die Document-Objekte des angegebenen Containertyps durchlaufen. Bei einem Treffer wird das Objekt direkt löschenderweise angesprochen.

So kann z.B erreicht werden, daß ein temporäres Objekt unter einem Standardnamen angelegt werden kann, und zwar unabhängig davon, ob bereits ein gleichnamiger Vorgänger besteht oder nicht. Weitere Fallunterscheidungen oder Fehlerbehandler in der eigentlichen Routine sind nicht nötig.

Function DeleteObject (sName As String, iObjectType As Integer) As Integer

    On Error GoTo err_DeleteObject

    Dim DB As Database

    Set DB = DBEngine.Workspaces(0).Databases(0)

    DoCmd DeleteObject iObjectType, sName

    DeleteObject = True

    Exit Function


err_DeleteObject:

    If Err = 2487 Then
       Beep
       MsgBox "Der angegebene Objekttyp " & iObjectType & " ist ungültig !", 16,
"Tools/DeleteObject"
    ElseIf Err = 3011 Then                ' wenn Objekt nicht existiert
       DeleteObject = True
    Else
       Fehler "Tools / DeleteObject"
    End If

    Exit Function

End Function

Die Funktion verhält sich ruhig, wenn das angegebene Objekt nicht (mehr) existiert und kann so auch sicherheitshalber aufgerufen werden.

* * * *

Auch das Umbennen von Datenbankobjekten (sollte eher selten vorkommen) ist an sich gar nicht so schwierig:

Private Function Rename_Query (vQ As Variant) As Integer

    On Error GoTo err_Rename_Query

    Dim DB As Database, QD As QueryDef, s As String

    Set DB = DBEngine.Workspaces(0).Databases(0)
    Set QD = DB.QueryDefs(vQ)

    s = InputBox$("Geben Sie den neuen Namen ein:", vQ, "dkl_")

    If s <> "" Then
       QD.Name = s
       Rename_Query = True
    End If

    Exit Function


err_Rename_Query:

    Fehler "Rename_Query"
    Exit Function

End Function

Mit ein wenig Aufwand kann das sogar auf andere Objektarten bzw. allgemein ausgeweitet werden.

* * * *

Ein nettes Info-Tool GENERAL_INFO, aufgerufen über die Funktion ShowContextInfo kann nicht nur formularspezifische Infos in einer Tabelle anlegen, sondern über Application.CurrentObjectType- und Application.CurrentObjectName-Übergabe sogar das Aktivsein allgemeiner Access-Objekte sowie bei Bedarf deren Zustand über die Funktion SysCmd(SYSCMD_GETOBJECTSTATE) als

OBJSTATE_OPEN   Geöffnet
OBJSTATE_NEW    Neu
OBJSTATE_DIRTY  Geändert

erkennen.


Objekte, Existenz prüfen   Quelle: dmt   Datum: 03.2004   nach oben

Ja, so ist das eben; also bevor man sich auf die GETOBJECTSTATE-Variante von SysCmd verläßt, um auch geschlossene Datenbank-Objekte auf Vorhandensein zu prüfen, sollte der Zuverlässigkeit zuliebe folgendes bemüht werden:

EXISTSOBJECT:

Function ExistsObject (iArt As Integer, sName As String) As Integer

    On Error GoTo err_ExistsObject

    ' *** Existiert das angegebene Objekt in der Datenbank ? ****

    Dim DB As Database
    Dim C As Container
    Dim D As Document
    Dim sContainerName As String

    Select Case iArt
        Case A_TABLE:   sContainerName = "Tables"
        Case A_QUERY:   sContainerName = "Tables"
        Case A_FORM:    sContainerName = "Forms"
        Case A_REPORT:  sContainerName = "Reports"
        Case A_MACRO:   sContainerName = "Scripts"
        Case A_MODULE:  sContainerName = "Modules"
        Case Else:      Beep
                        MsgBox "Ungültiger Parameter iArt:'" & iArt & "'.", 16, "ExistsObject"
                        Exit Function
    End Select

    Set DB = DBEngine.Workspaces(0).Databases(0)
    Set C = DB.Containers(sContainerName)
    Set D = C.Documents(sName)

    ExistsObject = True


exit_ExistsObject:

    Set D = Nothing
    Set C = Nothing
    Set DB = Nothing

    Exit Function


err_ExistsObject:

    If Err = 3265 Then
       ' nix
    Else
       Fehler "ExistsObject"
    End If

    Resume exit_ExistsObject

End Function

Übergeben werden die wohl bekannten Parameter a'la A_FORM und der Name des obskuren Objektes der Begierde und als Antwortwert gibt es ein vertrautes 0 oder -1.

Und zur Auswertung und zum folgerichtigen Reagieren kann dann so etwas bemüht werden:

If ExistsObject(A_FORM, s) Then
          DoCmd OpenForm s
       ElseIf ExistsObject(A_TABLE, s) Then
          DoCmd OpenTable A_TABLE, s
       Else
          Beep
          MsgBox "Ungültiger Wert '" & s & "' in pb_Ok_Click", 16, "Chefdaten-Freischaltung"
       End If
End if

Yes !


Objekte, löschen, austauschen   Quelle: dmt   Datum: 03.2004   nach oben

Löschen z.B. eines Modules, aber nur in der eigenen Datenbank:

DoCmd.DeleteObject [Objekttyp, Objektname]

Die Methode DeleteObject verwendet die folgenden Argumente:

Argument Beschreibung

Objekttyp Eine der folgenden eingebauten Konstanten:
acDefault (Standardwert)
acForm
acMacro
acModule
acQuery
acReport
acTable

Interessanter ist DoCmd.CopyObject, das Datenbank-übergreifend arbeitet, bzw. das Arbeiten mit DoCmd.TransferDatabase.

Evtl. kann mit den Methoden des Modul-Objektes in Access97 Quelltext eines Modules entfernt und danach durch neuen ersetzt werden.


odbc   Quelle: dmt   Datum: 03.2004   nach oben

ODBC

braucht nicht unbedingt DDE, um einen zu beschäftigen.

So kann es z.B. vorkommen, daß Access per odbc eingebundene Tabellendaten nicht bearbeiten kann, obwohl diese Daten zum Teil sogar durch eine Access-Anfügeabfrage in diese z.B. MySQL-Tabelle eingefügt wurden !

In mindestens diesem Fall hing das damit zusammen, daß Access beim Einbinden nach einem Feld zur eindeutigen Identifizierung der Datensätze frug. Wird der Dialog abgebrochen, scheint alles klar zu sein, nur daß halt die Datensatzgruppe schreib-geschützt ist. Bestimmt man ein halbwegs sinnvolles Feld als unique Kriterium, dann klappts auch mit dem Aktualisieren.


Stammdaten   Quelle: dmt   Datum: 03.2004   nach oben

STAMMDATEN und ihre Verwaltung:

Über Maus- und Tastatur-Aktionen öffne ich für Kombinationsfelder, die hinterlegte Werte anbieten, gerne die entsprechenden Stammdatenformulare, die bei Bedarf individuell programmiert werden können. Bisher war es aber immer ein
Problem, bei evtl. Änderungen an den Stammdatensätzen eine Aktualisierung des Kombinationsfeldes vorzunehmen. Dies führte zu so rührenden Versuchen mit unsichtbaren Unterformularen und lauter so Sachen.

Eine vernünftige Lösung sieht wie folgt aus:

Ein Standardformular mit Standard-Eigenschaften (alle Versuche, Daten in Listendarstellung innerhalb eines gebundenen Formulares darzustellen, sind mit unzumutbarem Aufwand verbunden oder gar nicht möglich).

Für das Ereignis Form_Current gilt z.Bsp.:

    If Me.CurrentView = 1 Then
       DoCmd MoveSize 3000, 1700, 4200, 1430
    Else
       DoCmd MoveSize 3000, 500, 4200, 5000
    End If

oder noch besser siehe CenterForm.

Hier wird für Einzel- sowie Listenansicht die Größe des Formulares getrennt eingestellt (it's not a trick, it's me). Wenn man das noch mit einer gesetzten Variable LastView vergleicht, werden die Anweisungen nur nach einem
Ansichtswechsel abgearbeitet.

In den Ereignissen De/Activate wird eine Standard-Symbolleiste (sehr geil in der LaserJob-Code-Datenbank LIB_LJ.MDA) ein- oder ausgeblendet. Beim Umschalten zu Formularen, die dieselbe Symbolleiste benutzen, ist nicht einmal ein Flackern zu bemerken.

Aufgerufen werden diese Formulare über eine Anweisung a'la 'ZeigeStammdaten xyz':

Sub ZeigeStammdaten (sFormular As String, sFeld As String, iAnsicht As Integer)

    On Error GoTo err_ZeigeStammdaten

    Dim v As Variant

    v = Screen.ActiveControl

    Application.Echo False

    DoCmd OpenForm sFormular, iAnsicht

    Forms(sFormular)(sFeld).SetFocus

    If Not IsNull(v) Then DoCmd FindRecord v



exit_ZeigeStammdaten:

    Application.Echo True
    Exit Sub


err_ZeigeStammdaten:

    If Err = 2102 Then
       DoCmd OpenTable sFormular
       Resume Next
    Else
       Fehler "Modul ZeigeStammdaten"
       Resume exit_ZeigeStammdaten
    End If

End Sub

In der neuesten Version mit Ansichtsparameter und und abgeschaltetem Applikations-Echo, um ein Aufflackern des ersten Formular-Datensatzes zu verhindern.

Hier werden sogar NULL-Werte im Mutterformular abgefangen und das ALLERGRÖßTE überhaupt besteht in dem Geschenk des Himmels, daß u.U. ALLE Manipulationen an Stammdatensätzen augenblicklich in den Kombinationsfeldern des Mutterformulares
zur Verfügung stehen, auch wenn für die betroffenen Tabellen KEINE referentiellen Beziehungen vorgesehen sind. Obendrein kann bei nicht vorhandenem Formular eine Tabelle geöffnet werden.

Unklar bleibt, warum ich mir beim Aktualisieren solcher Daten innerhalb eingebetteter Unter-formulare einen runterholen mußte, aber wie dem auch sei, dies ist einer von den ganz seltenen Augenblicken, in denen ich allen Ernstes glaube, daß man mit MS-ACCESS (2.0 !!!) Anwendungen entwickeln kann.

* * * *

Auch kommt es vor, daß Unterformulare sich auf Daten in sog. Zuordnungstabellen beziehen, in denen lediglich Idents und Namen stehen. Wenn sich aus irgendeinem Grund ungültige Einträge dorthin verirrt haben, ist es durchaus sinnvoll, vom Unterformular aus die Zuordnungstabelle zu öffnen und zu dem entsprechenden Datensatz zu wechseln:

Sub Zeige_Zuordnungsdaten (sTabelle As String, sTabellenfeld As String, vIdent As Variant, vFormularfeld As Variant)

    On Error GoTo err_Zeige_Zuordnungsdaten

    Meldung "synchronisiere Daten ..."
    DoCmd OpenTable sTabelle
    DoCmd Restore
    DoCmd FindRecord vIdent
    Meldung ""

    DoCmd SelectObject A_TABLE, sTabelle
    DoCmd GoToControl sTabellenfeld
    DoCmd FindRecord vFormularfeld, , , , , , False

    Exit Sub


err_Zeige_Zuordnungsdaten:

    Fehler "Zeige_Zuordnungsdaten"
    Exit Sub

End Sub

Für das zu bedienende Steuerelement gilt:

Sub Name_DblClick (Cancel As Integer)

    Zeige_Zuordnungsdaten "Mitwirkende", "Name", Me!Ident, Me!Name

End Sub

und natürlich das bewährte

Sub Name_KeyDown (KeyCode As Integer, Shift As Integer)

    If KeyCode = 32 And Shift = 2 Then
       Name_DblClick (0)
    End If

End Sub

und der freundliche Statuszeilenhinweis:

Name der Person: <Doppelklick/Strg+Leer> öffnet Stammdaten

Das Ganze läßt sich bei 'Untertabellen', die typische Unterformular-Zuweisungsdaten enthalten, noch erweitern. Neben der bewährten ZeigeStammdaten-Lösung kann per rechte Maustaste bzw. <Strg+Shift+Leer> auch die Zuweisungs-Untertabelle geöffnet werden.

Feld-Statuszeile:

Suchname der Institution: <Doppelklick/Strg+Leer> Stammdaten <r.Maustaste/Strg+Shift+Leer> Zuweisungstabelle

Im Zuge allgemeiner Kapselungen steht alles in einer eigenen Routine:

Sub Suchname_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single)

    If Button = RIGHT_BUTTON Then
       Zeige_Projekte_Institutionen
    End If

End Sub


Sub Suchname_KeyDown (KeyCode As Integer, Shift As Integer)

    If KeyCode = 32 And Shift = 2 Then
       Suchname_DblClick (0)
    ElseIf KeyCode = 32 And Shift = 3 Then
       Zeige_Projekte_Institutionen
    End If

End Sub


Private Sub Zeige_Projekte_Institutionen ()

    Zeige_Zuordnungsdaten "Projekte_Institutionen", "Suchname", Me!Ident, Me!Suchname

End Sub

Zu der äußerst lästigen Bitmasken-Auswertung bez. der diversen 'Shift'-Tastaturzustände muß gesagt werden, daß ich in diesem Fall die explizite Nenung bevorzuge:

1 Control
2 Shift
4 Alt

Daraus ergeben sich dann alle anderen Kombinationen:

3 Control und Shift
usw.


Statuszeile   Quelle: dmt   Datum: 08.2010   nach oben

Allgemeines zur Statuszeile:

Für eine durchgehende Anwenderinformation innerhalb einer längeren Code-Routine mittels der Statuszeile empfiehlt sich ein häufiges Setzen mit

    x = SysCmd(SYSCMD_SETSTATUS, "Sind die Einstellungen korrekt ?"),

damit auch Code-Unterbrechungen durch MessageBoxen nicht mit älteren Beschreibungen versehen werden.

Falls in dieser Routine 'Exit Sub/Function'-Anweisungen vorgesehen sind, sollte stattdessen ein Goto 'Exit_Routinen_Name' erfolgen, wo dann die Statuszeile mit

    x = SysCmd(SYSCMD_CLEARSTATUS)

zurückgesetzt wird, damit zugewiesene Texte nicht durch den Rest der Anwendung geistern.

Sehr ominös ist die Tatsache, daß, wenn aus einem gebundenem Menü-Formular normale Formulare aufgerufen und diese wieder geschlossen werden, die StatuszeilenText-Eigenschaften der Schaltflächen des Menü-Formulares alle
verlorengegangen zu sein scheinen !
Bisher konnte noch keine Abhilfe geschaffen werden. Allerdings könnte man das Problem dadurch beheben, daß ein gebundenes Menüformular in ein normales Windows-Fenster umgewandelt wird.

Bei einer stringenten Benutzer-Fenster-Führung muß man entweder

- alle nicht benötigten Formulare ausblenden, um sich bei mehreren Fenstern nicht zu verklicken, oder gebundene Formulare schließen und wieder öffnen, anstatt sie auszublenden,
- oder eben mit dem Problem leben,
- oder Contra-Programmierung mit heftigen FormX.SetFocus-Anweisungen.

****

Statuszeile zurücksetzen per SYSCMD_CLEARSTATUS:

Hier tritt ein Fehler auf, wenn SysCmd(SYSCMD_CLEARSTATUS) nochmals ausgeführt wird, ohne daß der Statustext inzwischen neu gesetzt wurde.

Wenn sich das partout nicht umgehen läßt, kann eine formular- oder anwendungsglobale Variable definiert werden Global gStatusHasBeenCleared As Integer, die im betreffenden Code z.B. so behandelt wird:

If xOk = True And yOk = True Then
   gStatusHasBeenCleared = False
   v = SysCmdSYSCMD_SETSTATUS, "..."
ElseIf gStatusHasBeenCleared = False Then
   v = SysCmdSYSCMD_CLEARSTATUS
   gStatusHasBeenCleared = True
End If


Systemobjekte   Quelle: dmt   Datum: 10.2004   nach oben

SYSTEMOBJEKTE:

sind in der Tabelle MySysObjects hinterlegt und können per Code oder SQL abgefragt werden.

Beispiele:

Code:

If (TD.Attributes And DB_SYSTEMOBJECT) then
prüft, ob ein angegebenes TableDef-Objekt als Systemtabelle definiert wurde.

SQL:

SELECT Name, Type, Flags FROM MSysObjects ORDER BY Type, Name;
listet alle Einträge in der Systemobjekt-Tabelle

* * * *

Systemobjekte erkennen und unterscheiden:

Die aufgelisteten Werte sind Zuordnungswerte aus der MSysObjects-Tabelle und haben natürlich überhaupt gar nichts mit den System-weit deklarierten Konstanten wie A_TABLE zu tun (Microdoofe Schwachmaten!).

Weitere, detaillierte Beispiele z.B. zur Unterscheidung des Flag-Merkmales finden sich bei "Struktur auswerten".

Objekt      Wert    Zusatz
------------------------------------------------------
Tabellen    1       interne
unbekannt   2       Name: MSysDb
unbekannt   3       Objektarten: Forms, Reports etc.
Abfragen    5
Tabellen    6       eingebundene
Referenzen  8       z.B. Beziehungen zwischen Tabellen
unbekannt  -32758   evtl. Benutzer
Module     -32761
Berichte   -32764
Makros     -32766
Formulare  -32768

Eine informative Umsetzung in SQL sieht dann so aus:

SELECT Name, Type, Flags,
IIF(Type=1, "Tabelle (intern)",
IIF(Type=2, "unbekannt",
IIF(Type=3, "Objektart",
IIF(Type=5, "Abfrage",
IIF(Type=6, "Tabelle (eingebunden)",
IIF(Type=8, "Referenz",
IIF(Type=-32758, "evtl. Benutzer",
IIF(Type=-32761, "Modul",
IIF(Type=-32764, "Bericht",
IIF(Type=-32766, "Makro",
IIF(Type=-32768, "Formular",
))))))))))) AS Art
FROM MSysObjects
ORDER BY Type, Name;

* * * *

Das Problem, in einer Access-Datenbank befindliche Tabellen aufzulisten, kann auf verschiedene Arten gelöst werden:

Einmal durch das wuchtige Zeige_Tabellen aus frühen Tagen, dann durch ein simples Abfragen der MSysObjects-Tabelle oder einem Durchnudeln der DBEngine.Objekte.

Me!lst_Objekte.RowSource = "SELECT Name FROM MSysObjects WHERE Type=" & Wert & " ORDER BY Name;"

weist einem Listenfeld die Datenherkunft zu, die alle internen Tabellen auflistet, wenn der Wert '1' übergeben wurde.

Zusätzlich enthält das Feld Flags weitere Informationen, z.B, ob eine Abfrage eine Aktualisierungsabfrage oder eine Tabelle eine Systemtabelle oder eine unsichtbare, temporäre Tabelle ist.

Function Zeige_Tabellen (SO As Integer, Feld As Integer)

    ' Wenn SO <> 0 und der obskur 'logische' Vergleich von
    ' If (TD.Attributes And DB_SYSTEMOBJECT) then
    ' durchgeht, werden auch die Systemtabellen aufgelistet.
    ' Da aber in TableDef.Attributes auch Flags enthalten sind, die andere als nur
    ' Systemtabellen-Zeiger enthalten, kann davon ausgegangen werden, daß bei
    ' SO = 0 evtl. auch Nicht-Systemobjekte unterschlagen werden.

    Dim s1 As String * 24    ' Variable fester Länge deklarieren.
    Dim s2 As String * 8     ' Variable fester Länge deklarieren.
    Dim s3 As String * 19    ' Variable fester Länge deklarieren.
    Dim s4 As String * 19    ' Variable fester Länge deklarieren.

    Dim DB As Database
    Dim TD As TableDef

    Dim i As Integer
    Dim i1 As Integer
    Dim i2 As Integer
    Dim z As Integer
    Dim s As String

    Dim Wert1 As String
    Dim Wert2 As String

    Dim W10 As String
    Dim W11 As String
    Dim W12 As String
    Dim W13 As String

    z% = 0

    s$ = InputBox$("Geben Sie den Namen der Datenbank-Datei an:", "Zeige Tabellen aus Datenbank", "dmt.mdb")

    If s$ = "" Then Exit Function

    Set DB = DBEngine.Workspaces(0).OpenDatabase(s$, True, True)

    ReDim Tabellen(3, DB.TableDefs.Count - 1)

    For i% = 0 To DB.TableDefs.Count - 1

        Set TD = DB.TableDefs(i%)

        If (TD.Attributes And DB_SYSTEMOBJECT) And (SO = 0) Then GoTo ZEIGE_TABELLEN_WEITER

        s1 = TD.Name
        RSet s2 = Format$(TD.RecordCount, "#,0")
        RSet s3 = TD.DateCreated
        RSet s4 = TD.LastUpdated

        Tabellen(0, z%) = s1               ' strukturierte Strings
        Tabellen(1, z%) = s2               ' an Array übergeben,
        Tabellen(2, z%) = s3               ' um daran weiter zu
        Tabellen(3, z%) = s4               ' operieren.

        z% = z% + 1                        ' Zähler für Aufrücken in Array

ZEIGE_TABELLEN_WEITER:

    Next i%

    ' ************** Sortieren der Arraydaten *******************

    If Feld = -1 Then GoTo ZEIGE_TABELLEN_AUSGABE

    For i1% = 0 To z%                            ' Das Array sortieren:
        For i2% = i1% + 1 To z% - 1              ' Eine Schleife zählt vom
            Wert1 = Tabellen(Feld, i1%)
            Wert2 = Tabellen(Feld, i2%)
            W10 = Tabellen(0, i1%)
            W11 = Tabellen(1, i1%)
            W12 = Tabellen(2, i1%)
            W13 = Tabellen(3, i1%)
            If Wert2 < Wert1 Then
                Tabellen(0, i1%) = Tabellen(0, i2%)
                Tabellen(1, i1%) = Tabellen(1, i2%)
                Tabellen(2, i1%) = Tabellen(2, i2%)
                Tabellen(3, i1%) = Tabellen(3, i2%)
                Tabellen(0, i2%) = W10
                Tabellen(1, i2%) = W11
                Tabellen(2, i2%) = W12
                Tabellen(3, i2%) = W13
            End If
        Next i2%
    Next i1%

    ' ********************** Ausgabe ****************************

ZEIGE_TABELLEN_AUSGABE:

    Debug.Print
    Debug.Print Str$(DB.TableDefs.Count) & " Tabellen in '" & s$ & "':" &
Str$(z%) & "normale und" & Str$(i% - z%) & " Systemtabellen"
    Debug.Print
    Debug.Print " Tabellenname              Anz.DS    erstellt am
letzte Änderung """
    Debug.Print

    For i% = 0 To z%
        Debug.Print " " & Tabellen(0, i%) & Tabellen(1, i%) & "    " &
Tabellen(2, i%) & " " & Tabellen(3, i%)
    Next i%

    ' **********************************************************

    DB.Close

End Function

* * * *

Eigene SYSTEMOBJEKTE erzeugen / unsichtbare bzw. versteckte Tabellen:

Es kann durchaus sinnvoll sein, z. B. eine System-Tabelle zum Hinterlegen interner Informationen zu benutzen. Wie üblich schlugen sämtliche Versuche, so etwas auf der Basis der Dokumentation zu lösen, fehl. Zwar wird versprochen, daß
Tabellen-Attribute beschreibbar sind, wenn das Tabellen-Objekt noch nicht der Auflistung vorhandener Tabellen hinzugefügt wurde, aber die Werte DB_SYSTEMOBJECT und DB_HIDDENOBJECT wollten sich partout nicht zuweisen lassen.
Unklar bleibt somit auch, wie dann sogenannte unsichtbare TObjekte zur temporären Verwendung erzeugt werden sollen.

Die Lösung war ebenso einfach wie unkonventionell. Bei angezeigten Systemobjekten wird z.B. die Tabelle "MSySToolbars" (für die übrigens KEINE Entwurs-Änderungsrechte bestehen) kopiert und wieder eingefügt. Das eingefügte Tabellenobjekt verfügt nun über den begehrten Systemstatus, ist aber witzigerweise NICHT mehr entwurfsgeschützt und kann daher nach Herzenslust bearbeitet werden.

Beim Erstellen von Abfragen, Formularen oder Berichten steht diese Tabelle wie die echten Systemobjekte nicht mehr in den diversen Klappfenstern oder Assistenten zur Verfügung. Wird jedoch der Name dieser Systemtabelle explizit angegeben (z.B. auch im Code), kann das Objekt trotzdem angesprochen werden.

* * * *

MAKRO / SYSTEMOBJEKTE / MSysMacros / Systemtabellen:

Eine Reihe von Access-Internas (Makros, relationale Referentiellitäten etc.) werden in Systemtabellen verwaltet. Klar, daß auch diese Sachen nicht in der mitgelieferten Dokumentation behandelt werden.

In MSysMacro stehen die Einträge für Makros sowie deren Elemente. Es ist zwar ein Mehrfelder-Index hinterlegt (Scriptname und Label, jeweils aufsteigend sortiert), die Daten erscheinen aber ohne eindeutiges ID, das die Bezüge untereinander herstellen könnte, untereinander in der Reihenfolge der Anlage der Makros (mit vergebenem Scriptname) und dann ohne Scriptname in der Reihenfolge der Makroelemente, wie sie in der Entwurfsansicht des Makros zu sehen sind. Die Einstellungen der Makro-Elemente-Eigenschaften werden in diesen Folge-Datensätzen in diversen 'Argumentx'-Feldern gespeichert.

So enthält ein Versuchsmakro 'Drucken_Aktion' das Element 'Drucken' mit diversen Eigenschaften-Einstellungen. In der Tabelle MSysMacros erscheinen dann am unteren Ende der Datensatzliste (zumindest optisch beim Öffnen der Tabelle) zwei Datensätze: Der erste mit Scriptname=Makroname und der folgende ohne Scriptname mit den Element-informationen, z.B. enthält das Argument5 für ein Element mit der Aktion 'Drucken' den Wert der Aktions-Eigenschaft 'Exemplare'. Da mit diesen Informationen nur korrekt angelegte Objekte ausgelesen und im schlimmsten Falle bearbeitet werden sollen, können die nicht nachvollziehbaren korrespondierenden Einträge in MSysObjects hoffentlich vernachlässigt werden.


Timer   Quelle: dmt   Datum: 03.2004   nach oben

TIMER:

Das Aktivieren eines Timers im Hauptformular für Termin-Erinnerungen kann zu seltsamen Effekten führen, wenn u.a. eine Dos-Task läuft. In diesem Fall kann es vorkommen, daß mittels <Alt + Tab> nicht per Task-Manager umgeschaltet werden kann.

Unter Windows-Applikationen scheint es keine Probleme zu geben.

Kompromiß: In Vorgabeeinstellungen sollte angeboten werden, die Timer-Option automatisch an- oder auszuschalten.


Tuning   Quelle: dmt   Datum: 03.2004   nach oben

BESCHLEUNIGEN:

Datei MSACC20.INI

Abschnitt [ISAM]
MaxBufferSizes=4096
ReadAheadPages=5

Abschnitt [Options]
DebugLibraries=False

Abschnitt [Libraries]
so weit abspecken wie möglich

Das großzügige Abspecken geladener Assistenten etc. (per Hand oder Add-In-Manager) reduziert Arbeitsspeicherbedarf und Ladezeiten.

Es gilt ferner natürlich alles, was Windows beschleunigt:

z.B.:

- nicht zu viele Programme gleichzeitig.
- Hintergrundbilder
- aufwendige Bildschirmschoner
- virtueller Speicher incl. RAM min. 25 MB !
- Auslagerungsdatei permanent und 32-Bit (nach Möglichkeit)
- Festplatte scandisk und defrag
- RAM ist durch nichts zu ersetzen
- bei wenig RAM nicht zuviel Smartdrv-WinCache oder gar RAM-Disk
- Buffers in config.sys min. 40 (eigentlich mit Smartdrv unnötig)
- Datenbank lokal halten und exklusiv öffnen, wenn nicht in Mehrbenutzerumgebung


Abfragen:

- wenig Indizes; sinnvoll für Felder, mit denen Tabellen verknüpft werden (auch referentielle Sachen)
- Reihenfolge der Felder in Abfrage gemäß der in Mehr-Felder-Indizes
- Sortierung vor allem nach nicht indizierten Feldern vermeiden.
- Daten aus verteilten Tabellen sollten am besten über Primary-Index-Felder verknüpft werden.
- Kein DLookup in Abfrage; eher Tabelle in Abfrage aufnehmen oder Unterabfrage.
- Anzahl der Felder beschränken, wenn möglich.
- Listen- und Kombinationsfelder sollten nicht auf eingebundene Tabelle zeigen.
- Eigenschaft Datensatzherkunft für Formulare und Felder besser auf eine Abfrage zeigen lassen.


Drucken:

- Berichtseigenschaft Schneller Laserdruck
- im Abschnitt [Microsoft Access] der msacc20.ini den Eintrag 'UseDefaultPrSetup' einfügen.


Module:

- immer erst kompilieren, bevor gespeichert wird.
- vollständige Bezeichner nicht häufig verwenden, eher an Objektvariablen übergeben.
- Transaktionen !


Wenn nicht in einer Mehrbenutzerumgebung gearbeitet wird, können innerhalb von MS-Access in ANSICHT / OPTIONEN die Zeitintervalle im Menüpunkt 'Mehrbenutzerumgebung / ODBC' auf die zulässigen Höchstwerte gesetzt werden.

Unbedingt das Hilfe-Item "Optimieren / Leistung" durchlesen !


Geschwindigkeit und Programmierstil:

TRANSAKTION:

Wenn Datenmengen durchgenudelt werden müssen und dabei Schreib-/Lesezugriffe erfolgen, kann die Performance durch den Einsatz von Transaktionen (transaction) erheblich beschleunigt werden:

    Dim WS As WorkSpace

    Set WS = DBEngine.Workspaces(0)

    WS.BeginTrans

    Do While Not RSZ.EOF
          ...
    Loop

    ' ************* Rücknahme der Transaktion anbieten **************

    x = SysCmd(SYSCMD_SETSTATUS, "Wollen Sie ... bestätigen ?")

    If MsgBox("Text", 292, "Titel") = 6 Then
        WS.CommitTrans
    Else
        WS.Rollback
    end if

    x = SysCmd(SYSCMD_CLEARSTATUS)

Allerdings kam es einmal vor, daß in einer Routine, in der in einer transaktions-überwachten Schleife ein Recordset redefiniert wurde, der Fehler "Kann keine weiteren Tabellen öffnen" auftrat. Erst nach Entfernen der Transaktions-Methoden konnte das bewältigt werden.

Ebenso mußte festgestellt werden, daß ein Recordset nicht durchlaufen wurde, wenn nicht ein explizites RS.MoveFirst angewandt wurde und, was viel schwerwiegender ist, daß es über das SEL-Stiftungs-Novell-Netz zu wiederholbaren
Abstürzen bei einer bestimmten Tabelle, an die per Transaktion Datensätze angefügt wurden, kam.


Vergleich von Access mit Excel   Quelle: microsoft   Datum: 08.2005   nach oben

Die Entscheidung, welche Aufgaben man besser mit Excel oder Access bearbeitet, ist selbst für fortgeschrittene Benutzer schwer zu überschauen. Als Datenbank-Programmierer habe ich meistens kein Problem, nach 'Sicht der Akten' zu entscheiden, welcher Weg der bessere ist.

Ausgerechnet aus dem 'feindlichen' Microsoft-Lager stammt ein einigermaßen allgemein verständlicher Artikel zu diesem Thema, geschrieben von Emma Nelson (Office-Hilfeteam):

Verwalten von Daten mit Access oder Excel


Verzögerung   Quelle: dmt   Datum: 03.2004   nach oben

PAUSE:

Sub Pause(Sekunden As Integer)

    Dim sngStart As Single

    sngStart = Timer                            ' Start-Zeit ermitteln.

    Do
      DoEvents
    Loop Until Timer >= sngStart + Sekunden     ' Übergabe-Sekunden-Schleife

End Sub

sorgt dafür, daß Access-Basic für die übergebene Anzahl Sekunden in dieser Routine verweilt.

* * * *

WAIT:

Eine andere, eher simple Möglichkeit, Minipausen einzubauen, um Systemhänger in z.B. anderen Anwendungen zu überbrücken, bestand bei SEL im Mehrfach-Aufruf von DoEvents. Hat bei RemoteShell-Anweisungen in Windows-TNVT-Unix-Terminal-Fenstern gut geholfen.

Sub wait ()

    Dim j as Integer

    For j = 1 To 200
        DoEvents
    Next j

End Sub

* * * *

XSENDKEYS:

Ebenfalls in diesem Zusammenhang steht xSendKeys, das einen SendKeys-String buchstabenweise übermittelt. Dann klappts auch in Shell-Fenstern.

Sub xSendKeys (sK As String)

    ' Wenn eine Anwendung sich beim Empfangen von Access-SendKeys-Zeichenketten
    ' verschluckt, können diese dann eben auch buchstabenweise übergeben werden.

    Dim l As Integer, C As String

    l = Len(sK)

    For i% = 1 To l
       C = Mid$(sK, i%, 1)
       SendKeys C, True
    Next i%

End Sub

nach oben
zur Startseite dieses Webangebotes zur infobase-Hauptseite   xhtml1.0