List Of <Class>, vorherige Elemente werden überschrieben

  • VB.NET

Es gibt 32 Antworten in diesem Thema. Der letzte Beitrag () ist von Igel.

    List Of <Class>, vorherige Elemente werden überschrieben

    Hallo zusammen,

    ich bin gerade dabei, ein Programm zu schreiben, dass auf einem Root-Server laufen soll und gerschiedene Game-Server verwalten soll.
    Ich hatte zunächst mit Arrays gearbeitet, was schon auch funktioniert hat, aber halt sehr sperrig ist (2-dimmensionales Array). Dann hab ich eine eigene structure definiert, hatte aber Probleme mit Schreibzugriffen und hab dann hier im Forum gelesen, dass Klassen hierfür doch ganz gut geeignet wären.

    Vorweg vielleicht noch: ich bin relativ neu dabei, was die Programmierung mit VB.net angeht, ich denke, das wird man auch am Code sehen :saint: generell bin ich für Tipps dankbar ^^

    Mein jetztiger Stand:

    VB.NET-Quellcode

    1. Enum ServerStatusField As Byte
    2. Stopped = 0
    3. Updating = 1
    4. UpdatingStart = 2
    5. Running = 3
    6. End Enum
    7. Public Class ServerInfo
    8. Private mstrServername As String
    9. Private mstrIP As String
    10. Private mintGameport As Integer
    11. Private mintQueryport As Integer
    12. Private mbytMaxplayers As Byte
    13. Private mbytTickrate As Byte
    14. Private mstrPath As String
    15. Private mintCPUAffinity As Integer
    16. Private mssfServerstate As ServerStatusField
    17. Private mintProcessID As Integer
    18. Private mintSteamCMDPID As Integer
    19. Private mbolDailyRestart As Boolean
    20. Public Property Servername As String
    21. Get
    22. Return mstrServername
    23. End Get
    24. Set(value As String)
    25. mstrServername = value
    26. End Set
    27. End Property
    28. '... hier folgen jetzt noch weitere Properties, aber das ist nicht das Problem


    Die Server selbst werden aus einer Datei zeilenweise ausgelesen. Dazu lese ich immer die entsprechenden Zeilen (zu einem Server) aus. So ein Datenpaket sieht folgendermaßen aus:

    Quellcode

    1. [test]
    2. IP = 1.2.3.4
    3. Gameport = 27015
    4. Queryport = 27016
    5. Maxplayers = 80
    6. Tickrate = 50
    7. Path = E:\
    8. CPUAffinity = 223
    9. DailyRestart = true


    Das Auslesen mache ich so (geht bestimmt besser, aber die ausgelesenen Daten stimmen 8-) ) :

    VB.NET-Quellcode

    1. Public ServerList As New List(Of ServerInfo)
    2. Sub LoadServers()
    3. If File.Exists("servers.ini") Then
    4. Dim TmpFileArray() As String = Split(My.Computer.FileSystem.ReadAllText("servers.ini"), vbCrLf)
    5. Dim srv As New ServerInfo
    6. For Each FLine As String In TmpFileArray
    7. If FLine <> "" Then
    8. If FLine.Substring(0, 1) = "[" Then
    9. srv.Servername = FLine.Substring(1, FLine.Length - 2)
    10. Else
    11. Select Case Trim(FLine.Substring(0, FLine.IndexOf(" ")))
    12. Case "IP"
    13. srv.IP = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    14. Case "Gameport"
    15. srv.Gameport = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    16. Case "Queryport"
    17. srv.Queryport = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    18. Case "Maxplayers"
    19. srv.Maxplayers = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    20. Case "Tickrate"
    21. srv.Tickrate = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    22. Case "Path"
    23. srv.Path = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    24. Case "CPUAffinity"
    25. srv.CPUAffinity = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    26. Case "DailyRestart"
    27. srv.DailyRestart = Trim(FLine.Substring(FLine.IndexOf("=") + 1))
    28. srv.Serverstate = ServerStatusField.Stopped
    29. srv.ProcessID = 0
    30. srv.SteamCMDPID = 0
    31. ServerList.Add(srv)
    32. Case Else
    33. End Select
    34. End If
    35. End If
    36. Next
    37. End If
    38. End Sub


    Mein Gedanke ist - mittlerweile glaube ich, dass das auch der Denkfehler ist - dass ich mit der Instanz srv zunächst alle Serverdaten sammle und dann am Ende ("DailyRestart" ist der letzte Eintrag) der Liste ServerList hinzufüge und dann eben die Instanz srv wieder mit dem nächsten Server neu befülle.
    Mein Problem ist nun, dass die Instanz srv zwar die richtigen Daten enthält und diese dann auch in die Liste übernommen werden, beim erneuten Befüllen der Instanz srv und anschließendem ServerList.add(srv) ALLE Einträge in der ServerList auf den aktuellen Inhalt der Instanz srv gesetzt werden. Somit erhalte ich am Ende eine ServerList, die zwar in der Anzahl an Elementen, denen der Server entspricht, diese aber alle die Daten des zuletzt eingelesenem Server enthalten.

    Daher meine Frage nun, was ich hier falsch gemacht habe?

    Ich stelle mir das so vor, dass nach der Übergabe von srv an ServerList, die Werte unabhängig von srv gespeichert sind und ich srv neu befüllen kann. Nach dem Verhalten, das ich sehe, scheint es mir aber eher so, dass die Instanz srv mit der Liste verlinkt ist..

    Vielen Dank für eure Hilfe!

    cya Igel
    Pack die Instanziierung von srv in die Schleife rein.
    Sonst hast du immer dieselbe Instanz.

    Kompiliert das eigentlich?
    Ich sehe z.B. in deiner ServerInfo-Klasse keine Property IP
    Wie kann dann eine Zeile wie srv.IP = Trim(FLine.Substring(FLine.IndexOf("=") + 1)) funktionieren?

    Edit:
    Ah OK, da gibt's nen Kommentar
    hier folgen jetzt noch weitere Properties, aber das ist nicht das Problem
    Tipp:
    Die ganze Get-Set-Orgie und die Private Variablen in der Klasse kannst du dir sparen, wenn du keine speziellen Get- oder Set-Funktionalität benötigst.

    Einfach

    VB.NET-Quellcode

    1. Public Class ServerInfo
    2. Public Property Servername As String
    3. Public Property IP As String
    4. Public Property Gameport As Integer
    5. Public Property Queryport As Integer
    6. '... usw.
    7. End Class

    Der Umweg über die lokalen Platzhalter ist seit einigen VS-Versionen nicht mehr nötig.
    Spötestens seit VS 2015 (ich glaube, auch schon früher) geht das direkt.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „petaod“ ()

    Willkommen im Forum.
    Das Problem dürfte eher sein, dass Du srv nur 1x per new instanziiert. Du musst es immer pro For-Schleifendurchgang machen, also die Zeilen#7 und #9 vertauschen. Sonst legst Du ein ServerInfo-Objekt an und überschreibst seine Daten bei jedem For-Schleifen-Durchgang. Und am Ende fügst Du der ServerList dasselbe Objekt immer und immer wieder hinzu, was man durch If ServerList(0) Is ServerList(1) Then Stop testen kann. Dreht man aber die beiden Zeilen um, erhält man x Objekte, die eben nicht dasselbe sind.

    Bevor Du weitermachst, bitte die empfohlenen VS-Einstellungen verwenden. Dann fällt der ganze VB6-Kram raus.
    Statt

    VB.NET-Quellcode

    1. If File.Exists("servers.ini") Then
    2. 'ewig viele Zeilen
    3. End If
    ist es einfacher, wenn Du schreibst:

    VB.NET-Quellcode

    1. If Not File.Exists("servers.ini") Then Exit Sub
    2. 'ewig viele Zeilen

    Verringert die Verschachtelungstiefe und lässt den Leser nicht in dem Glauben, dass am Ende vielleicht doch noch ein Else-Bereich kommt und was geschieht, wenn die Datei nicht existiert.
    Genauso Zeile#11: If FLine = "" Then Continue For

    ##########

    Wäre es nicht besser, einen Strukturdateityp wie XML oder JSON herzunehmen? Da gibt es effektive Parsingunterstützung und Du muss nicht die Daten per Hand auseinanderpfriemeln.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Vielen Dank für eure schnellen Antworten :)

    Ich merke, dass ich noch einiges Lernen muss :D

    @petaod: ich habe beim Schreiben der Klasse scheinbar nicht so viel gedacht, hätte so viel einfacher sein können :)

    @VaporiZed: Warum ich eine Datei nutze zum Daten auslesen, liegt zunächst daran, dass es sehr einfach ist, die Daten zu speichern. Klar die Verarbeitung ist vielleicht nicht so schön, vor allem wenn die Datei nicht genau die erwartete Struktur hat (hab ja auch keine Fehlerüberprüfung drin). Mein Ziel ist es eigentlich, das Ganze in einer lokalen Datenbank zu speichern, allerdings bekomme ich es nicht hin, in Visual Studio die Datenbank-Pakete nach zu installieren (oder auch mit zu installieren), weil das Ganze immer mit einem Fehler endet :(

    Quellcode

    1. Paket "sqlncli,version=15.1.61903.1040,chip=x64,language=de-DE" konnte nicht installiert werden.

    Habt ihr da ne Idee? ich hab schon gegoogled oder auch die VS Hilfe angeschaut, hat aber bisher nichts gebracht..

    Ich hoffe, ich kann eure Tipps morgen mal umsetzen, bin schon gespannt, was passiert wenn ich OPTION STRICT ON einbaue :D

    Vielen Dank schon mal!
    Einfacher als eine INI-Datei und viel einfacher als eine Datenbank:
    Du kannst deine ServerList zum abspeichern serialisieren und beim Einlesen deserialisieren.
    docs.microsoft.com/de-de/dotne…the-icollection-interface

    Dann brauchst du keine Datenbank, keine Konvertierung, keine Klimmzüge.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    @petaod
    Danke für den Hinweis, habs mir gleich mal angeschaut, Klimmzüge hats mich dennoch gekostet, aber jetzt gehts über XML :)

    ich habe jetzt mal die Option OPTION STRICT ON aktiviert und schon fast alle Fehler behoben, nur bei einem verstehe ich die Fehlermeldung nicht (folgenden Code habe ich aus einem Tutorial):

    VB.NET-Quellcode

    1. Sub SendToClients(ByVal Data As String)
    2. If ServerStatus = True Then
    3. If Clients.Count > 0 Then
    4. Try
    5. For Each Client As TcpClient In Clients
    6. Dim TX1 As New StreamWriter(Client.GetStream)
    7. '' Dim RX1 As New StreamReader(Client.GetStream)
    8. TX1.WriteLine(Data)
    9. TX1.Flush()
    10. Next
    11. Catch ex As Exception
    12. 'SendToClients(Data)
    13. MsgBox("SendToClients:" & vbCrLf & ex.Message)
    14. End Try
    15. End If
    16. End If
    17. End Sub


    beim Compilieren bekomme ich hier einen Fehler (SendToClients ist das Problem):

    VB.NET-Quellcode

    1. Private Sub BtnSend_Click(sender As Object, e As EventArgs) Handles BtnSend.Click
    2. Threading.ThreadPool.QueueUserWorkItem(AddressOf SendToClients, TxtSend.Text)
    3. End Sub


    Mit der Fehlermeldung Fehler BC36663 "Option Strict On" erlaubt keine Einschränkungen in impliziten Typkonvertierungen zwischen der Public Sub SendToClients(Data As String)-Methode und dem Delegaten "Delegate Sub WaitCallback(state As Object)". kann ich leider überhaupt nichts anfangen. :( Könnt ihr mir sagen, was das Problem ist?

    thx

    cya Igel

    Igel schrieb:

    Könnt ihr mir sagen, was das Problem ist?
    Thread-Prozeduren erwarten einen Parameter vom Typ Object als Parameter.
    Wenn Du da einen String reinschieben willst, musst Du ihn als Object empfangen und dann zu einem String konvertieren:

    VB.NET-Quellcode

    1. Sub SendToClients(ByVal Data2 As Object)
    2. Dim Data = Data2.ToString()
    3. ' ...
    4. End Sub

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!
    Wird nu vlt. bischen als OffTopic empfunden, aber
    Ich hab bislang noch nicht verstanden warum man diesen untypisierten Gurken-Aufruf verwenden sollte, um Nebenläufigkeit zu implementieren:

    Igel schrieb:

    VB.NET-Quellcode

    1. Threading.ThreadPool.QueueUserWorkItem(...)


    Schon immer konnte man auf typisierte Weise den Threadpool beauftragen, mittels Delegate.BeginInvoke:

    VB.NET-Quellcode

    1. Dim dlg As New Action(Of String)(AddressOf SendToClients)
    2. dlg.BeginInvoke(TxtSend.Text, AddressOf dlg.EndInvoke, Nothing)


    (Und neuerdings, mit Async/Await ist natürlich noch cooler.)

    Also ich mein das wirklich als Frage, weil man sieht dieses komische QueueUserWorkItem-Dingens ja wirklich öfter, und ich kann nur nachteiliges daran entdecken.
    @RodFromGermany: Danke, kann doch so einfach sein :)
    @ErfinderDesRades: Ich hab halt selbst nicht viel Ahnung, daher hab ich das aus einem Tutorial, da wurde es halt so gemacht und da es funktioniert, war das für mich i.O.

    Zum Thema Daten serialisieren:
    ich habe die Strukturen noch ein wenig erweitert und habe jetzt folgende Klassen:

    VB.NET-Quellcode

    1. <Serializable()>
    2. Public Class SettingsInfo
    3. Public AllowRemoteControl As Boolean
    4. Public RemotePort As Integer
    5. Public AutoRestartServer As Boolean
    6. Public SteamCmdPath As String
    7. Public AppID As Integer
    8. Public DailyServerRestart As Boolean
    9. Public DailyRestartTime As String
    10. Public DiscordSupportEnabled As Boolean
    11. Public DiscordSupportURL As String
    12. End Class
    13. <Serializable()>
    14. Public Class ServerInfo
    15. Public Servername As String
    16. Public IP As String
    17. Public Gameport As Integer
    18. Public Queryport As Integer
    19. Public Maxplayers As Byte
    20. Public Tickrate As Byte
    21. Public Path As String
    22. Public CPUAffinity As Integer
    23. Public Serverstate As Integer
    24. Public ProcessID As Integer
    25. Public SteamCMDPID As Integer
    26. Public DailyRestart As Boolean
    27. End Class
    28. <Serializable()>
    29. Public Class Cache
    30. Public Settings As New SettingsInfo
    31. Public Server As New List(Of ServerInfo)
    32. End Class


    Hier noch meine aktuelle Serialize-Funktion:

    VB.NET-Quellcode

    1. Public Shared Sub Serialize(ByVal data As Cache, sConfigFilePath As String)
    2. Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(Cache))
    3. Dim MySettings As New System.Xml.XmlWriterSettings()
    4. MySettings.Indent = True
    5. MySettings.CloseOutput = True
    6. Dim MyWriter As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(sConfigFilePath, MySettings)
    7. Dim tmp As New Cache
    8. tmp = data
    9. For idx As Byte = 0 To tmp.Server.Count - 1
    10. tmp.Server(idx).Serverstate = 0
    11. tmp.Server(idx).ProcessID = 0
    12. tmp.Server(idx).SteamCMDPID = 0
    13. Next
    14. XmlBuddy.Serialize(MyWriter, tmp)
    15. MyWriter.Flush()
    16. MyWriter.Close()
    17. End Sub


    Die 3 Variablen Serverstate, ProcessID und SteamCMDPID möchte ich eigentlich nicht speichern, da diese bei Programmstart immer mit Initialwerten belegt werden. Am liebsten würde ich diese beim Serialisieren unter den Tisch fallen lassen, aber da weiß ich schon gar nicht wie das gehen soll, da ich ja ein Objekt der Klasse Cache übergebe, die diese Variablen nun mal enthält. Also dachte ich mir, setze ich sie wenigsten auf 0 (Initialwert). Das habe ich innerhalb des Subs über das tmp-Objekt versucht. Was ich nun nicht verstehe ist, warum auch das ursprünglich übergebene Objekt data verändert wird, meines Wissens wird doch durch die Parameter-Übergabe der Funktion mit byval eine Kopie des Objekts innerhalb der Funktion angelegt und verwendet und nicht das Objekt selbst verändert. Und durch das neue tmp-Objekt sollte das ja erst recht nicht passieren..

    Gibt es eine Möglichkeit, die 3 Variablen komplett aus der XML-Datei raus fallen zu lassen (außer sie nachträglich aus der Datei zu löschen, was ja wirklich nicht sauber wäre..) oder wie schaffe ich es einen manipulierten Stand des aktuellen Objekts von Cache zu speichern?

    Danke schon mal :)

    cya Igel

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Igel“ ()

    Es geht noch einfacher. Es gibt die Xml-Serialize
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ​Private Sub SaveXml(Of T)(ByVal obj As T, ByVal fileName As String)
    2. Using fs As New FileStream(fileName, FileMode.Create)
    3. Dim xmlSer As New XmlSerializer(GetType(T))
    4. xmlSer.Serialize(fs, obj)
    5. fs.Flush()
    6. End Using
    7. End Sub
    8. Private Sub LoadXml(Of T)(ByRef obj As T, ByVal fileName As String)
    9. If File.Exists(fileName) Then
    10. Using fs As New FileStream(fileName, FileMode.Open)
    11. Dim xmlSer As New XmlSerializer(GetType(T))
    12. obj = DirectCast(xmlSer.Deserialize(fs), T)
    13. End Using
    14. End If
    15. End Sub


    Um Attribute zu ignorieren, gibt es das Zauberwort. XmlIgnoreAttribute​

    Freundliche Grüsse

    exc-jdbi

    Igel schrieb:

    Hier noch meine aktuelle Serialize-Funktion:
    Und wie rufst du die aktuell auf, bzw. versuchst das?

    Mit dem (ich finde: fragwürdigem) QueueUserWorkItem?
    Weil das dürfte ja unter Option Strict nicht funktionieren.
    Oder haste die Serialize-Funktion doch geändert, und so aktuell wie behauptet ist der gezeigte Code garnet?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @exc-jdbi: Cool danke, hätte ich jetzt nicht gedacht, dass es so einfach ist :)

    @ErfinderDesRades:
    Die Serialize-Funktion hat nichts mit dem Senden über den Socket zu tun. Dieser Button ist noch vom Tutorial drin, der wird später raus fliegen. Letztendlich soll es so sein, dass das Programm nur auf Anfragen von außen reagiert, also nicht von sich aus sendet. Dazu habe ich einen Handler, der auf eingehende Nachrichten wartet und dann eine Funktion aufruft, die die empfangenen Befehle plausibilisiert und dann eine Antwort über die Funktion SendToClients zurück sendet.

    ErfinderDesRades schrieb:


    Also ich mein das wirklich als Frage, weil man sieht dieses komische QueueUserWorkItem-Dingens ja wirklich öfter, und ich kann nur nachteiliges daran entdecken.

    Da ich überhaupt keine Erfahrung damit habe, konnte ich noch nichts nachteiliges Entdecken, außer dass es funktioniert, von daher ;)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Igel“ ()

    (ups - ich hatte Serialize mit SendToClient verwechselt)



    (Nachteiliges an QueueUserWorkItem()): Naja, deine SendToClient - Methode kannste damit ja nicht dem ThreadPool überantworten, bzw. nur unter Strict Off - also in einem Zustand, wo man (meist im ganzen Projekt) nicht mehr weiß, mit welchem Datentyp man eiglich arbeitet.
    Anners gesagt: Haupt-Nachteil an QueueUserWorkItem() ist, dasses ohne Typ-Umwandlung nicht Strict On - kompatibel ist.
    Andere Vorgehensweisen sind Strict On-kompatibel und kosten auch nicht mehr.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @Igel dann sei doch noch so Nett und zeige uns mal dein Ergebnis.
    Damit hier der komplett weg + Lösung zu sehen ist. So können wir falls jemand ein gleiches Anliegen hat z.b. hier hin verweisen.

    Und dann nicht vergessen das Thema als Erledigt zu markieren :)
    Grüße , xChRoNiKx

    Nützliche Links:
    Visual Studio Empfohlene Einstellungen | Try-Catch heißes Eisen
    Hallo @xChRoNiKx,

    klar kann ich machen, auch wenn ich eigentlich fast alles so umgesetzt habe, wie es hier alle empfohlen haben ;)

    Hier meine Klasse(n) fürs DataHandling:

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.Xml.Serialization
    3. Imports System.IO
    4. <Serializable()>
    5. Public Class SettingsInfo
    6. Public AllowRemoteControl As Boolean
    7. Public RemotePort As Integer
    8. Public AutoRestartServer As Boolean
    9. Public SteamCmdPath As String
    10. Public AppID As Integer
    11. Public DailyServerRestart As Boolean
    12. Public DailyRestartTime As String
    13. Public DiscordSupportEnabled As Boolean
    14. Public DiscordBotName As String
    15. Public DiscordSupportURL As String
    16. End Class
    17. <Serializable()>
    18. Public Class ServerInfo
    19. Public Servername As String
    20. Public IP As String
    21. Public Gameport As Integer
    22. Public Queryport As Integer
    23. Public Maxplayers As Byte
    24. Public Tickrate As Byte
    25. Public Path As String
    26. Public CPUAffinity As Integer
    27. <XmlIgnore()> Public Serverstate As Byte
    28. <XmlIgnore()> Public ProcessID As Integer
    29. <XmlIgnore()> Public SteamCMDPID As Integer
    30. Public DailyRestart As Boolean
    31. Public DiscordMessages As Byte
    32. End Class
    33. <Serializable()>
    34. Public Class Cache
    35. Public Settings As New SettingsInfo
    36. Public Servers As New List(Of ServerInfo)
    37. End Class
    38. Public NotInheritable Class XmlSerializer
    39. Public Shared Sub Serialize(ByVal obj As Cache, sConfigFilePath As String)
    40. Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(Cache))
    41. Dim MySettings As New System.Xml.XmlWriterSettings()
    42. MySettings.Indent = True
    43. MySettings.CloseOutput = True
    44. Dim MyWriter As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(sConfigFilePath, MySettings)
    45. XmlBuddy.Serialize(MyWriter, obj)
    46. MyWriter.Flush()
    47. MyWriter.Close()
    48. End Sub
    49. Public Shared Function Deserialize(Of T)(ByVal xml As String) As T
    50. Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(T))
    51. Dim fs As New FileStream(xml, FileMode.Open)
    52. Dim reader As New Xml.XmlTextReader(fs)
    53. If XmlBuddy.CanDeserialize(reader) Then
    54. Dim tempObject As T = DirectCast(XmlBuddy.Deserialize(reader), T)
    55. reader.Close()
    56. Return tempObject
    57. Else
    58. Return Nothing
    59. End If
    60. End Function
    61. End Class


    Das Senden/Empfangen über TCP habe ich jetzt so umgesetzt:

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.IO
    3. Imports System.Net
    4. Imports System.Net.Sockets
    5. Imports System.Threading
    6. Public Class TCPControl
    7. Public Event MessageReceived(sender As TCPControl, Data As String)
    8. ' SERVER CONFIG
    9. Public TCPServerIP As IPAddress = IPAddress.Parse("127.0.0.1")
    10. Public TCPServerPort As Integer = 4305
    11. Public TCPServer As TcpListener
    12. Private CommThread As Thread
    13. Private IsActivated As Boolean = True
    14. ' CLIENT
    15. Private Client As TcpClient
    16. Private ClientData As StreamReader
    17. Public Sub New()
    18. TCPServer = New TcpListener(TCPServerIP, TCPServerPort)
    19. TCPServer.Start()
    20. CommThread = New Thread(New ThreadStart(AddressOf Listening))
    21. CommThread.Start()
    22. End Sub
    23. Public Sub StartServer()
    24. IsActivated = True
    25. End Sub
    26. Public Sub StopServer()
    27. IsActivated = False
    28. End Sub
    29. Private Sub Listening()
    30. ' CREATE LISTENER LOOP
    31. Do Until IsActivated = False
    32. ' ACEPT INCOMING CONNECTIONS
    33. If TCPServer.Pending = True Then
    34. Client = TCPServer.AcceptTcpClient
    35. ClientData = New StreamReader(Client.GetStream)
    36. End If
    37. ' RAISE EVENT FOR INCOMING MESSAGES
    38. If ClientData IsNot Nothing Then
    39. Try
    40. RaiseEvent MessageReceived(Me, ClientData.ReadLine)
    41. Catch ex As Exception
    42. MsgBox(ex.Message)
    43. End Try
    44. End If
    45. ' REDUCE CPU USAGE
    46. Thread.Sleep(100)
    47. Loop
    48. End Sub
    49. Sub SendToClient(ByVal Data As Object, Optional ByVal SilentMode As Boolean = False)
    50. If IsActivated = True Then
    51. Try
    52. Dim TX As New StreamWriter(Client.GetStream)
    53. TX.WriteLine(Data.ToString)
    54. TX.Flush()
    55. Catch ex As Exception
    56. If SilentMode = False Then MsgBox("SendToClient:" & vbCrLf & ex.Message)
    57. End Try
    58. End If
    59. End Sub
    60. End Class


    und der dazugehörige Code im Hauptform:

    VB.NET-Quellcode

    1. Public Class Mainfrm
    2. Private TCPServer As TCPControl
    3. Private Delegate Sub UpdateTextDelegate(RTB As RichTextBox, txt As String)
    4. Private Sub Mainfrm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    5. TCPServer = New TCPControl
    6. AddHandler TCPServer.MessageReceived, AddressOf OnLineReceived
    7. TCPServer.StartServer()
    8. '...
    9. end sub
    10. ' UPDATE TEXTBOX
    11. Private Sub UpdateText(RTB As RichTextBox, txt As String)
    12. If RTB.InvokeRequired Then
    13. RTB.Invoke(New UpdateTextDelegate(AddressOf UpdateText), New Object() {RTB, txt})
    14. Else
    15. If txt IsNot Nothing Then RTB.AppendText("[" & Format(Now, "dd.MM.yyyy HH:mm:ss") & "] " & txt & vbCrLf)
    16. End If
    17. End Sub
    18. Private Sub OnLineReceived(sender As TCPControl, Data As String)
    19. UpdateText(TaReceive, Data)
    20. ValidateRequestAndRespond(Data)
    21. End Sub
    22. ' ...
    23. end class


    So, ich hoffe das hilft dem ein oder anderen weiter 8-)

    cya Igel
    (Der Code ist wohl übernommen und abgewandelt von stackoverflow)
    Einige Punkte sind nicht optimal (z.B. Fange nur Exceptions ab, die Du kennst und sinnvoll bearbeiten kannst. oder die Signatur des OnLineReceived-EventHandlers. Auch das Thread.Sleep(100) ist m.E. eher ne Ausbremsung als ne Entlastung.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Ja, sicher hab ich Code von verschiedenen Quellen verwendet. Ich bin leider nicht der VB-Crack, der alles selbst hinbekommt, ich bin eher froh, dass es soweit funktioniert ;)

    Wenn ich wüsste, wie alles "richtig" programmiert werden muss, dann hätte ich sicherlich einiges anders gemacht 8-)

    Wie wäre es denn richtig mit dem OnLineReceived-EventHandler?

    cya Igel

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Igel“ ()

    Macht ja nix, dass Du andere Quellen verwendest. Mir ging's eher darum, dass allen Lesern klar ist, woher die Codeänderungen und -inspirationen von Post#1 auf P#16 kamen, damit man nicht allzu überrascht ist.
    Bzgl. Eventhandler-Signatur: Standardmäßig haben diese die Form (sender As Object, e As WhateverEventArgs). Dann kann man auch schreiben

    VB.NET-Quellcode

    1. Public Event MessageReceived As EventHandler(Of String)
    2. '...
    3. RaiseEvent MessageReceived(Me, ClientData.ReadLine)
    4. '...
    5. Private Sub OnLineReceived(sender As Object, e As String)

    Normalerweise haut man aber noch ne von EventArgs abgeleitete Klasse dazu:

    VB.NET-Quellcode

    1. Public Event MessageReceived As EventHandler(Of MessageReceiveEventArgs)
    2. Public Class MessageReceiveEventArgs : Inherits EventArgs
    3. Public Message As String
    4. End Class
    5. '...
    6. RaiseEvent MessageReceived(Me, New MessageReceiveEventArgs With {.Message = ClientData.ReadLine})
    7. '...
    8. Private Sub OnLineReceived(sender As Object, e As MessageReceiveEventArgs)

    Da kann man dann auch richtig viel Info reinhauen als in nen schnöden String - falls gebraucht.

    zum Thema Try-Catch:
    Was für Exceptions erwartest Du bei dem Code

    VB.NET-Quellcode

    1. If ClientData IsNot Nothing Then
    2. Try
    3. RaiseEvent MessageReceived(Me, ClientData.ReadLine)
    4. Catch ex As Exception
    5. MsgBox(ex.Message)
    6. End Try
    7. End If

    btw: MsgBox = VB6 -> Bevor Du weitermachst, bitte die empfohlenen VS-Einstellungen verwenden.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Danke für die Infos, werd ich mir mal anschauen und auch - so gut ich kann - umsetzen :saint:

    Das mit der MessageBox im Catch ist sowieso bescheuert und werde ich rausnehmen (war noch drin fürs testen), da ich ja auch mein Programm über ein Webinterface steuern können will und da ist dann eine Meldung von ner MessageBox eh vollkommen am Ziel vorbei. :D

    Dass Try-Catch vermieden werden sollte, sehe ich prinzipiell auch so, eine sinnvolle Fehlerbehandlung ist natürlich der richtige Weg, allerdings kenn ich mich hier einfach noch zu wenig aus, um eine sinnvolle und umfassende Fehlerbehandlung in bestimmten Funktionsabläufen zu machen (z.B. TCP-Kommunikation)..

    cya Igel

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Igel“ ()