'  
'   Rebex Sample Code License
' 
'   Copyright 2026, REBEX CR s.r.o.
'   All rights reserved.
'   https://www.rebex.net/
' 
'   Permission to use, copy, modify, and/or distribute this software for any
'   purpose with or without fee is hereby granted.
' 
'   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
'   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
'   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
'   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
'   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
'   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
'   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
'   OTHER DEALINGS IN THE SOFTWARE.
' 

Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
Imports System.Threading
Imports System.IO
Imports Rebex.Net

Public Class MainForm
    Inherits System.Windows.Forms.Form
    Private lblRemote As System.Windows.Forms.Label
    Private lblLocal As System.Windows.Forms.Label
    Private txtRemote As System.Windows.Forms.TextBox
    Private WithEvents cmdDownload As System.Windows.Forms.Button
    Private WithEvents cmdUpload As System.Windows.Forms.Button
    Private pbProgress As System.Windows.Forms.ProgressBar
    Private statusStrip As System.Windows.Forms.StatusStrip
    Private statusLabel As System.Windows.Forms.ToolStripStatusLabel
    Private WithEvents cmdAbort As System.Windows.Forms.Button
    Private txtLocal As System.Windows.Forms.TextBox
    Private txtPort As System.Windows.Forms.TextBox
    Private txtHostname As System.Windows.Forms.TextBox
    Private txtUsername As System.Windows.Forms.TextBox
    Private txtPassword As System.Windows.Forms.TextBox
    Private lblHostname As System.Windows.Forms.Label
    Private lblUsername As System.Windows.Forms.Label
    Private lblPort As System.Windows.Forms.Label
    Private lblPassword As System.Windows.Forms.Label

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    ' Instance of Rebex.Net.Ftp
    Private _ftp As Ftp

    ' Background thread
    Private _backgroundThread As Thread

    ' Remote offset to start at
    Private _remoteOffset As Long

    'Connection info
    Dim localPath As String
    Dim remotePath As String
    Dim hostname As String
    Dim username As String
    Dim password As String
    Dim port As Integer
    Dim secure As Boolean
    Dim acceptAll As Boolean

    ''<summary>
    ''Wrapper for Invoke method that doesn't throw an exception after the object has been
    ''disposed while the calling method was running in a background thread.
    ''</summary>
    ''<param name="method"></param>
    ''<param name="args"></param>
    Private Sub SafeInvoke(ByVal method As [Delegate], ByVal args() As Object)
        Try
            If Not IsDisposed Then Invoke(method, args)
        Catch x As ObjectDisposedException
        End Try
    End Sub

    ' This is called at the change of FTP object state
    Private Sub StateChanged(ByVal sender As Object, ByVal e As FtpStateChangedEventArgs)
        statusLabel.Text = e.NewState.ToString()
    End Sub 'StateChanged

    ' This is called on transfer progress
    Private Sub TransferProgress(ByVal sender As Object, ByVal e As FtpTransferProgressChangedEventArgs)
        pbProgress.Value = Convert.ToInt32(e.CurrentFileProgressPercentage)
        statusLabel.Text = "Transferred " & e.CurrentFileBytesTransferred & " bytes"
    End Sub    'TransferProgress

    Private Sub StateChangedProxy(ByVal sender As Object, ByVal e As FtpStateChangedEventArgs)
        SafeInvoke(New EventHandler(Of FtpStateChangedEventArgs)(AddressOf Me.StateChanged), New Object() {sender, e})
    End Sub

    Public Sub TransferProgressProxy(ByVal sender As Object, ByVal e As FtpTransferProgressChangedEventArgs)
        SafeInvoke(New EventHandler(Of FtpTransferProgressChangedEventArgs)(AddressOf Me.TransferProgress), New Object() {sender, e})
    End Sub



    Public Sub New()
        MyBase.New()

        CheckForIllegalCrossThreadCalls = True
        Application.EnableVisualStyles()

        InitializeComponent()
    End Sub    'New


    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

