PDA

Vollständige Version anzeigen : Beenden der Applikation abfangen


Demian
03.04.2008, 15:15
Moin,moin alle zusammen,

ich habe eine kleine Hintergrundappliaktion ohne Forms inkl. Log-Funktionen. Jetzt gebe ich beim Start des Programms den Logtext "Programm gestartet" aus. Beim regulären Beenden (in diesem Programm die Taste "ENDE") erscheint ebenfalls ein Hinweis, dass das Programm beendet wurde.

Jetzt würde ich den Hinweistext bezüglich des Beenden gerne auch ausgeben, wenn das Programm nicht auf reguläre Weise geschlossen wird (z.B. über den Task-Manager).

Ist das möglich? Wenn ja, kann mir da einer nen Anhaltspunkt geben?


Gruß
Demian

hcscherzer
03.04.2008, 15:37
Da stellt sich die Frage (wenn das Programm das selbst ausgeben soll): woran merkt das Programm, dass es nicht mehr läuft? Wenn es nicht mehr (ordentlich) läuft, kann es meist auch nichts mehr von sich geben.
Entweder Du brauchst eine sehr ausgetüftelte Fehler-Routine, die bei allen möglichen Absturzursachen so eine Meldung ausgibt.
Oder Du schreibst einen zweiten Task, der den ersten überwacht ...

Demian
03.04.2008, 16:15
Naja, dachte es gibt für solche Hintergrund-Apps vielleicht auch so vordefinierte Prozeduren wie bei Forms Form_FormClosing mit der Eigenschaft CloseReason bei dem Parameter FormClosingEventArgs.

Nen zweiten Task möchte ich ungern. Der erste braucht laut Taskmanager schon 50 % der CPU (Endlosschleife).

Gruß
Demian

hcscherzer
03.04.2008, 18:09
Der erste braucht laut Taskmanager schon 50 % der CPUDa liesse sich doch sicher noch was optimieren, oder?
Und dann gibt es ja auch noch die Möglichkeit, den Task als Dienst zu starten und diesen von einem existierenden Tool wie z.B. Nagios überwachen zu lassen.

Demian
03.04.2008, 18:27
Ich glaube nicht das sich da viel optimieren lässt. Das Problem wird die gewollte Do Loop Endlosschleife in der Prozedur Main sein. Unten mal der Code. Mit Diensten habe ich mich noch gar nicht so befasst. Funktionieren da Tastaturüberwachungen und das Erstellen von Logdateien? Welcher Setuptyp sind Dienste? Finde da nichts bei neuem Projekt. Ich habe die Standardversion. Nagios benutzen wir in der Firma. Lassen sich da E-Mail schicken, wenn der Dienst beendet ist?

Gruß
Demian


'*****************************************************************************
'Dieses Programm fragt in einer Endlosschleife im Hintergrund ab, ob eine der
'Ziffern 1 - 0 auf dem Nummernblock gedrückt wurde, um eine .run-Datei zu
'erstellen, damit Biecom den entsprechenden Abrufbefehl für den Timeboy startet.
'
'Wenn bei einer Aufschaltung auf dem PC Eingaben erforderlich sind, für die die
'Ziffern 1 - 0 des Nummernblocks benötigt werden, muss dieses Programm über
'die Taste ENDE oder über den Task-Manager(<- ohne Log) beendet werden.
'*****************************************************************************

'Allgemein
Imports System
'Dateisystem
Imports System.IO

Module modAbruf
'WIN-Api zum Prüfen, welche Taste gedrückt wurde.
'Beim Drücken einer Taste hat sie den Rückgabewert -32768, sonst 0.
Public Declare Function GetAsyncKeyState Lib "user32" (ByVal Taste As Int32) As Short

'WIN-Api zum Prüfen, ob Numlock aktiviert ist, oder nicht. Wenn aus Rückgabewert = 0
Public Declare Function GetKeyState Lib "User32" (ByVal Taste As Int32) As Short

'WIN-Api um den Programmcode kurz zu pausieren.
Public Declare Sub Sleep Lib "Kernel32.dll" (ByVal Sleeptime As Int32) 'Zeit in Milisekunden (1000ms = 1s)

