Forums Neueste Beiträge
 

Event aus einer Klasse soll auf Form etwas ausführen

27/12/2008 - 14:26 von Thomas Hübner | Report spam
Hi NG,

Sorry für das dàmliche Subject.

Folgender Aufbau einer Klasse für ein simples SSH Terminal mit PLink.
Ganz unten in der Sub OutputHandler fülle ich eine Queue. PlinkTerminal
ist auf der Form instanziert als "PIO". Nun möchte ich aus OutputHandler
heraus ein Event auslösen was folgenden code aulöst (die Queue also in
eine Textbox entleert)

Do While PIO.outLines.Count > 0
SyncLock PIO.outLines
tbo_Log.Text &= PIO.outLines.Dequeue
End SyncLock
Loop


Wenn ich o.g. Code über einen Timer feuere klappt das wunderbar ist aber
unnütze Performance / CPU Last


Imports System.IO
Imports System.Text
Imports System.Diagnostics

Public Class PlinkTerminal
Private _outLines As New Queue(Of String)
Private Output As StringBuilder = Nothing
Private sw As StreamWriter
Private Plink As Process
Private stopConnection As Boolean = False

Public ReadOnly Property outLines() As Queue(Of String)
Get
Return _outLines
End Get
End Property

Public Sub New(ByVal PuttyBookmark As String)
Plink = New Process()
Dim PlinkLocation As String = Application.StartupPath &
"\plink.exe"
Plink.StartInfo.FileName = PlinkLocation
Plink.StartInfo.Arguments = "-load " & PuttyBookmark
Plink.StartInfo.UseShellExecute = False
Plink.StartInfo.CreateNoWindow = True
Plink.StartInfo.RedirectStandardOutput = True
Plink.StartInfo.RedirectStandardInput = True
AddHandler Plink.OutputDataReceived, AddressOf OutputHandler
End Sub

Public Sub New(ByVal ServerIP As String, ByVal Username As String,
ByVal Password As String)
Plink = New Process()
Dim PlinkLocation As String = Application.StartupPath &
"\plink.exe"
Plink.StartInfo.FileName = PlinkLocation
Plink.StartInfo.Arguments = "-ssh -l " & Username & " -pw " &
Password & " " & Username & "@" & ServerIP ' & " -batch "
Plink.StartInfo.UseShellExecute = False
Plink.StartInfo.CreateNoWindow = True
Plink.StartInfo.RedirectStandardOutput = True
Plink.StartInfo.RedirectStandardInput = True
AddHandler Plink.OutputDataReceived, AddressOf OutputHandler
End Sub

Public Sub New(ByVal PlinkParams As String, ByVal paramsused As
Boolean)
Plink = New Process()
Dim PlinkLocation As String = Application.StartupPath &
"\plink.exe"
Plink.StartInfo.FileName = PlinkLocation
Plink.StartInfo.Arguments = PlinkParams
Plink.StartInfo.UseShellExecute = False
Plink.StartInfo.CreateNoWindow = True
Plink.StartInfo.RedirectStandardOutput = True
Plink.StartInfo.RedirectStandardInput = True
AddHandler Plink.OutputDataReceived, AddressOf OutputHandler
End Sub

Public Sub OpenConnection()
Plink.Start()
sw = Plink.StandardInput
Plink.BeginOutputReadLine()
End Sub

Public Sub CloseConnection()
Plink.Close()
End Sub

Public Sub SendMessage(ByVal Message As String)
sw.WriteLine(Message)
End Sub

Private Sub OutputHandler(ByVal sendingProcess As Object, ByVal
outLine As DataReceivedEventArgs)
Try
If Not String.IsNullOrEmpty(outLine.Data) Then
SyncLock _outLines
_outLines.Enqueue(outLine.Data.Replace(vbLf,
vbNewLine) & vbNewLine)
End SyncLock
End If
Catch ex As Exception
'MsgBox(ex.Message)
End Try
End Sub

End Class
 

