M
Mobileboy36
Hi Group,
I built a TCP server handling multiple clients simultanously; everything is
working fine, except updating a listbox lstclients based on the funtion
_MyTcpServer.GetConnectedClients. Cross thread problems are known for me but
I don't understand the way
to handle them. Last year I found SafeUIInvoker on
http://weblogs.asp.net/rosherove/archive/2005/05/23/408511.aspx.
(you can find it below). Do you think this SafeUIInvoker is a possible way
to handle with cross thread exceptions?
Or can I simply set me.CheckForIllegalCrossThreadCalls to False?
Or is there another way to make the _MyTcpServer.GetConnectedClients thread
safe?
' A WINDOWS FORM
' --------------
Private Sub OnClientConnected(ByVal Sender As Object, ByVal e As
MyTcpServerAndClientEventArgs) Handles _MyTcpServer.OnClientConnected
FillListBox()
End Sub
Private Sub OnClientDisConnected(ByVal Sender As Object, ByVal e As
MyTcpServerAndClientEventArgs) Handles _MyTcpServer.OnClientDisConnected
FillListBox()
End Sub
Private Sub OnClientInfoListUpdated() Handles
_MyTcpServer.OnClientInfoListUpdated
FillListBox()
End Sub
Private Sub FillListBox()
' used to refill the listbox lstclients with clientnames when a new client
Connects, Disconnects or
If SafeUIInvoker.Invoke(Me) Then
Exit Sub
End If
lstClients.Items.Clear()
lstClients.ValueMember = "ci"
lstClients.DisplayMember = "Id"
For Each Ci As ClientInfo In _MyTcpServer.GetConnectedClients
lstClients.Items.Add(Ci)
Next
End Sub
'
' The class SafeUIInvoker
' -----------------------
' THE SafeUIInvoker Class found last year on
http://weblogs.asp.net/rosherove/archive/2005/05/23/408511.aspx
' Examples at the end of this file
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Reflection
''' <summary>
''' Use this class if you have to update user controls in events generated
''' from within another thread
''' Example usage:
''' If SafeUIInvoker.Invoke(Me, Sender, e) Then
''' Exit Sub
''' End If
''' </summary>
''' <remarks></remarks>
Public Class SafeUIInvoker
Private Shared ReadOnly _instance As New SafeUIInvoker()
Private Sub New()
End Sub
Private Shared _ownTypeName As String = GetType(SafeUIInvoker).Name
Public Shared Function getOriginalCallingMethod() As MemberInfo
Dim trace As New StackTrace()
For i As Integer = 1 To trace.FrameCount - 1
Dim pastFrame As StackFrame = trace.GetFrame(i)
Dim pastMethod As MemberInfo = pastFrame.GetMethod()
If pastMethod.DeclaringType.Name <> _ownTypeName Then
Return pastMethod
End If
Next
Return Nothing
End Function
Private Function invokeSafely(ByVal thisTarget As Object, ByRef
returnValue As Object, ByVal ParamArray realArgs As Object()) As Boolean
returnValue = Nothing
'Any Form inherits from Control which implements this interfaces
'this interfaces allows us to determine whethere we are on the UI
thread or not
'and to invoke the method on the UI thread usine Control.Invoke() if
needed
Dim invokeableObject As ISynchronizeInvoke = TryCast(thisTarget,
ISynchronizeInvoke)
If invokeableObject Is Nothing OrElse (Not
invokeableObject.InvokeRequired) Then
Return False
End If
Dim callerMethodInfo As MethodInfo =
TryCast(getOriginalCallingMethod(), MethodInfo)
If callerMethodInfo Is Nothing Then
Return False
End If
Dim delgateToLocalMethodThatTakesAllArguments As New
UISafeInvocationDelegate(AddressOf _instance.UIThreadInvocation)
Dim passDownArgsToRealMethod As Object() = New Object()
{callerMethodInfo, invokeableObject, realArgs}
returnValue =
invokeableObject.Invoke(delgateToLocalMethodThatTakesAllArguments,
passDownArgsToRealMethod)
Return True
End Function
Private Delegate Function UISafeInvocationDelegate(ByVal method As
MethodInfo, ByVal target As Object, ByVal args As Object()) As Object
'private delegate object UISafeInvocationDelegate(MethodInfo method,
object target, params object[] args);
Private Function UIThreadInvocation(ByVal method As MethodInfo, ByVal
target As Object, ByVal ParamArray args As Object()) As Object
'if we're here we're already inside the UI thread
'as we've been invoked from a Control.Invoke method
'so we simply forward the call to the *real* method on the target
object
Return method.Invoke(target, args)
End Function
#Region "public interface"
Public Shared Function Invoke(ByVal target As Object, ByVal ParamArray
args As Object()) As Boolean
Dim result As Object
Return _instance.invokeSafely(target, result, args)
End Function
Public Shared Function InvokeAndReturn(ByVal thisInstanceTarget As
Object, ByVal ParamArray methodParams As Object()) As Object
Dim result As Object
_instance.invokeSafely(thisInstanceTarget, result, methodParams)
Return result
End Function
#End Region
End Class
''EXAMPLE: this shows how to work with the TeamAgile SafeUIInvoker class
'Public Class MyForm
' Inherits Form
' Public Sub ThreadSafeEmptyMethod()
' If SafeUIInvoker.Invoke(Me) Then
' Return
' End If
' 'do whatever GUI things you want here!
' End Sub
' Public Sub ThreadSafeMethodWithParams(ByVal i As Integer, ByVal str As
String)
' If SafeUIInvoker.Invoke(Me, i, str) Then
' Return
' End If
' 'do whatever GUI things you want here!
' End Sub
' Public Function ThreadSafeMethodWithParamsAndReturnValue(ByVal i As
Integer, ByVal str As String) As Integer
' If InvokeRequired Then
' Return SafeUIInvoker.InvokeAndReturn(Me, i, str)
' End If
' 'do whatever GUI things you want here!
' TextBox1.Text = "whatever"
' Return 1
' End Function
'End Class
I built a TCP server handling multiple clients simultanously; everything is
working fine, except updating a listbox lstclients based on the funtion
_MyTcpServer.GetConnectedClients. Cross thread problems are known for me but
I don't understand the way
to handle them. Last year I found SafeUIInvoker on
http://weblogs.asp.net/rosherove/archive/2005/05/23/408511.aspx.
(you can find it below). Do you think this SafeUIInvoker is a possible way
to handle with cross thread exceptions?
Or can I simply set me.CheckForIllegalCrossThreadCalls to False?
Or is there another way to make the _MyTcpServer.GetConnectedClients thread
safe?
' A WINDOWS FORM
' --------------
Private Sub OnClientConnected(ByVal Sender As Object, ByVal e As
MyTcpServerAndClientEventArgs) Handles _MyTcpServer.OnClientConnected
FillListBox()
End Sub
Private Sub OnClientDisConnected(ByVal Sender As Object, ByVal e As
MyTcpServerAndClientEventArgs) Handles _MyTcpServer.OnClientDisConnected
FillListBox()
End Sub
Private Sub OnClientInfoListUpdated() Handles
_MyTcpServer.OnClientInfoListUpdated
FillListBox()
End Sub
Private Sub FillListBox()
' used to refill the listbox lstclients with clientnames when a new client
Connects, Disconnects or
If SafeUIInvoker.Invoke(Me) Then
Exit Sub
End If
lstClients.Items.Clear()
lstClients.ValueMember = "ci"
lstClients.DisplayMember = "Id"
For Each Ci As ClientInfo In _MyTcpServer.GetConnectedClients
lstClients.Items.Add(Ci)
Next
End Sub
'
' The class SafeUIInvoker
' -----------------------
' THE SafeUIInvoker Class found last year on
http://weblogs.asp.net/rosherove/archive/2005/05/23/408511.aspx
' Examples at the end of this file
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Reflection
''' <summary>
''' Use this class if you have to update user controls in events generated
''' from within another thread
''' Example usage:
''' If SafeUIInvoker.Invoke(Me, Sender, e) Then
''' Exit Sub
''' End If
''' </summary>
''' <remarks></remarks>
Public Class SafeUIInvoker
Private Shared ReadOnly _instance As New SafeUIInvoker()
Private Sub New()
End Sub
Private Shared _ownTypeName As String = GetType(SafeUIInvoker).Name
Public Shared Function getOriginalCallingMethod() As MemberInfo
Dim trace As New StackTrace()
For i As Integer = 1 To trace.FrameCount - 1
Dim pastFrame As StackFrame = trace.GetFrame(i)
Dim pastMethod As MemberInfo = pastFrame.GetMethod()
If pastMethod.DeclaringType.Name <> _ownTypeName Then
Return pastMethod
End If
Next
Return Nothing
End Function
Private Function invokeSafely(ByVal thisTarget As Object, ByRef
returnValue As Object, ByVal ParamArray realArgs As Object()) As Boolean
returnValue = Nothing
'Any Form inherits from Control which implements this interfaces
'this interfaces allows us to determine whethere we are on the UI
thread or not
'and to invoke the method on the UI thread usine Control.Invoke() if
needed
Dim invokeableObject As ISynchronizeInvoke = TryCast(thisTarget,
ISynchronizeInvoke)
If invokeableObject Is Nothing OrElse (Not
invokeableObject.InvokeRequired) Then
Return False
End If
Dim callerMethodInfo As MethodInfo =
TryCast(getOriginalCallingMethod(), MethodInfo)
If callerMethodInfo Is Nothing Then
Return False
End If
Dim delgateToLocalMethodThatTakesAllArguments As New
UISafeInvocationDelegate(AddressOf _instance.UIThreadInvocation)
Dim passDownArgsToRealMethod As Object() = New Object()
{callerMethodInfo, invokeableObject, realArgs}
returnValue =
invokeableObject.Invoke(delgateToLocalMethodThatTakesAllArguments,
passDownArgsToRealMethod)
Return True
End Function
Private Delegate Function UISafeInvocationDelegate(ByVal method As
MethodInfo, ByVal target As Object, ByVal args As Object()) As Object
'private delegate object UISafeInvocationDelegate(MethodInfo method,
object target, params object[] args);
Private Function UIThreadInvocation(ByVal method As MethodInfo, ByVal
target As Object, ByVal ParamArray args As Object()) As Object
'if we're here we're already inside the UI thread
'as we've been invoked from a Control.Invoke method
'so we simply forward the call to the *real* method on the target
object
Return method.Invoke(target, args)
End Function
#Region "public interface"
Public Shared Function Invoke(ByVal target As Object, ByVal ParamArray
args As Object()) As Boolean
Dim result As Object
Return _instance.invokeSafely(target, result, args)
End Function
Public Shared Function InvokeAndReturn(ByVal thisInstanceTarget As
Object, ByVal ParamArray methodParams As Object()) As Object
Dim result As Object
_instance.invokeSafely(thisInstanceTarget, result, methodParams)
Return result
End Function
#End Region
End Class
''EXAMPLE: this shows how to work with the TeamAgile SafeUIInvoker class
'Public Class MyForm
' Inherits Form
' Public Sub ThreadSafeEmptyMethod()
' If SafeUIInvoker.Invoke(Me) Then
' Return
' End If
' 'do whatever GUI things you want here!
' End Sub
' Public Sub ThreadSafeMethodWithParams(ByVal i As Integer, ByVal str As
String)
' If SafeUIInvoker.Invoke(Me, i, str) Then
' Return
' End If
' 'do whatever GUI things you want here!
' End Sub
' Public Function ThreadSafeMethodWithParamsAndReturnValue(ByVal i As
Integer, ByVal str As String) As Integer
' If InvokeRequired Then
' Return SafeUIInvoker.InvokeAndReturn(Me, i, str)
' End If
' 'do whatever GUI things you want here!
' TextBox1.Text = "whatever"
' Return 1
' End Function
'End Class