'Allgemein
Sub Main()
'*****************************************************************************
'In dieser Prozedur läuft die Endlosschleife und ruft bei Druck auf
'eine der Ziffern 1 - 0 auf dem Nummernblock die Prozedur RunDateiErstellen()
'auf und übergibt die gedrückte Ziffer.
'
'Durch Drücken auf die Taste ENDE wird das Programm beendet
'*****************************************************************************
On Error GoTo Fehler
Dim id As Int32
Call Log("Timeboy Mehrfachdockingstation Datenabruf wurde gestartet.")


Do
'Hier werden die RückgabeWerte aus GetAsyncKeyState mit 0 verglichen.
'Beim Drücken einer Ziffer, ändert sich der Rückgabewert auf -32768.

'Innerhalb der Schleife wird GetAsyncKeyState so schnell erneut aufgerufen, dass der
'Rückgabewert systemseitig noch nicht auf 0 gesetzt werden konnte. Das führt dazu,
'dass die Bedingung mehrere Male zutrifft und mehrmals versucht wird die .run zu
'erstellen. Deshalb wird nach dem Drücken einer Taste der Code für 0,2 Sekunden pausiert.

If Then Call Log("Timeboy Mehrfachdockingstation Datenabruf wurde beendet")

Call NumlockPruefen()

'Fach 51 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad1) < 0 Then Call RunDateiErstellen("1") : Sleep(200)
'Fach 52 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad2) < 0 Then Call RunDateiErstellen("2") : Sleep(200)
'Fach 53 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad3) < 0 Then Call RunDateiErstellen("3") : Sleep(200)
'Fach 54 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad4) < 0 Then Call RunDateiErstellen("4") : Sleep(200)
'Fach 55 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad5) < 0 Then Call RunDateiErstellen("5") : Sleep(200)
'Fach 56 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad6) < 0 Then Call RunDateiErstellen("6") : Sleep(200)
'Fach 57 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad7) < 0 Then Call RunDateiErstellen("7") : Sleep(200)
'Fach 58 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad8) < 0 Then Call RunDateiErstellen("8") : Sleep(200)
'Fach 59 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad9) < 0 Then Call RunDateiErstellen("9") : Sleep(200)
'Fach 60 bei Mehrfachdockingstation
If GetAsyncKeyState(Keys.NumPad0) < 0 Then Call RunDateiErstellen("0") : Sleep(200)

'Beenden des Programms durch Drücken der Taste Ende
If GetAsyncKeyState(Keys.End) < 0 Then
MsgBox("Das Programm 'Timeboy Mehrfachdockingstation Datenabruf' wurde beendet. Die Ziffern auf dem Nummernblock können wieder verwendet werden.", MsgBoxStyle.Information, "Musterfirma")
Call Log("Timeboy Mehrfachdockingstation Datenabruf wurde beendet")
Exit Sub
End If
Loop
Exit Sub

Fehler:
Call Log(Fehlertext() & " in Main")
Err.Clear()
End Sub

Sub NumlockPruefen()
'*****************************************************************************
'Diese Prozedur wird in Main als erstes innerhalb der Do Loop Schleife auf-
'gerufen, um zu prüfen, ob Numlock angeschaltet ist. Ggf. erfolgt die Aktivierung.
'Sollte Numlock nicht angeschaltet sein, würde bei Drücken auf die Taste 1 (Ende)
'das Programm mit entsprechendem Hinweis beendet.
'*****************************************************************************
On Error GoTo Fehler

'GetKeyState gibt bei aktivierten Numlock 1 zurück (außer WIN 2000)
If GetKeyState(Keys.NumLock) = 0 Then
Dim wshshell As Object
wshshell = CreateObject("WScript.Shell")
wshshell.SendKeys("{NUMLOCK}")

Call Log("Numlock wurde eingeschaltet")
End If
Exit Sub

Fehler:
Call Log(Fehlertext() & " in NumlockPruefen")
Err.Clear()
End Sub