Lesen sie die antworten

#1 Elmar Boye
31/12/2008 - 11:26 | Warnen spam
Hallo Thomas,

Thomas Hübner schrieb:
Nun möchte ich aus OutputHandler heraus ein Event auslösen was folgenden code aulöst
(die Queue also in eine Textbox entleert)



Das kannst Du auch. Nur mußt Du den Thread übergreifenden Zugriff
berücksichtigen.


Do While PIO.outLines.Count > 0
SyncLock PIO.outLines
tbo_Log.Text &= PIO.outLines.Dequeue
End SyncLock
Loop



Der Code ist so nicht threadsicher, denn auch der Zugriff auf
Count muß im SyncLock erfolgen.
Wobei Du generell solchen Code nicht über unterschiedliche Module
verteilen solltest, da das schnell sehr unübersichtlich wird.

Ich habe unten mal eine SyncQueue Klasse angehàngt, die Du
anstatt dessen verwenden kannst und alle Sperren intern abhandelt.

Für das Ereignis könnte das

Public Class PlinkTerminal


Private _outLines As New SyncQueue(Of String)
[...]



Public Event DataAvailable As EventHandler

Public ReadOnly Property outLines() As SyncQueue(Of String)

Private Sub OutputHandler(ByVal sendingProcess As Object, _
ByVal outLine As DataReceivedEventArgs)


_outLines.Enqueue(outLine.Data.Replace(vbLf,
RaiseEvent DataAvailable(Me, EventArgs.Empty)
End Sub
End Class

Public ReadOnly Property outLines() As Queue(Of String)
Get
Return _outLines
End Get
End Property




Tipp am Rande: Anstatt den Konstruktorcode immer zu wiederholen,
kannst Du die Basisinitialisierung in Sub New zusammenfassen
und via Me.New() darauf zugreifen.

Und im verarbeitenden Formular könnte das dann wie folgt aussehen:

' Handler hinzufügen:
AddHandler terminal.DataAvailable, AddressOf ProcessDataAvailable

' Handler für die Verarbeitung
Private Sub ProcessDataAvailable(ByVal sender As Object, ByVal e As EventArgs)
Dim outputHandler As New Action(Of String)(AddressOf AppendToTextBox)
Do
Dim text As String = terminal.outLines.Dequeue()
Me.Invoke(outputHandler, text)
Application.DoEvents()
Loop Until terminal.outLines.IsEmpty
End Sub

Private Sub AppendToTextBox(ByVal text As String)
Me.outputTextBox.AppendText(text)
End Sub


Zum Abschluß noch die SyncQueue, aufs wesentliche reduziert.

Imports System.Threading
Imports System.Collections.Generic

Public Class SyncQueue(Of T)
Private ReadOnly _queue As Queue(Of T)

Public Sub New()
Me.New(Nothing)
End Sub

Public Sub New(ByVal queue As Queue(Of T))
If queue Is Nothing Then
_queue = New Queue(Of T)
Else
_queue = queue
End If
End Sub

Public Sub Enqueue(ByVal item As T)
SyncLock (Me)
_queue.Enqueue(item)
Monitor.Pulse(Me)
End SyncLock
End Sub

Public Function Dequeue() As T
SyncLock (Me)
While (_queue.Count = 0)
Monitor.Wait(Me)
End While
Return _queue.Dequeue()
End SyncLock
End Function

Public Function TryDequeue(ByRef item As T) As Boolean
SyncLock (Me)
If _queue.Count = 0 Then
item = Nothing
Return False
Else
item = _queue.Dequeue()
Return True
End If
End SyncLock
End Function

Public ReadOnly Property Count() As Integer
Get
SyncLock (Me)
Return _queue.Count
End SyncLock
End Get
End Property

Public ReadOnly Property IsEmpty() As Boolean
Get
SyncLock (Me)
Return (_queue.Count = 0)
End SyncLock
End Get
End Property
End Class

Gruß Elmar

Ähnliche fragen