VBA: Timeout für Functions oder Subs

Hallo zusammen,

ich suche nach einer Möglichkeit ein Timeout in VBA-Funktionen einzubauen. Nach Ablauf einer bestimmten Zeit, soll die Funktion einfach verlassen werden. Mir fehlt leider ein Ansatz, wie man ein einmal gestartetes Programm während der Laufzeit abbrechen kann.

Ausgangsituation:
Einer Funktion werden Parameter übergeben, die Funktion führt dann damit etwas aus, und gibt anschließen einen String zurück. („Erfolgreich beendet“ oder „Wegen Fehler abgebrochen“.)

Zukünftige Situation
Ich würde der Funktion zusätzlich noch gerne den Parameter Timeout (as integer) übergeben. Timeout ist die Anzahl der Sekunden, nach der die Funktion einfach „Timeout überschritten“ zurückgibt und anschließend Exit Function ausführt.

Hat jemand eine Idee, wie man das machen könnte?

Danke.

MfG
Stephan

Hallo Stephan,

arbeite mal mit den Funktionen Now und DateDiff, etwa so:

Function deineFunktion(deinParameter1, deinParameter2, maxSec As Integer) As String
 Dim jetzt As Date

 jetzt = Now
 ' hier laufen deine Berechnungen
 If DateDiff("s", jetzt, Now) \>= maxSec Then
 deineFunktion = "Timeout überschritten"
 Exit Function
 End If
 ' hier laufen deine weiteren Berechnungen
End Function

Die Abfrage „If DateDiff…“ muss halt ab und zu in deiner Funktion erfolgen. du hast ja vermutlich irgendwelche Berechnungsschleifen (sonst würde die Funktion wohl nicht so lange brauchen). Da kannst du es ja in die Schleifen einbauen.

Gruß, Andreas

Hallo Andreas,

erstmal vielen Dank für deine Antwort.

Die Abfrage „If DateDiff…“ muss halt ab und zu in deiner
Funktion erfolgen. du hast ja vermutlich irgendwelche
Berechnungsschleifen (sonst würde die Funktion wohl nicht so
lange brauchen). Da kannst du es ja in die Schleifen einbauen.

Von der Idee her wird das in Schleifen sicher funktionieren, wenn man Endlosschleifen abfangen möchte. Allerdings bläht das den Code ganz schön auf. Da ich außerdem mit sehr vielen Funktionen arbeite, die sich teilweise noch gegenseitig aufrufen, erscheint mir der Aufwand des Umprogrammierens zu groß. Ich müsste dann erst alle Funktionen und anschließend nochmal jeden einzelnen Funktionsaufruf anpassen. Der Code würde nahezu unleserlich.

In Anlehnung an „On Error Goto Fehler“ hätte ich gerne ein „On TimeLimitExceeded(300) Goto Timelimit“. (300 steht für das Zeitlimit in Sekunden)

Gibt es eine solche Möglichkeit in VBA?

Alternativ wäre es auch interessant, wenn ich zwei Dinge gleichzeitig starten könnte. Die eigentliche Funktion und eine weitere Funktion, welche die Applikation (z.B. Excel) in der die Funktion läuft gnadenlos killt, wenn das Timelimit überschritten wurde.

Irgendwelche Ideen?

MfG
Stephan

Hallo Stephan,

zu deinen Anforderungen fallen mir jetzt nur ein paar Stichworte ein, die ich teilweise aber selber nur wenig getestet habe:

  • Application.OnTime wird dir vermutlich nichts nützen, weil das nur funktioniert wenn kein anderer Makro läuft, bzw. wenn in dem anderen Makro DoEvent Kommandos eingebaut sind.

  • Mit Shell einen VB Script starten, in dem erst ein Sleep Kommando steht und dann Excel abgeschossen wird. Wie das genau geht weiß ich grad nicht.

  • Mit dem Windows API Kommando SetTimer. Ich glaube aber, auch dazu muss man in den Makro DoEvent Kommandos einbauen.

ich glaube, so wie du das möchtest, wird es nicht gehen. Aber es gibt ja noch Experten hier, die sicher mehr wissen.

Sorry und Gruß,
Andreas

Hi,

In Anlehnung an „On Error Goto Fehler“ hätte ich gerne ein „On
TimeLimitExceeded(300) Goto Timelimit“. (300 steht für das
Zeitlimit in Sekunden)

