Excel Macro

hondacrusty

Newbie
Registriert
Mai 2008
Beiträge
4
Hallo, kann mir jemand bitte helfen?
Folgendes Problem: Ich habe unten stehendes Macro in eine Excel Arbeitsmappe gespeichert, welches dazu dienen soll dies Mappe nach einer Zeit X automatisch zu speichern und zu schliessen.

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Dim Beginn As Date, Pause As Date, Ende As Date

Beginn = Timer
Pause = 120

Do Until Timer > Beginn + Pause
DoEvents
Loop

Ende = Timer

Workbooks("Filename.xls").Close SaveChanges:=True

Me.Close

End Sub


Dieses Macro wurde hier vor einiger Zeit schon mal gepostet. :freak:
Leider unterdrückt es aber sämtliche Kommentare in meiner Excel-Liste. Es ist nur möglich die Kommentare dauerhaft anzuzeigen. Ein aufpopen der Kommentare funktioniert nicht mehr. Kennt jemand das Problem,und kann mir weiterhelfen?

Mein Dank wird euch ewig nachschleichen.;)
 
Ich tippe mal -ohne es genau zu wissen- darauf, dass hier das Problem in der Herangehensweise über die DoEvents-unterbrochene Schleife liegt. Demzufolge wäre die einfachste Lösung eine andere Herangehensweise zu wählen.

In VBA gibt es leider kein vordefiniertes Timer-Objekt, also müsstest du die API bemühen. In der Library user32 gibt es dafür die Funktionen SetTimer / KillTimer, die dabei behilflich sind.

Als kleiner Ansatz:
Füge deiner Arbeitsmappe ein Modul hinzu und deklariere die beiden Funktionen:

Code:
Declare Function SetTimer Lib "user32" (_
ByVal hwnd As Long, _
ByVal nIDEvent As Long,_
ByVal uElapse As Long, _
ByVal lpTimerFunc As Long) As Long

Declare Function KillTimer Lib "user32" (_
ByVal hwnd As Long, _
ByVal nIDEvent As Long) As Long

Du kannst dir dann mit deren Hilfe einen Timer setzen, der nach einer bestimmten Zeit (uElpase in Millisekunden) eine Sub/Funktion aufruft und so die gewünschte Arbeit ausführt. Die für den Aufruf dieser Sub/Funktion notwendige CallBack-Adresse (IpTimerFunc) findest du in VBA mit "AdressOf". Die Parameter für Handle und nIDEvent kannst du hier beim Aufruf von SetTimer auf 0 setzen.

Der Aufruf für SetTimer wäre also z.B.
Code:
SetTimer(0, 0, 30000, AddressOf [B][I]Aufgerufene Sub[/I][/B])

Ich hoffe, das hilft dir als Ansatz.
 
... das klingt ja ziemlich kompliziert.
Mein Macro hat ja eigentlich ganz gut funktioniert. Speichern, schliessen nach 2min ... alles kein Problem.
Es will mir einfach nicht in den Kopp wieso das Ding nun solche Auswirkungen auf meine Excel-Tabelle hat.
Leider bin ich nicht gerade ein Auskenner in der Materie, kannst du deine Ansätze mal für einen Laien wie mich erklären? Wer oder was ist API? Ich habe nur den VBA der dem Excel beigefügt ist.:lol:
 
Hallo,

Ja, das ist leider schon etwas komplizierter als deine ansonsten eigentlich auch gut funktionierende Methode. Es könnte auch sein, dass dein Problem, das während der Ausführung der Schleife auftritt letztendlich nichts damit zu tun hat - ich hab ähnliche Erfahrungen aber schon mit DoEvents-Schleifen gemacht, daher war das meine Vermutung. ;)

Aber zu meinem Ansatz:
VBA für Excel - klar - dafür ist das ganze auch gedacht, im ("richtigen") VB hätte man das sogar einfacher lösen können als mit einem API-Aufruf. API bedeutet im Übrigen "Application Programming Interface" oder Programmierschnittstelle - ganz einfach gesagt sind da fertige Funktionen drin, auf die du beim Programmieren zurückgreifen kannst. Dafür musst du sie eben nur erst deklarieren, weil sie nicht standardmäßig in VBA deklariert sind.

Ich gebe dir einfach mal eine halbfertige Lösung (halbfertig = extrem quick and dirty, man sollte auf jeden Fall noch prüfen, ob schon ein Timer existiert, bevor man ihn setzt und löschen, per KillTimer - sollte man ihn dann nach nach Aufruf der TimerProc-Funktion auch noch - all das spare ich mir erstmal, darfst du dann gerne noch ergänzen ;)).

