McBasic™Get McBasic™ ↗
Build 881
Hands-on tutorial · Data & services

Download with progress and cancellation

Keep the interface responsive while a file downloads safely.

1 Form / control designs1 complete BASIC filesGuided sample project

StartDownload returns an owned operation. WaitAsync produces the byte count, while a Timer samples progress. TotalBytes can be unknown, so use an indeterminate progress bar in that case. QueryUnload cancels and delays closure; Finally restores controls and stops the timer on every exit path.

Step 1Prepare a safe practice project#

Extract the archive into a new writable folder, separate from your own application. Open HttpDownloadDemo.mcproj in McBasic™. Keep every declared source, layout, resource and migration file together.

To reconstruct it yourself, create a DesktopApp project and add the files declared below. In the complete project, follow these steps as a guided rebuild: replace each named source file once and Check after all its procedures are present.

Exact project configuration
HttpDownloadDemo.mcproj
{
  "schemaVersion": 1,
  "projectId": "b728c63f-2ee1-4ab6-a2ec-deba701fa709",
  "name": "HttpDownloadDemo",
  "kind": "DesktopApp",
  "language": {
    "dialect": "McBasic1",
    "requireExplicit": true,
    "compare": "Binary",
    "defaultArrayBase": 0
  },
  "application": {
    "bundleIdentifier": "com.mcbasic.samples.httpdownloaddemo",
    "version": "0.1.0",
    "buildNumber": "1",
    "startupFormId": "form-main",
    "shutdownMode": "OnStartupFormClosed"
  },
  "forms": [
    {
      "id": "form-main",
      "typeName": "frmMain",
      "layout": "Forms/frmMain.form.json",
      "code": "Forms/frmMain.bas"
    }
  ],
  "modules": [],
  "classes": [],
  "tests": [],
  "references": [],
  "resources": [],
  "targets": [
    "osx-arm64"
  ],
  "capabilities": []
}

Step 2Build the Form and set its properties#

frmMain

Select the Form first and set its properties. Add containers before their children. These are initial Designer values, not statements to paste into a procedure.

Form propertyValue
Caption"HTTP Download — McBasic"
Width610
Height310
MinWidth610
MinHeight310
TypeNameParent
TextBoxtxtUrlForm
TextBoxtxtDestinationForm
CommandButtoncmdDownloadForm
CommandButtoncmdCancelForm
ProgressBarbarDownloadForm
LabellblProgressForm
TimertmrProgressComponent tray
txtUrl · exact Inspector values
PropertyValue
Left24
Top24
Width550
Height34
Text"http://127.0.0.1:8080/binary"
TabIndex0
Anchor edgesLeft, Top
txtDestination · exact Inspector values
PropertyValue
Left24
Top70
Width550
Height34
Text"download.bin"
TabIndex1
Anchor edgesLeft, Top
cmdDownload · exact Inspector values
PropertyValue
Left24
Top120
Width150
Height34
Caption"Download"
TabIndex2
Anchor edgesLeft, Top
cmdCancel · exact Inspector values
PropertyValue
Left194
Top120
Width150
Height34
Caption"Cancel"
EnabledFalse
TabIndex3
Anchor edgesLeft, Top
barDownload · exact Inspector values
PropertyValue
Left24
Top178
Width550
Height24
Minimum0
Maximum100
Value0
IndeterminateFalse
Anchor edgesLeft, Top
lblProgress · exact Inspector values
PropertyValue
Left24
Top218
Width550
Height38
Caption"Ready. Existing destination is replaced only after success."
Anchor edgesLeft, Top
tmrProgress · exact Inspector values
PropertyValue
EnabledFalse
Interval100

Step 3Connect the events explicitly#

frmMain

ObjectEventProcedureParameters
cmdDownloadClickcmdDownload_ClickClick()
cmdCancelClickcmdCancel_ClickClick()
tmrProgressTimertmrProgress_TimerTimer()
FormQueryUnloadForm_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)

Step 4Enter the complete behavior#

Open the named source file. Enter declarations first, then every procedure. Expand a procedure for its full body, or copy the complete file. Helpers are required: do not stop after entering the first Click handler.

Forms/frmMain.bas

Enter these declarations at file scope, before the procedures.

Forms/frmMain.bas · declarations
Option Explicit

Private active As HttpDownload
Private busy As Boolean
Private closeRequested As Boolean
Private Async Sub cmdDownload_Click()

Follow the success, failure and cleanup paths together. Do not omit a guard or Finally block when entering this procedure.

Forms/frmMain.bas · cmdDownload_Click
Private Async Sub cmdDownload_Click()
    Dim saved As LongLong
    If busy Then Exit Sub
    busy = True
    cmdDownload.Enabled = False
    cmdCancel.Enabled = True
    barDownload.Indeterminate = False
    barDownload.Value = 0
    lblProgress.Caption = "Starting..."
    Try
        Using cancellation As CancellationSource = Http.CreateCancellationSource()
            Set active = Http.StartDownload(txtUrl.Text, txtDestination.Text, 10000, cancellation, True)
            tmrProgress.Enabled = True
            Using owned As HttpDownload = active
                saved = Await owned.WaitAsync()
            End Using
        End Using
        barDownload.Indeterminate = False
        barDownload.Value = 100
        lblProgress.Caption = "Complete: " & CStr(saved) & " bytes"
    Catch error As McException
        If error.Number = 18 Then
            lblProgress.Caption = "Cancelled; prior file preserved."
        Else
            lblProgress.Caption = "Error " & CStr(error.Number) & "; prior file preserved."
        End If
    Finally
        tmrProgress.Enabled = False
        barDownload.Indeterminate = False
        busy = False
        cmdDownload.Enabled = True
        cmdCancel.Enabled = False
        If closeRequested Then Unload Me
    End Try