Gibt es eine solche Möglichkeit in VBA?

Verstehe ich Dich richtig und Du hättest gern so etwas …

Option Explicit
 Dim Ret As Boolean

Private Sub Command1\_Click()
 Dim Ret As Boolean
 Ret = myfunction(parameter)
 Wait\_For\_OK 5
End Sub

Private Sub Wait\_For\_OK(ByVal Zeit As Boolean)
 Dim tm As Double
 tm = Timer + Zeit
 Do While tm \> Timer
 If Ret = True Then Exit Sub
 Loop
 MsgBox "Die Fuktion konnte in " & CStr(Zeit) & " Sekunden nicht ausgeführt werden", vbCritical
 End
End Sub

Das funktioniert leider nicht.

Du hast nur einen Programzeiger. Wenn Dein Programm aus der Funktion mit einem ‚False‘ zurück kommt, ist die Funkion bereits abgeschlossen, die Funktion wird nicht mehr versuchen, zu einem positiven Ergebnis zu kommen. Der Rückgabewert kann sich nicht mehr ändern.

Die Prüfung musst Du in Deine Funktion einbauen oder den Aufruf Deiner Funktion in die Prüfung einbauen, wenn die Funktion nicht selbst eine Schleife enthält.

So wie Du Dir das vorstellst wird es nach meiner Meinung nicht gehen. Dazu müsstest Du Multithreading bemühen und das wäre für diesen Zweck heftig übertrieben. Das geht mit VB.Net, mit VB6 nur sehr umständlich, ist etwas für die ganz großen Experten, mir zu hoch. Ob’s mit VBA überhaupt geht, weiß ich nicht genau.

Gruß Rainer

Hallo!
So, wie Du Dir das vorstellst, geht das leider mit VBA nicht.
Du brauchst irgendwas, das am Anfang der Funktion gestartet wird und gesagt bekommt, wann es die Funktion abbrechen soll.
Abbrechen kann dieses „Teil“ (ist letztlich eine Art Watchdog) die Funktion aber nur dann, wenn es sie auch gestartet hat.

Bei „richtigen“ Laufzeitumgebungen würde man das z.B. so lösen, dass der Watchdog die Funktion in einem eigenen Thread laufen lässt. Dieser Thread kann dann nämlich vom Watchdog auch wieder beendet werden, wenn bspw. ein Timeout abgelaufen ist.

Da mit VBA aber kein Multithreading möglich ist (bitte korrigiert mich, falls ich mich irre), sind solche Versuche zum Scheitern verurteilt.

Gruß,
Martin

So wie Du Dir das vorstellst wird es nach meiner Meinung nicht
gehen. Dazu müsstest Du Multithreading bemühen und das wäre
für diesen Zweck heftig übertrieben. Das geht mit VB.Net, mit
VB6 nur sehr umständlich, ist etwas für die ganz großen
Experten, mir zu hoch. Ob’s mit VBA überhaupt geht, weiß ich
nicht genau.

Hallo Rainer,

naja, wenn das z.B. in VB.Net geht, Aufwand jetzt mal weglassend, so wäre das zumindest ein Weg.
Dann bräuchte Stephan halt nur noch jmdn. der ihm das in VB.Net
programmiert, möglichst mit vielen Parametern beim .Net-.exe Start
bzw. Übergabe des Namens/Pfads einer von mir aus Textdatei
womit dann Stephan den genauen Ablauf der .exe „steuern“ kann

Natürlich interessiert mich so eine Anfrage, hab deshalb auch experimentiert :smile: Zum Testen hab ich dann u.a. sowas gebaut:
While 1
a=a+1
if a=1000000 then a=0
wend
Das ließ sich problemlos mit „Esc“ abbrechen.

@Stephan, „Esc“ stoppt auch deine Makros? WEnn ja wäre das gut.
Sicherheitshalber könntest du da beim Programmstart Enablecancelkey auf True setzen.

Wenn es manuell geht mit dem Abbruch so sehe ich da eine vage aber realistische Chance das auch automatisch nach Zeit x tun zu lassen durch ein anderes Makro.

Plan ist, Excel in zwei Instanzen zu öffnen. Aus der zweiten startest du deine Programme in der ersten.
Dann schickst du nach Zeit x per Sendkeys oder API-Sendkeys Esc an die erste Instanz.
Oder machst was besseres, Sendkeys ist immer Risko :frowning:

Ist jetzt nur angedacht. Ich weiß das man auf eine andere Excelinstanz zugreifen kann per Code. Wie weiß ich grad nicht genau aber man kann sogar in der fremden Instanz/Mappe ein Makro starten
und dem Startparameter mitgeben.

Ergo auch ein Makro starten was Sendkeys veranlasst o.ä.
Aber ich befürchte wegen Nicht Multitasking würde das zwar klappen,
aber wenn überhaupt erst dann wenn die erste Instanz nicht mehr durch
deine eigentlichen Programme „belegt“ ist :frowning:

Was natürlich garantiert geht ist die erste Instanz ganz abzuschießen.
Dafür brauchste nur API Findwindow, Postmessage, sendmessageu.ä.
Aber, dann isse weg, alle Änderungen in der Mappe sind natürlich auch futsch.
Also müßte man dann aus der zweiten Instanz heraus die Daten der Mappe der ersten Mappe rauskopieren und speichern bevor man die erste abschießt. Wie und ob das so möglich ist.
Ich denke da z.B. an Application.Screenupdating.

Stefan, irschendwie sehe ich es deutlich vor mir daß du doch deinen Code umschreiben mußt. Spontan gedacht müßte da doch eine Codezeile reichen die du überall an/in den richtigen Stellen pro Prozedur einfügst, sowas in etwa:
If ZeitAbgelaufen = True then exit function.
Gibt da aber zig Möglichkeiten.

Du siehst ja das Thema hat viele interessiert, darfst du den Code zeigen? Klar, wenn das ein Mammutprojekt ist wo die Byteanzahl
nur der Codezeilen größer ist als so manche kleine Exelmappe,
naja, sowas lese ich gerne, aber da überall was ändern, auweija :smile:

Aber den Code zu zeigen hat nix mit einer Lösung deiner Anfrage zu tun. Bei all meinen Recherchen/gedanken kristallisiert sich raus
daß zwei Dinge gehen. Die zeitlichen Abbruchbedingungen in die Prozeduren schreiben oder die Excelinstanz abschießen, letzteres hat halt Kollateralschäden.

Sorry.

Gruß
Reinhard

Hallo Reinhard,

naja, wenn das z.B. in VB.Net geht, Aufwand jetzt mal
weglassend, so wäre das zumindest ein Weg.
Dann bräuchte Stephan halt nur noch jmdn. der ihm das in
VB.Net
programmiert, möglichst mit vielen Parametern beim .Net-.exe
Start
bzw. Übergabe des Namens/Pfads einer von mir aus Textdatei
womit dann Stephan den genauen Ablauf der .exe „steuern“ kann

in einem .NET Projekt gibt es Möglichkeiten, daß sich die Threads gegenseitig beeinflussen. Eine zusätzliche .exe ist wieder ein anderes Thema.

So oder so ist die Änderung des Codes der einfachere Weg. Mit einem vielfachen Aufwand wird die andere Lösung sicher auch möglich sein, aber das bedeutet eben einen erheblichen Mehraufwand und nicht wie beabsichtigt, eine Vereinfachung.

Gruß Rainer

Hallo zusammen,

in einem .NET Projekt gibt es Möglichkeiten, daß sich die
Threads gegenseitig beeinflussen.

ich hatte befürchtet, dass ich mit VBA nicht weiterkomme. Mal sehen, wenn ich die Zeit finde, baue ich das Ganze mal auf VB.NET um.

Es geht übrigens um eine Access-Datenbank (mittlerweile nur noch Frontend eines SQL-Servers) die zu bestimmten Zeitpunkten unterschiedliche Dinge (z.B. Stored Procedures oder Makros in anderen Excel-/Accessdateien) sequentiell startet. Beim SQL-Server kann ich bequem mit Timeouts arbeiten und bekomme beim Überschreiten entsprechende Rückmeldungen. Bei den VBA Makros kommt es hin und wieder mal vor, dass, trotz eingebauter Fehlerbehandlung, Fehler auftreten und anschließend der ganze Ablauf hängt. Wäre jetzt schön gewesen, wenn es in VBA die von gewünsche Funktion schon gegeben hätte. Ich war einfach nur zu faul die Fehlerbehandlung aller möglicherweise betroffenen VBA-Makros auf Plausibilität prüfen und ggf. anzupassen. Anscheinend ist das aber die schnellste und sauberste Lösung.

MfG
Stephan