'Biecom
Sub RunDateiErstellen(ByVal Port As String)
'*****************************************************************************
'Diese Prozedur wird in Main innerhalb der Do Loop Schleife aufgerufen, sobald
'eine der Ziffern 1 - 0 auf dem Nummernblock gedrückt wird.
'
'Parameter:
'Port = gedrückte Ziffer auf dem Nummernblock
'*****************************************************************************
On Error GoTo Fehler

Call Log("Abruf.txt nach Port" & Port & ".run kopieren")

'nur kopieren, wenn die entsprechende .run seitens Biecom bereits verarbeitet wurde
If Not (File.Exists("C:\Timeboys Bretzenheim\line1\Port" & Port & ".run")) Then
File.Copy("C:\Timeboys Bretzenheim\Musterfirma\Abruf.txt", "C:\Timeboys Bretzenheim\line1\Port" & Port & ".run")
Call Log("Abruf.txt nach Port" & Port & ".run erfolgreich kopiert")
Else
Call Log("Abruf.txt wurde nicht kopiert da Port" & Port & ".run bereits vorhanden")
End If

Exit Sub

Fehler:
Call Log(Fehlertext() & " in Abruf")
Err.Clear()
End Sub

'Logging
Sub Log(ByVal Text As String)
'*****************************************************************************
'In dieser Prozedur wird der übergebene Logtext zusammen mit der Uhrzeit
'in die entprechende Logdatei des Monats geschrieben.
'
'Parameter:
'Text = bei allen nicht abgefangenen Fehler Function Fehlertext, ansonsten
' indivdueller Text
'*****************************************************************************
On Error GoTo Fehler

