﻿Imports System.Net.Sockets
Imports System.Net
Imports System.Collections.Generic
Imports System.Windows.Forms
'Implements IDisposable


''' <summary>
''' Easy networking class. Copyright 2012 PlazSoft. All rights reserved.
''' </summary>
''' <remarks></remarks>
Public Class clsNetEasy

    Enum serverResponse
        ActivationOK = 0
        InvalidSerial = 1
        KeyDisabled = 2
        ReactivationNeeded = 3
        PreviousTrialFound = 4
        TrialOk = 5
        TrialExpired = 6
        BadActivation = 7
        UnknownError = 8
        ServerNotFound = 9
        ActivateLimitExceeded = 10
        ValidRetailKey = 11
        ValidTrialKey = 12
        ValidWebKey = 13
    End Enum

    Public Structure IPinfo
        Dim IPAddress As String
        Dim SubnetMask As String
    End Structure

    Public Structure ClientInfo
        Dim ip As System.Net.IPEndPoint
        Dim username As String
        Dim stream As NetworkStream
        Dim status As connectionStatusEnum
        Dim lastPing As DateTime
    End Structure

    Public Enum connectionStatusEnum
        connected
        Verified
    End Enum

    'Dim streamList As New List(Of NetworkStream)



    '
    ' Dim threadList As New List(Of threading.thread)
    Dim connection As TcpClient = Nothing
    Dim checked As Integer = 0
    Public myIpInfo As New List(Of IPinfo)
    Public addresslist As New List(Of String)
    Public clientList As New List(Of ClientInfo)
    Dim server As TcpListener = Nothing
    Dim _close As Boolean = False
    Dim _username As String
    Dim Disposing As Boolean = False
    Dim serverThread As Threading.Thread = Nothing

    Dim serverPort As Integer
    Dim clientPort As Integer

    'Private Delegate Sub testDelegate()
    'Private Delegate Function pingAsyncDelegate(ByVal ip As String) As String

    Public Event ServerFound(ByVal address As String)
    Public Event ClientConnected(ByVal tempClient As ClientInfo)
    Public Event DataArrival(sender As Object, message As String)
    Dim _ServerString As String


    ''' <summary>
    ''' Sets up the networking and finds the IP addresses available on the same machine (so they can be used later for hosting...). Servers should handle something like this: "{LOGIN123456789};username;password"
    ''' </summary>
    ''' <param name="_clientPort"></param>
    ''' <param name="_serverPort"></param>
    ''' <param name="isServer">Is this going to be a server hosting?</param>
    ''' <remarks></remarks>
    Public Sub New(_clientPort As Integer, _serverPort As Integer, isServer As Boolean)
        serverPort = _serverPort
        clientPort = _clientPort
        If isServer = True Then 'only check if it is a server
            findserverworker()
        End If
    End Sub


    ''' <summary>
    '''  This is used internally to make TCP connection.
    ''' </summary>
    ''' <param name="ip">The IP Address of the remote system</param>
    ''' <param name="port">The port to establish the connection on</param>
    ''' <remarks></remarks>
    Private Sub createTCPconnection(ByVal ip As String, ByVal port As Integer)
        Try
            If connection Is Nothing Then
                connection = New TcpClient
            Else
                connection.Close()
                connection = New TcpClient
            End If


            connection.Connect(ip, port)
        Catch ex As Exception
        End Try
    End Sub

    ''' <summary>
    ''' Connects to a server. Has some old activation server protocal confirmations. For refrence: 127.1.1.1 is the self address.
    ''' </summary>
    ''' <param name="ip">The ip address to connect to.</param>
    ''' <param name="port">The remote port to connect to.</param>
    ''' <param name="username">The username to store.</param>
    ''' <param name="password">Can be an empty string. We recommend SHA2 or better password hashes and encrypting communications.</param>
    ''' <param name="pingSendString">What to send to the server. StudyX uses STXSVR_V2_CHK</param>
    ''' <param name="pongReturnString">What to expect from the server (NOT CaSe sensitve). StudeyX uses stxsvr_v2_ack</param>
    ''' <remarks></remarks>
    Public Sub connectToServer(ByVal ip As String, ByVal port As Integer, username As String, password As String, pingSendString As String, pongReturnString As String)
        If connected Then
            close()
        End If

        createTCPconnection(ip, port) '"127.1.1.1" "192.168.1.100"

        If connected Then
            Dim query() As Byte = System.Text.Encoding.ASCII.GetBytes(pingSendString & ";" & username & ";" & password) '"STXSVR_V2_CHK")
            sendData(query, stream)

            Dim data() As Byte = receiveData(stream)
            If System.Text.Encoding.ASCII.GetString(data).ToLower.Contains(pongReturnString.ToLower) Then '"stxsvr_v2_ack"
                Dim split() As String = System.Text.Encoding.ASCII.GetString(data).Split(";")

                query = System.Text.Encoding.ASCII.GetBytes(ip)
                sendData(query, stream)
                sendText("[LOGIN123456789Tt];" & username & ";" & password)
                startStreamWatcher()
            End If
        End If
    End Sub

    Public ReadOnly Property stream As NetworkStream
        Get
            If connection IsNot Nothing AndAlso connection.Connected Then 'TODO: Test  AndAlso connection.Connected
                Return connection.GetStream
            Else
                Return Nothing
            End If

        End Get
    End Property



    Public Shared Function makeConnection(connectionList As List(Of ConnectionInfo)) As NetworkStream
        Dim stream As NetworkStream = Nothing
        Try

            For Each myServerInfo As ConnectionInfo In connectionList 'JC.Global.serverList
                stream = Online.connect(myServerInfo)
                If stream IsNot Nothing Then Exit For
            Next


            If stream IsNot Nothing Then
                Return stream
            Else
                Return Nothing
                MsgBox("Could not connect to activation server.")
            End If
        Catch ex As Exception

#If DEBUG Then
            MsgBox("Could not connect to activation server." & vbNewLine & ex.Message)
#Else
             MsgBox("Could not connect to activation server.")
#End If
            Return Nothing

        End Try
    End Function




    'Public Shared Function SendToServer(ByVal msg As String) As serverResponse
    '    Dim action As String = "" 'hold action on return
    '    Dim stream As NetworkStream = makeConnection()

    '    If stream IsNot Nothing Then
    '        JC.Online.sendText(JC.Global.PublicKey, "[studyx]" & msg, stream)


    '        Dim strData As String = System.Text.Encoding.ASCII.GetString(JC.Online.receiveData(JC.Global.PublicKey, stream))

    '        If JC.Global.RegBox.ProgressBar1.Value < JC.Global.RegBox.ProgressBar1.Maximum Then
    '            JC.Global.RegBox.ProgressBar1.Value += 1
    '        End If

    '        action = JC.Activate.getAction(strData) ' get action and assign
    '        Dim response As serverResponse = JC.Activate.ProcessReturn(strData, action, stream) 'preform action
    '        Return response

    '        If JC.Global.RegBox.ProgressBar1.Value < JC.Global.RegBox.ProgressBar1.Maximum Then
    '            JC.Global.RegBox.ProgressBar1.Value += 1
    '        End If

    '        stream.Close()
    '        stream = Nothing
    '    Else
    '        Return JC.Net.serverResponse.ServerNotFound
    '    End If
    '    'If modActivation.Sendback = True Then
    '    '    Do Until modActivation.Sendback = False
    '    '        Select Case acttype
    '    '            Case "RReAct"
    '    '                'winsock::

    '    '            Case Else
    '    '        End Select
    '    '    Loop
    '    'End If

    'End Function




    ''' <summary>
    ''' Checks for internet access.
    ''' </summary>
    Public Shared Function gotInternet() As Boolean
        Dim req As System.Net.HttpWebRequest
        Dim res As System.Net.HttpWebResponse
        gotInternet = False
        Try
            req = CType(System.Net.HttpWebRequest.Create("http://www.studyx.com"), System.Net.HttpWebRequest)
            res = CType(req.GetResponse(), System.Net.HttpWebResponse)
            req.Abort()
            If res.StatusCode = System.Net.HttpStatusCode.OK Then
                gotInternet = True
            End If
        Catch weberrt As System.Net.WebException
            gotInternet = False
        Catch except As Exception
            gotInternet = False
        End Try
    End Function

    ''' <summary>
    ''' Watches a stream while a user is connected? Stays alive while user is connected?
    ''' </summary>
    Private Sub streamWatcher(ByVal objStream As Object)
        Try
            Dim curStream As NetworkStream = CType(objStream, NetworkStream)
            Do Until Disposing = True Or curStream Is Nothing

                If curStream.DataAvailable Then
                    Dim message As String = receiveText(curStream)
                    RaiseEvent DataArrival(Me, message)
                End If
                Threading.Thread.Sleep(10)

            Loop
        Catch
        End Try
    End Sub

    ''' <summary>
    ''' Closes the connection.
    ''' </summary>
    Public Sub close()
        If connection IsNot Nothing Then
            If connection.Connected Then
                connection.Close()
            End If
            connection = Nothing
        End If
        _close = True
        'Me.Dispose()
        Dispose(True) 'TODO: Test this
    End Sub

    ''' <summary>
    ''' Checks if it is connected.
    ''' </summary>
    Public ReadOnly Property connected As Boolean
        Get
            If connection IsNot Nothing Then
                Return connection.Connected
            Else
                If serverThread IsNot Nothing Then
                    If serverThread.IsAlive Then
                        Return True
                    End If
                End If
                Return False
            End If

        End Get
    End Property

    ' ''' <summary>
    ' ''' listbox1 is not used
    ' ''' </summary>
    'Public Sub findServer(ByRef listbox1 As ListBox, ByVal serverString As String)
    '    _ServerString = serverString
    '    Dim t As New Threading.Thread(AddressOf findserverworker)
    '    t.Start()
    'End Sub

    ''' <summary>
    ''' Finds the IP addresses available for the server.
    ''' </summary>
    ''' <param name="serverString"></param>
    ''' <remarks></remarks>
    Public Sub findServer(ByVal serverString As String)
        _ServerString = serverString
        Dim t As New Threading.Thread(AddressOf findserverworker)
        t.Start()
    End Sub

    ''' <summary>
    ''' Private function used by findServer. Finds the IP addresses available for the server.
    ''' </summary>
    Private Sub findserverworker()

        Dim mc As New Management.ManagementClass("Win32_NetworkAdapterConfiguration")
        Dim nics As Management.ManagementObjectCollection


        nics = mc.GetInstances()

        For Each nic As Management.ManagementObject In nics
            If CBool(nic("ipEnabled")) = True Then
                Dim myinfo As New IPinfo
                Try
                    myinfo.IPAddress = CType(nic("IPAddress"), String())(0)
                    myinfo.SubnetMask = CType(nic("IPSubnet"), String())(0)
                    myIpInfo.Add(myinfo)
                Catch ex As Exception
                    MsgBox(ex.Message)
                End Try
            End If
        Next

        For Each myinfo As IPinfo In myIpInfo
            Dim baseIP As String = myinfo.IPAddress.Substring(0, InStrRev(myinfo.IPAddress, "."))
            For i As Integer = 0 To 255
                Dim t As New Threading.Thread(AddressOf pingAsync)
                t.Start(baseIP & i)
            Next
        Next
    End Sub

    ''' <summary>
    ''' Starts hosting...
    ''' </summary>
    Public Sub hostServer(ByVal username As String, ByVal ServerString As String)
        If myIpInfo.Count > 0 Then
            server = New TcpListener(IPAddress.Parse(myIpInfo(0).IPAddress), serverPort) '1244

            server.Start()
        End If
        _ServerString = ServerString

        serverThread = New Threading.Thread(AddressOf startHosting)
        serverThread.Start(username)

    End Sub

    ''' <summary>
    ''' Used by host server.
    ''' </summary>
    Private Sub startHosting(ByVal username As Object)
        Dim client As TcpClient

        _username = CType(username, String)

        'before anyone tries changing this, it MUST BE AN OBJECT ARRAY!!!!
        'it stores a TCPClient and a string, so explicit typing WON'T work. -Ray

        Do Until _close Or Disposing = True
            Try
                client = server.AcceptTcpClient()



                Dim myThread As New Threading.Thread(AddressOf clientWorker)
                myThread.Start(client)
            Catch
                _close = True
            End Try

        Loop
    End Sub

    ''' <summary>
    ''' Gets a count of how many clients are connected.
    ''' </summary>
    Public Function getCountOfClientsConnected() As Long
        Return clientList.Count
    End Function

    ''' <summary>
    ''' Sends a message to all clients connected to the server.
    ''' </summary>
    Public Sub ServerBroadcast(ByVal message As String)
        'Dim removelist As New List(Of NetworkStream)
        '

        For Each client As ClientInfo In clientList
            'If cStream IsNot Nothing Then
            If client.stream.CanWrite Then
                Try
                    Dim msgBytes() As Byte = System.Text.Encoding.ASCII.GetBytes(message)
                    sendData(msgBytes, client.stream)
                Catch ex As Exception
                    Application.DoEvents()
                    '  removelist.Add(cStream)
                End Try
                'Else
                '    removelist.Add(cStream)
            End If
            'Else
            '    removelist.Add(cStream)
            'End If
        Next

        'For Each rStream As NetworkStream In removelist
        '    streamList.Remove(rStream)
        'Next
    End Sub

    ''' <summary>
    ''' Sends a message to a SINGLE client connected to the server.
    ''' </summary>
    Public Sub ServerSendToClient(ByVal message As String, index As Long)
        'Dim removelist As New List(Of NetworkStream)
        '

        'For Each client As ClientInfo In clientList
        'If cStream IsNot Nothing Then
        If clientList(index).stream.CanWrite Then
            Try
                Dim msgBytes() As Byte = System.Text.Encoding.ASCII.GetBytes(message)
                sendData(msgBytes, clientList(index).stream)
            Catch ex As Exception
                Application.DoEvents()
                '  removelist.Add(cStream)
            End Try
            'Else
            '    removelist.Add(cStream)
        End If
        'Else
        '    removelist.Add(cStream)
        'End If
        'Next

        'For Each rStream As NetworkStream In removelist
        '    streamList.Remove(rStream)
        'Next
    End Sub


    ''' <summary>
    ''' Called when clients connect to the server. This is called in a multi-threaded mannor.
    ''' </summary>
    Private Sub clientWorker(ByVal param As Object)

        Dim client As TcpClient = CType(param, TcpClient)
        Dim stream As NetworkStream = client.GetStream




        Dim clientIP As System.Net.IPEndPoint = client.Client.RemoteEndPoint
        Dim data() As Byte = receiveData(stream)

        If System.Text.Encoding.ASCII.GetString(data).ToLower.Contains("stxsvr_v2_chk") Then
            ReDim data(-1)
            data = System.Text.Encoding.ASCII.GetBytes("stxsvr_v2_ack;" & _username)
            sendData(data, stream)
            data = receiveData(stream)
            Dim tmpstr As String = System.Text.Encoding.ASCII.GetString(data)

            If Not tmpstr = _ServerString And tmpstr.Contains("server: ") = False Then
                Dim myinfo As New ClientInfo
                myinfo.ip = client.Client.RemoteEndPoint
                myinfo.username = tmpstr


                Dim tempClient As New ClientInfo
                tempClient.stream = stream
                tempClient.ip = client.Client.RemoteEndPoint
                clientList.Add(tempClient)
                RaiseEvent ClientConnected(tempClient) 'was on top of the dimTempClient

                Dim t As New Threading.Thread(AddressOf streamWatcher)
                t.Start(client.GetStream)
                client = Nothing
            Else
                'send back the server string so they can make sure it's the right server
                data = System.Text.Encoding.ASCII.GetBytes(_ServerString)
                sendData(data, stream)
            End If
        End If
    End Sub

    'TODO: Add client verification that links to our function (that returns a boolean true if verified). Then adds the client's username to clientList

    ''' <summary>
    ''' Sends text on the stream to the server.
    ''' </summary>
    Public Sub sendText(ByVal message As String)

        If stream Is Nothing Then
            If connection IsNot Nothing Then
                If connection.Connected Then
                    sendData(System.Text.Encoding.ASCII.GetBytes(message), connection.GetStream)
                ElseIf server IsNot Nothing Then
                    RaiseEvent DataArrival(Me, message)
                End If
            ElseIf server IsNot Nothing Then
                RaiseEvent DataArrival(Me, message)
            End If
        Else
            sendData(System.Text.Encoding.ASCII.GetBytes(message), stream)
        End If
    End Sub



    Private Function receiveText(ByVal curstream As NetworkStream) As String
        Return System.Text.Encoding.ASCII.GetString(receiveData(curstream))
    End Function

    ''' <summary>
    ''' Sends binary data.
    ''' </summary>
    Public Sub sendData(ByVal data() As Byte, Optional ByVal curStream As NetworkStream = Nothing)
        Try
            Dim endbytes() As Byte = System.Text.Encoding.ASCII.GetBytes("{682427e7-6a56-4513-87f1-2dec81ddae2d}")

            Dim lenBytes() As Byte = System.Text.Encoding.ASCII.GetBytes(data.Length)

            curStream.Write(lenBytes, 0, lenBytes.Length)
            curStream.Write(endbytes, 0, endbytes.Length)

            curStream.Write(data, 0, data.Length)
            curStream.Flush()

        Catch

        End Try

        'If curStream IsNot Nothing Then
        '    curStream.Write(data, 0, data.Length)
        'End If

    End Sub


    Public Sub startStreamWatcher()
        Dim t As New Threading.Thread(AddressOf streamWatcher)
        t.Start(connection.GetStream)
    End Sub

    ''' <summary>
    ''' Receives binary data.
    ''' </summary>
    Public Function receiveData(ByVal curStream As NetworkStream) As Byte()
        Dim data(50000) As Byte
        Dim read As Integer = 0
        Dim chunkSize As Integer = 0

        Dim endbytes() As Byte = System.Text.Encoding.ASCII.GetBytes("{682427e7-6a56-4513-87f1-2dec81ddae2d}")

        Dim timeout As Integer = 0

        Do Until curStream.DataAvailable = True Or timeout >= 30000
            Threading.Thread.Sleep(1)
            timeout += 1
        Loop



        ReDim data(-1)
        Do Until JC.Data.endsWith(data, endbytes)
            ReDim Preserve data(data.Length)
            data(data.GetUpperBound(0)) = curStream.ReadByte
        Loop

        ReDim Preserve data(data.GetUpperBound(0) - endbytes.Length)

        chunkSize = System.Text.Encoding.ASCII.GetString(data)



        ReDim data(chunkSize - 1)
        Dim ms As New System.IO.MemoryStream

        Do Until ms.Length = chunkSize
            read = curStream.Read(data, 0, data.Length)
            ReDim Preserve data(read - 1)
            ms.Write(data, 0, data.Length)
            If ms.Length < chunkSize Then
                ReDim data((chunkSize - ms.Length) - 1)
            End If
        Loop


        'Try


        Return ms.ToArray()
        '    stream.Write(Encoding.ASCII.GetBytes("ok"), 0, 2)
        'Catch
        '    stream.Write(Encoding.ASCII.GetBytes("er"), 0, 2)
        '    Return receiveData(key, stream)

        'End Try









        'Dim data(5000) As Byte
        'Dim start As TimeSpan = Now.TimeOfDay

        'Try
        '    Do Until curStream.DataAvailable = True Or Now.TimeOfDay.Subtract(start) > New TimeSpan(0, 0, 15)
        '        Threading.Thread.Sleep(100)
        '    Loop




        '    If curStream.DataAvailable Then
        '        Dim r As Integer = curStream.Read(data, 0, data.Length)
        '        ReDim Preserve data(r - 1)
        '    Else
        '        ReDim data(-1)
        '    End If

        'Catch ex As Exception
        '    ReDim data(-1)
        'End Try

        'Return data
    End Function



    ''' <summary>
    ''' A client uses this to find servers. This is to find if a server is alive on this IP address, of the correct type. 1244 was the old port used here.
    ''' </summary>
    Private Sub pingAsync(ByVal objIP As Object)
        'TODO: Document better :)
        Dim ip As String = CStr(objIP)

        If My.Computer.Network.Ping(ip) Then
            Dim tmpConnection As New TcpClient
            Try
                tmpConnection.Connect(IPAddress.Parse(ip), clientPort) '1244

                If tmpConnection.Connected Then
                    Dim query() As Byte = System.Text.Encoding.ASCII.GetBytes("STXSVR_V2_CHK")
                    sendData(query, tmpConnection.GetStream)
                    Dim start As TimeSpan = Now.TimeOfDay

                    Do Until tmpConnection.GetStream.DataAvailable = True Or Now.TimeOfDay.Subtract(start) > New TimeSpan(0, 0, 15)
                        Threading.Thread.Sleep(100)
                    Loop

                    If tmpConnection.GetStream.DataAvailable Then
                        Dim data() As Byte = receiveData(tmpConnection.GetStream)

                        If System.Text.Encoding.ASCII.GetString(data).ToLower.Contains("stxsvr_v2_ack") Then
                            Dim split() As String = System.Text.Encoding.ASCII.GetString(data).Split(";")

                            query = System.Text.Encoding.ASCII.GetBytes("server: " & _ServerString)
                            sendData(query, tmpConnection.GetStream)

                            data = receiveData(tmpConnection.GetStream)
                            If System.Text.Encoding.ASCII.GetString(data) = _ServerString Then
                                SyncLock addresslist.GetType
                                    addresslist.Add(ip & ";" & split(1))
                                    RaiseEvent ServerFound(ip & ";" & split(1))

                                End SyncLock
                            End If


                        End If
                    End If

                    tmpConnection.Close()
                End If
            Catch ex As Exception
                Application.DoEvents()

            End Try
        End If

    End Sub



    'Public Shared Function createServerString(ByVal ActivationType As ServerCommands, ByVal foundout As String, ByVal Snumber As String) As String
    '    Dim tmpStr As String = ""

    '    Select Case ActivationType
    '        Case ServerCommands.NewActivation   'New Activation

    '            Debug.Print("Serial: " & Snumber)

    '            If Snumber <> "" Then
    '                tmpStr = JC.Activate.ReturnNewActString(Snumber, foundout, )
    '                Debug.Print("CLIENT Sendwhat: " & ReturnNewActString(Snumber))
    '            Else
    '                tmpStr = ReturnNewActString()
    '                Debug.Print("CLIENT Sendwhat: " & ReturnNewActString())
    '            End If

    '            Return tmpStr

    '        Case ServerCommands.ReActivate
    '            tmpStr = ReturnNewActString(JC.Global.KeyFile.Serial)
    '            Debug.Print("CLIENT Sendwhat: " & tmpStr)

    '        Case ServerCommands.RequestTrial   ' request trial
    '            tmpStr = TrialReq 'return
    '            Return tmpStr

    '        Case ServerCommands.CheckActivation
    '            '  SendWhat = tempinfo.encryptcomm("ChkAct" & "^" & tempinfo.osserial & "^" & tempinfo.SerialFromRegistry & "^" & tempinfo.ActCodeFromRegistry) ((old))
    '            If JC.Global.KeyFile.istrial = True Then
    '                tmpStr = "ChkAct^" & JC.Global.ActivationInfo.UID & "^" & JC.Global.KeyFile.Serial & "^" & JC.Global.KeyFile.ActivationCode & "^" & 3 & "^" & JC.Global.KeyFile.trialStartdate
    '            Else
    '                tmpStr = "ChkAct^" & JC.Global.ActivationInfo.UID & "^" & JC.Global.KeyFile.Serial & "^" & JC.Global.KeyFile.ActivationCode & "^" & 1
    '            End If

    '            Return tmpStr


    '        Case ServerCommands.CheckKey
    '            tmpStr = "ChkKey^" & Snumber
    '            Return tmpStr

    '        Case Else
    '            JC.Global.ErrLog("Error! in sendwhat") 'shldnt happen


    '    End Select

    '    Return tmpStr
    'End Function

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                Me.Disposing = True
                If server IsNot Nothing Then
                    server.Stop()
                End If


                ' TODO: dispose managed state (managed objects).
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    'TODO: Make an alternative for this<<<<<<<<<<<<<<<<<<
    '' This code added by Visual Basic to correctly implement the disposable pattern.
    'Public Sub Dispose() Implements IDisposable.Dispose
    '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    '    Dispose(True)
    '    GC.SuppressFinalize(Me)
    'End Sub
#End Region

End Class

