Dieser Tipp & Trick beschreibt eine Vielzahl von Methoden zur Vermeidung und Behandlung von Laufzeitfehlern in Ihrem VBA-Code. Sie werden mit den FindColumn– und FindRow-Methoden von Tabellenobjekten und der Exists -Methode von Dictionary-Objekten vertraut gemacht. Darüber hinaus wird der Einsatz von Variablen-Info-Funktionen wie IsNumeric beschrieben. Dadurch können Sie einige Laufzeitfehler vermeiden, andere lassen sich jedoch nicht verhindern. Das Standard-Fehlerbehandlungsverhalten von VBA besteht dann darin, die Simulation abzubrechen, sodass Sie nach der Behebung der Ursache die Simulation neu starten müssen. Dieser Tipp & Trick führt Sie in die benutzerdefinierten Fehlerbehandlungsfunktionen von VBA (oder, genauer gesagt, die WWB-VBA-Variante, die mit INOSIM geliefert wird) ein, wie die TryCatchFinally -Anweisung und die On Error -Anweisung.
Das Demo-Modell (verfügbar im Download-Bereich) wurde entwickelt, um Laufzeitfehler zu erzeugen und zu zeigen, wie diese vermieden werden können. Nehmen Sie sich Zeit und „spielen“ Sie mit dem Modell, löschen Sie die „Ergebnis“-Blätter im Parameter-Arbeitsbuch, um zu sehen, was passiert. Die folgenden Codebeispiele sind Teil einer Operationsparametersteuerung oder EndSim-Prozedur oder werden von diesen aufgerufen.
FindColumn- und FindRow-Methoden von Tabellenobjekten
Der Versuch, über einen nicht vorhandenen String-Index auf eine Zelle eines Tabellenobjekts zuzugreifen, führt zu einem Laufzeitfehler. Daher stehen die Methoden FindColumn und FindRow zur Verfügung, um zu überprüfen, ob ein bestimmter String-Index existiert. Die Methode gibt die Spalten- oder Zeilennummer (>0) oder -1 zurück, falls sie nicht existiert.
Im folgenden Beispiel wird der Name der Operation als Zeilenindex verwendet. Wenn er nicht existiert, sollte stattdessen der String aus der Operationsbeschreibung verwendet werden.
Dim duration As Double, row_index As String
If operation_parameters.FindRow(mop.Name) > 0 Then
row_index = mop.Name
Else
row_index = mop.Description
End If
duration = operation_parameters(row_index,"Duration")
Exists-Methode für Dictionary-Objekte
Der Zugriff auf einen nicht vorhandenen Eintrag eines Dictionary-Objekts würde einen Nullwert zurückgeben, was zu Laufzeitfehlern in den folgenden Zeilen führen kann. Daher steht die Exists-Methode für Dictionary-Objekte im Allgemeinen zur Verfügung. Da es sich bei der Liste der CustomAttributes eines INOSIM-Objekts um ein Dictionary handelt, wird im folgenden Beispiel mit der Exists-Methode geprüft, ob ein bestimmtes CustomAttribute für die Operation erstellt wurde. Das Sub Initialize_CA_randomly verwendet einen Zufallswert, um entweder den CA “Duration Factor” mit dem Zufallswert zu initialisieren oder gar nicht zu initialisieren.
'Called sub will decide randomly if the CA "Duration Factor" will be initialized,
'and if so, assign a random value. If not initialized, CA will not exist.
Initialize_CA_randomly(cop, "Duration Factor")
Dim duration_factor As Double
If cop.CustomAttributes.Exists("Duration Factor") Then
duration_factor = cop.CustomAttributes("Duration Factor")
Else
duration_factor = 1
End If
cop.Duration = duration * duration_factor * 3600
Verwendung von Variable-Info-Funktionen
Um zu überprüfen, ob eine Variable einen bestimmten Datentyp enthält, gibt es mehrere Variablen-Info-Funktionen wie IsDate, IsNumeric und IsDBNull (.NET) oder IsNull (COM). (In der WinWrapBasic-Dokumentation finden Sie eine vollständige Liste der verfügbaren Funktionen, die Sie in der Online-Hilfe der Software oder als PDF in unserem Download-Bereich finden.)
In diesem Beispiel können Benutzer des Modells Zahlen und das Schlüsselwort „content“ in die folgende Operationsparametertabelle (siehe Abbildung) eingeben, um die Transfermenge der Operationen Out 1 und Out 2 festzulegen. Der folgende Code verwendet die IsNumeric -Funktion, um zu überprüfen, ob der Inhalt der Tabellenzelle eine Zahl ist.
'Check if table entry is numeric for transfer amount (number or content),
'using the IsNumeric-function
Dim amount_info As Object
amount_info = operation_parameters(row_index,"Amount")
If IsNumeric(amount_info) Then
cop.Amount = - CDbl(amount_info)
ElseIf amount_info = "content" Or amount_info = "-content" Then
cop.AmountType = AmountType.amContent
Else
Console.Error("Specify a number or the keyword >content< in row " & row_index)
End If
Leere Tabellenzellen sind DBNull in WWB.NET (und Null in WWB-COM). Daher kann die IsDBNull-Funktion (oder IsNull-Funktion für WWB-COM) verwendet werden, um zu überprüfen, ob eine Zelle leer ist. Im folgenden Code wird der Nicht-Null-Wert in die Variable name geschrieben, während eine Warnung ausgegeben wird, wenn die Zelle Null ist.
'Check if transfer target in table is not null, using the IsDBNull-function
Dim transfer_target As Unit, unit_name As String
If Not IsDBNull(operation_parameters(row_index,"to/from")) Then
unit_name = operation_parameters(row_index,"to/from")
Else
Console.Warning("Enter valid unit name for the transfer target in row "&row_index)
End If
Benutzerdefinierte Fehlerbehandlung
Selbst mit den besten Absichten und Sorgfalt ist es nicht immer möglich, alle Laufzeitfehler zu vermeiden. Manchmal muss man sich auf externe Daten verlassen, manchmal entstehen Fehler aus komplexen Szenarien. In vielen dieser Fälle ist es vorteilhaft, den allgemeinen Fehlerfall explizit zu behandeln. Die folgenden Beispiele zeigen Ihnen, wie das geht. Die günstigste Methode, das Try-Catch-Finally-Statement, steht nur in WWB.NET zur Verfügung, das mit INOSIM 13 eingeführt wurde. Die On Error-Anweisung ist sowohl in WWB.NET als auch in WWB-COM verfügbar, aber weniger leistungsfähig.
Generell sind Fehlerbehandlungsstrategien nur dann ratsam, wenn einfache If-Anweisungen nicht möglich sind, da letztere in der Ausführungsgeschwindigkeit deutlich schneller sind. (Versuchen Sie es mit unserem T&T Ausführungsdauer von VBA-Steuerungen messen.) In den folgenden Abschnitten werden drei durchdachte Anwendungsfälle für die individuelle Fehlerbehandlung vorgestellt.
Anwendungsfall: Prüfen ob Objekt mit bestimmtem Namen existiert
Der Versuch, ein Objekt aus einer Collection unter Verwendung seines String-Namens abzurufen, kann beispielsweise bei Tippfehlern zu einem Laufzeitfehler führen. Daher ist es sinnvoll, vor der Zuordnung zu prüfen, ob das Objekt existiert.
Allerdings haben Objektklassen wie Units, Aufträge, Rezepte etc. derzeit keine Exists-Methode, im Gegensatz zu den oben gezeigten Dictionaries. Aber Sie können ganz einfach Ihre eigene Funktion für diesen Zweck erstellen, wie unten gezeigt. Alle drei (alternativen!) Funktionen geben true zurück, wenn die Teilanlage existiert, so dass die Zuweisung in der folgenden Zeile sicher ohne Laufzeitfehler ausgeführt werden kann.
Anschließend wird mit der Funktion IsNothing überprüft, ob der Variablen transfer_target ein Objekt zugewiesen wurde. Wenn ja, wird die Teilanlage als Transferziel verwendet, andernfalls wird die Senke verwendet.
If Does_Unit_Exist_OnErrorResumeNext(unit_name) Then
transfer_target = Units(unit_name)
End If
If Does_Unit_Exist_OnErrorGoTo(unit_name) Then
transfer_target = Units(unit_name)
End If
If Does_Unit_Exist_TryCatchFinally(unit_name) Then
transfer_target = Units(unit_name)
End If
cop.SourceSinkUnits.Clear
If Not IsNothing(transfer_target) Then
cop.SourceSinkUnits.Add transfer_target
Else
cop.SourceSinkUnits.Add Units("Sink")
End If
Try-Catch-Finally-Statement
WWB.NET, in INOSIM 13 eingeführt, unterstützt die Try-Catch-Finally-Anweisung, die nach Möglichkeit den Anweisungen On Error vorzuziehen ist. Tritt im Try-Block eine Ausnahme auf, wird der Catch-Block ausgeführt. Der optionale Finally-Block wird immer ausgeführt. Es ist möglich – und empfehlenswert – mehrere Catch-Blöcke zu implementieren, um verschiedene Arten von Ausnahmen abzufangen. Ein Beispiel finden Sie am Ende dieses Tipps & Tricks.
Function Does_Unit_Exist_TryCatchFinally(unit_name As String) As Boolean
Does_Unit_Exist_TryCatchFinally = True
Dim u As Unit
Try
u = Units(unit_name)
Catch
Does_Unit_Exist_TryCatchFinally = False
Finally
'
End Try
End Function
On Error-Anweisung
In WWB-COM können Sie die Standardfehlerbehandlung mit der On Error Resume Next-Anweisung deaktivieren. Von nun an werden alle Fehler ignoriert (statt die Simulation abzubrechen) bis zur Zeile On Error Goto 0. Das bedeutet für das folgende Beispiel: Tritt beim Zuweisen des Objekts ein Fehler auf, wird das (letzte) Fehlerereignis im Err-Objekt gespeichert. Durch Überprüfen der Err.Number-Eigenschaft gibt die Funktion false oder true zurück. Err.Clear löscht den Fehler.
Alternativ kann ein benutzerdefinierter Fehlerhandler mit einer On Error GoTo [errhandler]-Anweisung erstellt werden. Ein Beispiel finden Sie in dem Modell im Download-Bereich. Die Verwendung von GoTo-Anweisungen ist jedoch generell nicht ratsam. Während sich die Verwendung von GoTo zur strikten Trennung von Geschäftslogik und Fehlerbehandlung bei kleineren Funktionen lohnt, ist dies bei komplexeren Methoden nicht machbar.
Function Does_Unit_Exist_OnErrorResumeNext(unit_name As String) As Boolean
Dim u As Unit
On Error Resume Next 'sets error handler to continue with next line
u = Units(unit_name)
If Err.Number <> 0 Then
Does_Unit_Exist_OnErrorResumeNext = False
Else
Does_Unit_Exist_OnErrorResumeNext = True
End If
Err.Clear
On Error GoTo 0 'resets error handler to default
End Function
Anwendungsfall: Auf Worksheet-Existenz prüfen mit Try-Catch-Finally
Bei der EndSim-Prozedur werden einige Ergebnisse auf ein Sheet geschrieben, dessen Name sich aus dem Versuchsnamen und einem String zusammensetzt. Es wäre sehr unschön, wenn Sie vergessen hätten, dieses Sheet für einen langen Simulationslauf zu erstellen, und dessen Ergebnisse nun aufgrund eines Laufzeitfehlers verloren gehen.
Der folgende Code verwendet einen Try-Block, um ein Sheet mit dem Namen sheet_name zuzuweisen. Tritt eine Ausnahme auf, wird mit der Funktion Create_Sheet (siehe unten) eine Tabelle mit dem richtigen Namen erstellt und zugewiesen. Der Finally-Block wird unabhängig vom Auftreten einer Ausnahme ausgeführt und ruft den Sub Write_Results_to_Sheet auf, um die Ergebnisse zu schreiben.
Dim ResultsSheet1 As Sheet
Dim experiment_name As String, sheet_name As String
experiment_name = ActiveExperiment.Name
sheet_name = ActiveExperiment.Name & " Results 1"
Try 'exceptions allowed
ResultsSheet1 = Parameters.Sheets(sheet_name)
Catch 'executed if exception occured
Debug.Print Ex.ToString
Create_Sheet(sheet_name)
ResultsSheet1 = Parameters.Sheets(sheet_name)
Finally 'always executed
Write_Results_to_Sheet(ResultsSheet1)
End Try
Bitte beachten Sie, dass die Excel-Referenz unter Tools -> Referenzen aktiviert sein muss, damit der folgende WWB.NET Code funktioniert.
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Interop.Excel
Sub Create_Sheet(sheet_name)
Dim objApp As Excel.Application
Dim objWorkBook As Excel._Workbook
Dim objSheets As Excel.Sheets
Dim objSheet As Excel._Worksheet
objWorkBook = Parameters.ExcelWorkbook
objSheets = objWorkBook.Worksheets
objSheet = objSheets.Add
objSheet.Name = sheet_name
End Sub
Anwendungsfall: Kontrolliertes “Aufräumen” bei Laufzeitfehlern
Die COM-Schnittstelle von INOSIM ermöglicht das Arbeiten mit externen Dateien wie Textdateien oder Excel-Arbeitsmappen, siehe T&T Externe Excel-Arbeitsmappen, das Lesen von Simulationsparametern oder das Schreiben von Simulationsergebnissen. Die Verbindung zu externen Dateien muss vor dem Ende der Simulation beendet werden. Andernfalls bleibt die Dateiverbindung im Hintergrund aktiv und verhindert, dass Sie die Datei anschließend manuell öffnen können.
Nehmen wir an, Sie möchten Ihre Simulationsergebnisse in eine externe Excel-Arbeitsmappe schreiben. Während des Schreibens, bevor die Close-Anweisung erreicht wird, tritt ein Fehler auf, der dazu führt, dass die Simulation abgebrochen wird. Somit bleibt die Dateiverbindung aktiv und muss manuell durch Abbrechen des Prozesses im Windows Task Manager geschlossen werden. Andernfalls erhalten Sie beim Versuch, die Datei zu öffnen, eine Warnung, die besagt, dass das Dokument gerade verwendet wird. Um dies zu verhindern, kann eine benutzerdefinierte Fehlerbehandlung für ein kontrolliertes “Aufräumen” verwendet werden: Tritt beim Öffnen ein Fehler auf, wird die Datei automatisch geschlossen.
Wenn Sie „External“ für das benutzerdefinierte Attribut „Results Workbook“ anstelle von „Parameters“ wählen, werden Sie in der Simulation aufgefordert, eine Excel-Datei für die Ergebnisausgabe auszuwählen.
Das folgende Codebeispiel zeigt auch, wie die Try-Catch-Anweisung mit mehreren Catch-Anweisungen für verschiedene Fehlerquellen erweitert werden kann:
- Tritt beim Schreiben* ein Fehler auf, d. h. während die Datei geöffnet ist, wird eine Fehlermeldung auf die Konsole ausgegeben und die Datei wird gespeichert und geschlossen.
- Wenn keine Excel-Arbeitsmappe ausgewählt wurde, wird eine Fehlermeldung ausgegeben.
(* In diesem Beispiel wird der Fehler absichtlich durch Aufrufen der Err.Raise-Methode ausgelöst.)
Sub Write_Results_to_External_Workbook
'Write to external excel file (WBB.NET)
Dim FullFilePath As String
FullFilePath = GetFilePath("Select File","xlsx",,"Select Excel Workbook",0)
Dim myWorkbook As Excel._Workbook
Try
myWorkbook = OpenExternalWorkbook(FullFilePath)
Write_Results_to_External_Worksheet(myWorkbook.Worksheets(1))
myWorkbook.Save ' Optional
myWorkbook.Close
Catch ErrorWriting As System.Exception When Not myWorkbook Is Nothing
Console.Error "An error occured while writing simulation results to the external file."
myWorkbook.Save ' Optional
myWorkbook.Close
Catch NoFile As System.Exception When myWorkbook Is Nothing
Console.Error "No Excel-file was selected."
Finally
End Try
End Sub
(für registrierte Anwendende von INOSIM)
- Beispielmodell Custom Error Handling.ixml
- PDF-Ausdruck für diesen Tipp & Trick
Fragen?
Möchten Sie mehr über dieses Thema erfahren oder haben weitere Fragen? Bitte kontaktieren Sie uns.
Mehr Tipps & Tricks
Benutzerdefiniertes Verhalten im Störungsfall
In INOSIM Simulationen können benutzerdefinierte, stochastische Teilanlagen-Störungen genutzt werden, um die Realität in einer Anlage möglichst präzise abzubilden. Mit dem Add-On Statistische Analyse kann dann…
Arbeiten mit externen Excel-Arbeitsmappen
Arbeiten mit externen Excel-Arbeitsmappen Neben dem Zugriff auf die in INOSIM eingebaute Excel-Arbeitsmappe (siehe Tipp Nutzen Sie Ihr Excel-VBA-Wissen bei der Arbeit mit INOSIM), kann mit Hilfe…
Table-Objekte anwenden
In INOSIM Modellen ist es sehr praktisch, die Eingabe für Ihre Rezepte in Excel-Tabellen zu organisieren, um entweder einen besseren Überblick über Ihre Eingabe zu…