'Prüfen, ob Logdatei bereits vorhanden, dann Text anfügen, sonst Logdatei erstellen (,true)
Dim Logdatei As StreamWriter = New StreamWriter("C:\Timeboys Bretzenheim\Musterfirma\Log\" & MonthName(Month(Now)) & " " & Year(Now) & ".log", True)

Logdatei.WriteLine(TimeOfDay & " - " & Text)
Logdatei.Close()
Exit Sub

Fehler:
Logdatei.Close()
'Versuchen den Logfehler trotzdem zu loggen
Call Log(Fehlertext() & " in Log")
Err.Clear()
End Sub

Function Fehlertext() As String
'*****************************************************************************
'Dieser Fehlertext wird in allen Prozeduren verwendet und ist Übergabeparameter
'für die Prozedur Log() für alle nicht abgefangenen Fehler
'*****************************************************************************
Return "FEHLER: " & Err.Number & ", " & Err.Description & " in Zeile " & Err.Erl
End Function
End Module

Robert_Zenz
04.04.2008, 07:53
Moin.

Nur so eine Frage die mir auffällt...schriebst du in VB oder VB.NET? Weil irgendwie seh ich in dem Projekt Teile von beiden Syntaxen?

Ansonsten kann ich dir leider nicht helfen, da ich mich nicht wirklich mit dem Thema von Endlosschleifen beschäftigt habe. Aber was mir spontan einfällt wäre, dass du doch ein Sleep einabuen könntest, auf diese Weise läuft die Schleife nur z.B. alle 5 Milisekunden durch (die Verzögerung spürt man nicht), sollte aber den CPU ein wenig entlasten.

Rob

Demian
04.04.2008, 08:20
Moin,moin,

programmiere mit Visual Studio 2005. Inwiefern VB-Syntax? Wegen dem wshshell? Das hab ich gemacht, weil das sendkeys von VS2005 irgendwie nur an die Applikation sendet, und Numlock nicht eingeschaltet wurde.

Habe ja schon sleep eingebaut, und das behagt mir schon nicht so. Bin mir nicht sicher, obs nicht passieren kann, dass jemand in dem Moment die Taste drückt, wo sleep läuft. Musste schon mit den 0,3 Sekunden rumspielen, bei weniger hat die BEdingung wieder mehrmals gegriffen und bei mehr, hat er teilweise das Drücken nicht berücksichtigt.

Gruß
Demian

Robert_Zenz
04.04.2008, 11:04
Ich meinte damit u.a. die Error-Struktur...die wurde in VB.NET durch Try and Catch (Try ... Catch ex as Exception ... End Try) ersetzt. Und soweit ich weiß ist Call auch nicht mehr nötig.

Achso, du setzt das Sleep hinter jede If (beim ersten mal übersehen *hüstl*), probier mal ein einziges Sleep vor Loop, dadurch pausiert er erst dann wenn er alles abgehandelt hat.

Wegen dem schließen, du könntest eine Variable setzen und z.B. in einer .ini/.config speichern wenn das Programm läuft. Wird das Programm über den Taskmanager abgeschossen kann es nicht wirklich merken das es beendet wird, weil der Prozess abgewürgt wird, aber so würde er wenigstens beim nächsten Start merken das er vorher unfreiwillig Hops ging. ^^

Rob

P.s.: Hab cih was verpasst?
If Then Call Log("Timeboy Mehrfachdockingstation Datenabruf wurde beendet")

Edit:
Achja, wenn ich mich nicht verlesen habe wirkt die Funktion GetAsyncKeyState auch rückwirkend, will heißen auch wenn einer während dem Sleep die Taste drückt, wird sie beim nächsten Aufruf berücksichtigt (zumindest was ich auf die schnelle in der Beschriebung gesehen hab).

Demian
04.04.2008, 11:35
Moin Robert,

ich habe mit VBA angefangen zu programmieren. Dann kam noch Lotus Notes (Lotus Skript) dazu. In beiden gibt es das Try an Catch nicht. Um nicht durcheinander zu kommen, werde ich bei dem Error-Handling erstmal bleiben solange es geht. Try and Catch habe ich schonmal gesehen. Müsste ich mich aber erstmal näher mit beschäftigen. Wüsste zur Zeit nicht wo ich das einsetzen müsste???

Wegen dem Zitat: das ist ja das "normale" Beenden über die ENDE-Taste. Da kann ich es ja abfangen.

Das mit der .ini ist ne gute Idee. Da werde ich mir mal meine Gedanken machen, wie ich das handhabe.

Ich habe mir beim Testen immer das Logfile angesehen, bezüglich GetAsncKeyState. Und bei größeren Sleep-Zeiträumen wurde bei 2 maligem Drücken einer Ziffer nur einmal die Bedingung abgearbeitet. Also, scheint er das nicht zu speichern. Wobei ich sagen muss, es ist das 1. Mal dass ich mit APi was mache. Kenne mich da nicht so wirklich aus.

Das mit dem Sleep vor dem Loop - soweit habe ich gar nicht gedacht. Werde ich am WE auch mal testen.

Vielen Dank für die guten Tipps.

Gruß
Demian

Demian
08.04.2008, 10:23
Moin,moin alle zusammen,

mir ist jetzt aufgefallen was Rober mit

P.s.: Hab cih was verpasst?
If Then Call Log("Timeboy Mehrfachdockingstation Datenabruf wurde beendet")

meint. Diese Zeile war noch aus Versuchen, und ich habe sie vor dem Kopieren nicht gelöscht.

Ich habe das mit dem Sleep vor dem Loop mal probiert, aber auch mit dem Ergebnis, dass die If-Abfragen mehrmals zutreffen, also müsste ich den Sleep-Zeitraum erhöhen, was aber wieder dazu führen könnte, dass der Tastendruck nicht registriert wird. Ich lasse es lieber so.

Das mit der .ini funktioniert theoretisch. Praktisch wollte ich in meinem Log die genaue Absturzzeit aufnehmen. Bin also hingegangen und innerhalb der Loop-Schleife immer eine neue Datei mit der Uhrzeit zu erstellen.

Manchmal enthält die Datei aber keinen Text. Der Programmabbruch erfolgt, dann scheinbar nach dem Erstellen der Datei, jedoch vor dem Schreiben der Uhrzeit.

Gut, habe ich mir gedacht und schreibe die Uhrzeit nicht rein, sondern orientiere mich am Erstellungsdatum der Datei. Leider scheint Windows den Zeitpunkt aber irgendwo zu cashen. Sprich ich erhalte oft die Uhrzeit der Datei, die vor meinetwegen 5 - 10 minuten erstellt wurde, obwohl im Explorer die richtige Uhrzeit angezeigt wird.

Auf dem Weg bin ich jetzt. MAl gucken wie ich das handhabe.

Gruß
Demian