Also - Im Excel-VBA-Editor rechtsklickst du links in die "Projektübersicht" auf VBAProject (Arbeitsmappenname)und wählst "Einfügen -> Modul" In das Modul klatscht du dann einfach folgendes rein :

Code:
Declare Function SetTimer Lib "user32" ( _
ByVal hWnd As Long, _
ByVal nIDEvent As Long, _
ByVal uElapse As Long, _
ByVal lpTimerFunc As Long) As Long

Declare Function KillTimer Lib "user32" ( _
ByVal hWnd As Long, _
ByVal nIDEvent As Long) As Long


Public Sub TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, _
    ByVal wParam As Long, ByVal lParam As Long)
       '(...)
       [B]Workbooks("Filename.xls").Close SaveChanges:=True
       Me.Close[/B]
       '(...)

End Sub

Ins Worksheet, wo jetzt dein Makro steht, kommt dann einfach

Code:
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
'(...)
hTimer = SetTimer(0, 0, [B]x[/B], AddressOf TimerProc)
'(...)
End Sub

Das x ersetzt du mit der Anzahl der Millisekunden, die gewartet werden soll.

Ich hab das jetzt ohne Möglichkeit das auszuprobieren so ins Blaue geschrieben, wenn also Fehler aufschlagen mit denen du nichts anfangen kannst, sag Bescheid - ich hoffe das bringt dich erstmal weiter.
 
Danke für deine Mühe, ich werds sicher bald mal ausprobieren . Werd mich dann mit dem Ergebniss melden.
Nur noch `ne Frage: wie prüfe ich ob schon ein Timer gesetzt ist? Ich muss vielleicht dazu sagen, dass das Macro an mehreren Rechner benutzt werden soll (Im Netzwerk sozusagen). Das bedeutet also die "Timerprüfung" sollte auch durch das Macro erfolgen, oder?
 
Hi,

Genau, ob der Timer bereits gesetzt ist, überprüfst du im Makro. Das machst du, indem du im Modulcode (also der Code, den du in das erstellte Modul - "Modul1", wenn du es nicht umbenannt hast) kopiert/geschrieben hast, eine Variable deklarierst, der du den Timer bei der Erstellung zuweist. Dann kannst du bei der Erstellung des Timers prüfen ob diese Variable noch leer ist und wenn nein mit Exit Function aus der Funktion springen.

Allerdings - und deswegen spare ich mir das detaillierte Erklären des o.g. Vorgehens- ich habe mal ein wenig recherchiert und evtl. eine etwas bequemere Lösung gefunden. VBA für Excel bietet im Application-Objekt die Methode "OnTime" an.

Ich habe leider momentan wieder keine Möglichkeit, das zu testen, aber nach dem Text in der Hilfe zu VBA für Excel (da kannst du ja selbst mal reinschauen, falls du sie verfügbar hast) verstehe ich das wie folgt :

Du rufst in deinem Eventhandler (also die Prozedur die aufgerufen wird um den "countdown" zu starten - in deinem Ausgangsbeispiel also die Private Sub Workbook_SheetSelectionChange) folgenden Code auf (die Do/Loop Schleife muss dann natürlich weg und der Workbooks.Close Aufruf muss entsprechend in die Aufgerufene Prozedur umgelagert werden.)

Code:
  Application.OnTime Now + TimeValue("00:01:00"), "TimerProzedur"

Das bewirkt, dass die Prozedur "TimerProzedur" nach einer Minute von jetzt an aufgerufen wird. "TimerProzedur" musst du dann natürlich noch schreiben - wichtig: Wieder in ein Modul!

Code:
Public Sub TimerProzedur

  [COLOR="SeaGreen"]'(...) <- Weiterer automatisch ausgeführter Code[/COLOR]

  [COLOR="#2e8b57"]'Dein Code aus dem Ausgangsbeispiel.[/COLOR]
  Workbooks("Filename.xls").Close SaveChanges:=True
  Me.Close

End Sub

Etwaige Funktionsdeklarationen aus der API kannst du dir dann sparen, genau wie den ganzen übrigen Kram von oben, sollte das so funktionieren. Ich konnte es wie gesagt noch nicht testen, aber es klingt vielversprechend. :)
 
Zurück
Oben