#Region "Windows Form Designer generated code"

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents cbSecure As System.Windows.Forms.CheckBox
    Friend WithEvents cbAcceptAll As System.Windows.Forms.CheckBox
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.txtLocal = New System.Windows.Forms.TextBox()
        Me.lblRemote = New System.Windows.Forms.Label()
        Me.lblLocal = New System.Windows.Forms.Label()
        Me.txtRemote = New System.Windows.Forms.TextBox()
        Me.cmdDownload = New System.Windows.Forms.Button()
        Me.cmdUpload = New System.Windows.Forms.Button()
        Me.pbProgress = New System.Windows.Forms.ProgressBar()
        Me.statusStrip = New System.Windows.Forms.StatusStrip()
        Me.statusLabel = New System.Windows.Forms.ToolStripStatusLabel()
        Me.cmdAbort = New System.Windows.Forms.Button()
        Me.txtPort = New System.Windows.Forms.TextBox()
        Me.txtHostname = New System.Windows.Forms.TextBox()
        Me.txtUsername = New System.Windows.Forms.TextBox()
        Me.txtPassword = New System.Windows.Forms.TextBox()
        Me.lblHostname = New System.Windows.Forms.Label()
        Me.lblUsername = New System.Windows.Forms.Label()
        Me.lblPort = New System.Windows.Forms.Label()
        Me.lblPassword = New System.Windows.Forms.Label()
        Me.cbSecure = New System.Windows.Forms.CheckBox()
        Me.cbAcceptAll = New System.Windows.Forms.CheckBox()
        Me.statusStrip.SuspendLayout()
        Me.SuspendLayout()
        '
        'txtLocal
        '
        Me.txtLocal.Location = New System.Drawing.Point(80, 56)
        Me.txtLocal.Name = "txtLocal"
        Me.txtLocal.Size = New System.Drawing.Size(248, 20)
        Me.txtLocal.TabIndex = 0
        Me.txtLocal.Text = "c:\temp\readme.txt"
        '
        'lblRemote
        '
        Me.lblRemote.Location = New System.Drawing.Point(8, 80)
        Me.lblRemote.Name = "lblRemote"
        Me.lblRemote.Size = New System.Drawing.Size(72, 24)
        Me.lblRemote.TabIndex = 1
        Me.lblRemote.Text = "Remote file:"
        Me.lblRemote.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblLocal
        '
        Me.lblLocal.Location = New System.Drawing.Point(8, 56)
        Me.lblLocal.Name = "lblLocal"
        Me.lblLocal.Size = New System.Drawing.Size(72, 24)
        Me.lblLocal.TabIndex = 2
        Me.lblLocal.Text = "Local file:"
        Me.lblLocal.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'txtRemote
        '
        Me.txtRemote.Location = New System.Drawing.Point(80, 80)
        Me.txtRemote.Name = "txtRemote"
        Me.txtRemote.Size = New System.Drawing.Size(248, 20)
        Me.txtRemote.TabIndex = 1
        Me.txtRemote.Text = "/pub/example/readme.txt"
        '
        'cmdDownload
        '
        Me.cmdDownload.Location = New System.Drawing.Point(192, 144)
        Me.cmdDownload.Name = "cmdDownload"
        Me.cmdDownload.Size = New System.Drawing.Size(64, 20)
        Me.cmdDownload.TabIndex = 9
        Me.cmdDownload.Text = "Download"
        '
        'cmdUpload
        '
        Me.cmdUpload.Location = New System.Drawing.Point(120, 144)
        Me.cmdUpload.Name = "cmdUpload"
        Me.cmdUpload.Size = New System.Drawing.Size(64, 20)
        Me.cmdUpload.TabIndex = 8
        Me.cmdUpload.Text = "Upload"
        '
        'pbProgress
        '
        Me.pbProgress.Location = New System.Drawing.Point(8, 144)
        Me.pbProgress.Name = "pbProgress"
        Me.pbProgress.Size = New System.Drawing.Size(104, 20)
        Me.pbProgress.TabIndex = 8
        '
        'statusStrip
        '
        Me.statusStrip.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.statusLabel})
        Me.statusStrip.Location = New System.Drawing.Point(0, 166)
        Me.statusStrip.Name = "statusStrip"
        Me.statusStrip.Size = New System.Drawing.Size(334, 22)
        Me.statusStrip.TabIndex = 9
        '
        'statusLabel
        '
        Me.statusLabel.Name = "statusLabel"
        Me.statusLabel.Size = New System.Drawing.Size(90, 21)
        Me.statusLabel.Text = "Ready"
        '
        'cmdAbort
        '
        Me.cmdAbort.Enabled = False
        Me.cmdAbort.Location = New System.Drawing.Point(264, 144)
        Me.cmdAbort.Name = "cmdAbort"
        Me.cmdAbort.Size = New System.Drawing.Size(64, 20)
        Me.cmdAbort.TabIndex = 10
        Me.cmdAbort.Text = "Abort"
        '
        'txtPort
        '
        Me.txtPort.Location = New System.Drawing.Point(272, 8)
        Me.txtPort.Name = "txtPort"
        Me.txtPort.Size = New System.Drawing.Size(56, 20)
        Me.txtPort.TabIndex = 3
        Me.txtPort.Text = "21"
        '
        'txtHostname
        '
        Me.txtHostname.Location = New System.Drawing.Point(80, 8)
        Me.txtHostname.Name = "txtHostname"
        Me.txtHostname.Size = New System.Drawing.Size(152, 20)
        Me.txtHostname.TabIndex = 2
        Me.txtHostname.Text = "test.rebex.net"
        '
        'txtUsername
        '
        Me.txtUsername.Location = New System.Drawing.Point(80, 32)
        Me.txtUsername.Name = "txtUsername"
        Me.txtUsername.Size = New System.Drawing.Size(88, 20)
        Me.txtUsername.TabIndex = 6
        Me.txtUsername.Text = "demo"
        '
        'txtPassword
        '
        Me.txtPassword.Location = New System.Drawing.Point(240, 32)
        Me.txtPassword.Name = "txtPassword"
        Me.txtPassword.PasswordChar = Global.Microsoft.VisualBasic.ChrW(42)
        Me.txtPassword.Size = New System.Drawing.Size(88, 20)
        Me.txtPassword.TabIndex = 7
        Me.txtPassword.Text = "password"
        '
        'lblHostname
        '
        Me.lblHostname.Location = New System.Drawing.Point(8, 8)
        Me.lblHostname.Name = "lblHostname"
        Me.lblHostname.Size = New System.Drawing.Size(72, 24)
        Me.lblHostname.TabIndex = 15
        Me.lblHostname.Text = "Hostname:"
        Me.lblHostname.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblUsername
        '
        Me.lblUsername.Location = New System.Drawing.Point(8, 32)
        Me.lblUsername.Name = "lblUsername"
        Me.lblUsername.Size = New System.Drawing.Size(72, 24)
        Me.lblUsername.TabIndex = 16
        Me.lblUsername.Text = "Username:"
        Me.lblUsername.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblPort
        '
        Me.lblPort.Location = New System.Drawing.Point(240, 8)
        Me.lblPort.Name = "lblPort"
        Me.lblPort.Size = New System.Drawing.Size(32, 24)
        Me.lblPort.TabIndex = 17
        Me.lblPort.Text = "Port:"
        Me.lblPort.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'lblPassword
        '
        Me.lblPassword.Location = New System.Drawing.Point(176, 32)
        Me.lblPassword.Name = "lblPassword"
        Me.lblPassword.Size = New System.Drawing.Size(64, 24)
        Me.lblPassword.TabIndex = 18
        Me.lblPassword.Text = "Password:"
        Me.lblPassword.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'cbSecure
        '
        Me.cbSecure.Location = New System.Drawing.Point(8, 112)
        Me.cbSecure.Name = "cbSecure"
        Me.cbSecure.Size = New System.Drawing.Size(88, 24)
        Me.cbSecure.TabIndex = 19
        Me.cbSecure.Text = "TLS/SSL"
        '
        'cbAcceptAll
        '
        Me.cbAcceptAll.Location = New System.Drawing.Point(80, 108)
        Me.cbAcceptAll.Name = "cbAcceptAll"
        Me.cbAcceptAll.Size = New System.Drawing.Size(248, 32)
        Me.cbAcceptAll.TabIndex = 20
        Me.cbAcceptAll.Text = "Do not validate any server certificates - this is insecure but useful for testing."
        '
        'MainForm
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(334, 188)
        Me.Controls.Add(Me.cbAcceptAll)
        Me.Controls.Add(Me.cbSecure)
        Me.Controls.Add(Me.lblPassword)
        Me.Controls.Add(Me.lblPort)
        Me.Controls.Add(Me.lblUsername)
        Me.Controls.Add(Me.lblHostname)
        Me.Controls.Add(Me.txtPassword)
        Me.Controls.Add(Me.txtUsername)
        Me.Controls.Add(Me.txtHostname)
        Me.Controls.Add(Me.txtPort)
        Me.Controls.Add(Me.cmdAbort)
        Me.Controls.Add(Me.statusStrip)
        Me.Controls.Add(Me.pbProgress)
        Me.Controls.Add(Me.cmdUpload)
        Me.Controls.Add(Me.cmdDownload)
        Me.Controls.Add(Me.txtRemote)
        Me.Controls.Add(Me.lblLocal)
        Me.Controls.Add(Me.lblRemote)
        Me.Controls.Add(Me.txtLocal)
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D
        Me.MaximizeBox = False
        Me.MinimizeBox = False
        Me.Name = "MainForm"
        Me.Text = "Resumable FTP Transfer"
        Me.statusStrip.ResumeLayout(False)
        Me.statusStrip.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub 'InitializeComponent 