End Sub
Private Sub cmdCancel_Click()

Enter the complete action, including its input checks and calls to helper procedures.

Forms/frmMain.bas · cmdCancel_Click
Private Sub cmdCancel_Click()
    If busy Then active.Cancel()
End Sub
Private Sub tmrProgress_Timer()

Enter this helper in the named source file, outside every other procedure. Its callers are included in the same project.

Forms/frmMain.bas · tmrProgress_Timer
Private Sub tmrProgress_Timer()
    If Not busy Then Exit Sub
    lblProgress.Caption = CStr(active.BytesReceived) & "/" & CStr(active.TotalBytes) & " bytes"
    If active.TotalBytes > 0 Then
        barDownload.Indeterminate = False
        barDownload.Value = CLng(CDbl(active.BytesReceived) / CDbl(active.TotalBytes) * 100)
    Else
        barDownload.Indeterminate = True
    End If
End Sub
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)

This is the lifetime boundary. Preserve the cancellation signature where present and release resources owned by this Form.

Forms/frmMain.bas · Form_QueryUnload
Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    If busy Then
        Cancel = 1
        closeRequested = True
        tmrProgress.Enabled = False
        lblProgress.Caption = "Cancelling before close..."
        active.Cancel()
    End If
End Sub
Copy the complete Forms/frmMain.bas file

Replace the file once; do not append duplicate procedures.

Forms/frmMain.bas
Option Explicit

Private active As HttpDownload
Private busy As Boolean
Private closeRequested As Boolean

Private Async Sub cmdDownload_Click()
    Dim saved As LongLong
    If busy Then Exit Sub
    busy = True
    cmdDownload.Enabled = False
    cmdCancel.Enabled = True
    barDownload.Indeterminate = False
    barDownload.Value = 0
    lblProgress.Caption = "Starting..."
    Try
        Using cancellation As CancellationSource = Http.CreateCancellationSource()
            Set active = Http.StartDownload(txtUrl.Text, txtDestination.Text, 10000, cancellation, True)
            tmrProgress.Enabled = True
            Using owned As HttpDownload = active
                saved = Await owned.WaitAsync()
            End Using
        End Using
        barDownload.Indeterminate = False
        barDownload.Value = 100
        lblProgress.Caption = "Complete: " & CStr(saved) & " bytes"
    Catch error As McException
        If error.Number = 18 Then
            lblProgress.Caption = "Cancelled; prior file preserved."
        Else
            lblProgress.Caption = "Error " & CStr(error.Number) & "; prior file preserved."
        End If
    Finally
        tmrProgress.Enabled = False
        barDownload.Indeterminate = False
        busy = False
        cmdDownload.Enabled = True
        cmdCancel.Enabled = False
        If closeRequested Then Unload Me
    End Try
End Sub

Private Sub cmdCancel_Click()
    If busy Then active.Cancel()
End Sub

Private Sub tmrProgress_Timer()
    If Not busy Then Exit Sub
    lblProgress.Caption = CStr(active.BytesReceived) & "/" & CStr(active.TotalBytes) & " bytes"
    If active.TotalBytes > 0 Then
        barDownload.Indeterminate = False
        barDownload.Value = CLng(CDbl(active.BytesReceived) / CDbl(active.TotalBytes) * 100)
    Else
        barDownload.Indeterminate = True
    End If
End Sub

Private Sub Form_QueryUnload(ByRef Cancel As Integer, ByVal UnloadMode As Integer)
    If busy Then
        Cancel = 1
        closeRequested = True
        tmrProgress.Enabled = False
        lblProgress.Caption = "Cancelling before close..."
        active.Cancel()
    End If
End Sub

Step 5Keep resources and storage reproducible#

No external resource or migration files are declared. Any inline migration, seed data or local JSON is present in the code above. Runtime application data and private preferences are not included in the archive.

Step 6Check, run and test the result#

Save all files and run Check. Resolve diagnostics before Run. Developer CLI users can work from the extracted folder:

Developer CLI
mcbasic check HttpDownloadDemo.mcproj
mcbasic run HttpDownloadDemo.mcproj

Acceptance checklist

  • Choose an HTTPS file you control and an existing writable destination directory.
  • Start a download and confirm controls switch to the busy state.
  • Cancel and confirm the prior destination file is preserved.
  • Start again and confirm a successful byte count.
  • Close during download and confirm cancellation finishes before the Form closes.

Step 7Make one change and repeat the tests#

Use a deliberately slow test endpoint to verify unknown-length progress and cancellation.

Save a working Git checkpoint first. Change one behavior, Check again and repeat the acceptance checklist. Compare with the downloaded solution if a regression appears.

Source baseline 20f4bfed6b · Build manifest