#End Region


    <STAThread()> _
    Shared Sub Main()
            ' set the trial license key
            Rebex.Licensing.Key = Rebex.Samples.TrialKey.Key


        Application.Run(New MainForm)
    End Sub    'Main

    ' Download operation. This is run in a background thread.
    Private Sub BackgroundDownload()
        Dim local As Stream = Nothing
        Try
            If File.Exists(localPath) Then
                ' If the local file exists, ask whether to resume transfer.
                Dim res As DialogResult = ShowMessage("Local file already exists. Do you want to resume the transfer? (If you answer 'no', the file will be overwritten.)", "Question", MessageBoxButtons.YesNoCancel)
                Select Case res
                    Case System.Windows.Forms.DialogResult.Yes
                        ' Open for writing and seek to the end if we are resuming
                        local = File.OpenWrite(localPath)
                        local.Seek(0, SeekOrigin.End)
                    Case System.Windows.Forms.DialogResult.No
                        local = File.Create(localPath)
                    Case Else
                        Return
                End Select
            Else
                local = File.Create(localPath)
            End If

            Dim secureMode As SslMode = SslMode.None
            ' If TLS/SSL was checked, try to secure the connection.
            If secure Then
                _ftp.Settings.SslAcceptAllCertificates = acceptAll
                secureMode = SslMode.Explicit
            End If

            ' Connect to the server.
            _ftp.Connect(hostname, port, secureMode)

            ' Log in.
            _ftp.Login(username, password)

            ' Get the length of the remote file. This is needed to be able to resume the transfer.
            Try
                _remoteOffset = _ftp.GetFileLength(remotePath)
            Catch x As FtpException
                If x.Response.Code <> 550 Then
                    Throw
                End If
                _remoteOffset = -1
            End Try

            If _remoteOffset < 0 Then
                ShowMessage("Unable to download, remote file does not exist.", "Error")
                Return
            End If

            If local.Length > _remoteOffset Then
                ShowMessage("Unable to resume transfer, local file is longer than remote file.", "Error")
                Return
            End If

            ' Do the transfer if there data is available to download.
            If local.Length < _remoteOffset Then
                SetAbortState(True)
                _remoteOffset = local.Position
                _ftp.GetFile(remotePath, local, _remoteOffset)
            End If

            ShowMessage("Download finished.", "Info")
        Catch ex As FtpException
            If ex.Status = FtpExceptionStatus.OperationAborted Then
                ShowMessage("Transfer aborted.", "Info")
            Else
                ShowMessage(ex.ToString(), "Error")
            End If
        Catch x As Exception
            ShowMessage(x.ToString(), "Error")
        Finally
            If Not (local Is Nothing) Then
                local.Close()
            End If

            _ftp.Disconnect()
            _backgroundThread = Nothing
            SafeInvoke(New OperationDoneDelegate(AddressOf OperationDone), New Object() {})
        End Try
    End Sub    'BackgroundDownload



    ' Download operation. Similar to upload operation.
    Private Sub BackgroundUpload()
        Dim local As Stream = Nothing
        Try
            local = File.OpenRead(localPath)

            Dim secureMode As SslMode = SslMode.None
            ' If TLS/SSL was checked, try to secure the connection.
            If secure Then
                _ftp.Settings.SslAcceptAllCertificates = acceptAll
                secureMode = SslMode.Explicit
            End If

            ' Connect to the server.
            _ftp.Connect(hostname, port, secureMode)

            ' Log in.
            _ftp.Login(username, password)

            ' Get the length of the remote file. This is needed to be able to resume the transfer.
            Try
                _remoteOffset = _ftp.GetFileLength(remotePath)
            Catch x As FtpException
                If x.Response.Code <> 550 Then
                    Throw
                End If
                _remoteOffset = -1
            End Try

            If _remoteOffset >= 0 Then
                ' If the remote file exists, ask whether to resume transfer.
                Dim res As DialogResult = ShowMessage("Remote file already exists. Do you want to resume the transfer? (If you answer 'no', the file will be overwritten.)", "Question", MessageBoxButtons.YesNoCancel)
                Select Case res
                    Case System.Windows.Forms.DialogResult.Yes
                    Case System.Windows.Forms.DialogResult.No
                        _remoteOffset = 0
                    Case Else
                        Return
                End Select
            Else
                _remoteOffset = 0
            End If

            If _remoteOffset > local.Length Then
                ShowMessage("Unable to resume transfer, remote file is longer than local file.", "Error")
                Return
            End If

            ' Do the transfer if there data is available to upload.
            If _remoteOffset < local.Length Then
                SetAbortState(True)
                local.Seek(_remoteOffset, SeekOrigin.Begin)
                _ftp.PutFile(local, remotePath, _remoteOffset, -1)
            End If

            ShowMessage("Upload finished.", "Info")
        Catch ex As FtpException
            If ex.Status = FtpExceptionStatus.OperationAborted Then
                ShowMessage("Transfer aborted.", "Info")
            Else
                ShowMessage(ex.ToString(), "Error")
            End If
        Catch x As Exception
            ShowMessage(x.ToString(), "Error")
        Finally
            If Not (local Is Nothing) Then
                local.Close()
            End If

            _ftp.Disconnect()
            SafeInvoke(New OperationDoneDelegate(AddressOf OperationDone), New Object() {})
            _backgroundThread = Nothing
        End Try
    End Sub    'BackgroundUpload

    Public Delegate Sub OperationDoneDelegate()

    Private Sub OperationDone()
        ' Reset the progress bar
        pbProgress.Value = 0

        ' Enable editable boxes and buttons
        txtHostname.Enabled = True
        txtPort.Enabled = True
        txtUsername.Enabled = True
        txtPassword.Enabled = True
        txtRemote.Enabled = True
        txtLocal.Enabled = True
        cmdDownload.Enabled = True
        cmdUpload.Enabled = True
        cbSecure.Enabled = True
        cbAcceptAll.Enabled = True

        SetAbortState(False)
    End Sub


    ' Starts the background transfer operation.
    Private Sub StartBackgroundThread(ByVal threadStart As ThreadStart)
        If (Not (_backgroundThread) Is Nothing) Then
            ShowMessage("Another background transfer is active.", "Error")
            Return
        End If

        ' Initialize an instance of FTP class and set up events
        _ftp = New Ftp
        AddHandler _ftp.StateChanged, AddressOf Me.StateChangedProxy
        AddHandler _ftp.TransferProgressChanged, AddressOf Me.TransferProgressProxy

        ' Initialize connection info
        hostname = txtHostname.Text
        port = Convert.ToInt32(txtPort.Text)
        password = txtPassword.Text
        username = txtUsername.Text
        remotePath = txtRemote.Text
        localPath = txtLocal.Text
        secure = cbSecure.Checked
        acceptAll = cbAcceptAll.Checked

        ' Disable editable boxes and buttons
        txtHostname.Enabled = False
        txtPort.Enabled = False
        txtPassword.Enabled = False
        txtUsername.Enabled = False
        txtRemote.Enabled = False
        txtLocal.Enabled = False
        cmdDownload.Enabled = False
        cmdUpload.Enabled = False
        cbSecure.Enabled = False
        cbAcceptAll.Enabled = False

        ' Start background thread
        _backgroundThread = New Thread(threadStart)
        _backgroundThread.Start()

    End Sub    'StartBackgroundThread


    ' Abort the background thread on exit if it is active.
    Protected Overrides Sub OnFormClosed(ByVal e As FormClosedEventArgs)
        MyBase.OnFormClosed(e)

        Dim thread As Thread = _backgroundThread
        If Not (thread Is Nothing) Then
            thread.Abort()
        End If
    End Sub    'OnFormClosed

    Private Sub cmdDownload_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdDownload.Click
        StartBackgroundThread(New ThreadStart(AddressOf BackgroundDownload))
    End Sub    'cmdDownload_Click


    Private Sub cmdUpload_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdUpload.Click
        StartBackgroundThread(New ThreadStart(AddressOf BackgroundUpload))
    End Sub    'cmdUpload_Click


    ' Abort transfer if background operation is active.
    Private Sub cmdAbort_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdAbort.Click
        If _backgroundThread Is Nothing Then
            ShowMessage("No background transfer is active.", "Error")
            Return
        End If

        statusLabel.Text = "Aborting"

        _ftp.Abort()
        _backgroundThread = Nothing
    End Sub    'cmdAbort_Click

    Private Sub SetAbortState(ByVal enabled As Boolean)
        If Me.InvokeRequired Then
            Me.Invoke(New Action(Of Boolean)(AddressOf SetAbortState), enabled)
            Exit Sub
        End If

        cmdAbort.Enabled = enabled
    End Sub

    Private Function ShowMessage(text As String, title As String) As DialogResult
        Return ShowMessage(text, title, MessageBoxButtons.OK)
    End Function


    Private Function ShowMessage(text As String, title As String, buttons As MessageBoxButtons) As DialogResult
        ' on Mono, MessageBox.Show has to be called from the application's main thread
        If Me.InvokeRequired Then
            Return DirectCast(Me.Invoke(DirectCast(AddressOf ShowMessage, ShowMessageDelegate), text, title, buttons), DialogResult)
        End If

        Return MessageBox.Show(text, title, buttons)
    End Function


    Private Delegate Function ShowMessageDelegate(text As String, title As String, buttons As MessageBoxButtons) As DialogResult
End Class 'MainForm