Using Claude to convert old VB 6.0 code to B4J

Didier9

Well-Known Member
Licensed User
Longtime User
I bit the bullet. I have been using Claude Code to develop microcontroller firmware for about 2-3 months with great satisfaction.
Many moons ago I wrote a piece of code to download waveforms from a Tektronix oscilloscope to a PC under VB 6.0.
That worked well enough until VB 6.0 stopped working under Windows 10/11.
So yesterday after work I fed the old VB code into Claude and asked it to make it into a B4J application.
I did prune the less useful features. It was more of an exercise than something I really needed but in about 2 hours time, it was working.

TDSDump.png


I had to create the layout file, but Claude told me exactly the elements I needed, I just had to put them on the screen where I wanted them.
There were a handful of syntax errors, not unexpected considering the relatively small code base for B4J, but surprisingly few and I did not fix them, I just told Claude what was wrong.
The original application did not support the TDS3000 Series, so I had to find out the few differences.

Overall I am quite impressed.

The code and comments below are pretty much 100% Claude. I wrote very little code myself, I mostly told Claude what I needed.

B4X:
#Region  Project Attributes
 
    #MainFormWidth: 680
    #MainFormHeight: 780
 
#End Region

' ============================================================================
' TDS2xxDump - B4J conversion (bitmap screen-dump path only; waveform plotting
' dropped per requirements). Converted from the original VB6 project.
'
' ASSUMPTIONS / THINGS TO VERIFY (flagged, not silently guessed):
' 1. UI layout: this code assumes a Designer layout file "Main" containing the
'    nodes listed in the "REQUIRED DESIGNER LAYOUT" comment block below, with
'    those EXACT names. You'll need to build that layout yourself in the B4J
'    Designer (I can't reliably hand you a working .fxml sight-unseen).
' 2. Save/Open dialogs use B4J's built-in FileChooser type (confirmed against
'    the official jfx.html docs: Initialize/SetExtensionFilter/ShowOpen/ShowSave).
' 3. Baud rate / model selection: mapped VB6's mnTDS2xx/mnTDS2xxx menu choice
'    to two RadioButtons (rbTDS210, rbTDS2014) in the same ToggleGroup, per
'    your own jSerial example's UI pattern (label+listview) adapted to radio
'    buttons since there are only two choices here. A third, rbTDS3014, was
'    added later for the TDS3014 (not supported by the original VB6 code).
' 4. Expected BMP sizes: 38464 (TDS210/220) and 27250 (TDS2014) bytes are
'    carried over verbatim from your VB6 comments, unverified independently.
'    308278 bytes for the TDS3014 (color hardcopy) came from your own
'    measurement against real hardware.
' ============================================================================

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private MsgBox As Msgboxes
    Private bc As ByteConverter

    ' -- Serial port --
    Private sp As Serial
    Private AStream As AsyncStreams
    Private COMPortName As String
    Private cmdTimeoutTimer As Timer
    Private msRemaining As Int

    ' -- Comm state machine --
    Private Const MODE_IDLE As Int = 0
    Private Const MODE_COMMAND As Int = 1   ' waiting for an LF-terminated text reply
    Private Const MODE_BITMAP As Int = 2    ' waiting for raw binary BMP bytes
    Private commMode As Int = MODE_IDLE
    Private respText As String              ' accumulator for MODE_COMMAND
    Private bmpBytes As List                ' accumulator for MODE_BITMAP (List of Byte)
    Private pendingCmdLabel As String        ' which command we're waiting on, for the log
    Private expectedBMPSize As Int

    ' -- UI nodes (must exist in the "Main" Designer layout with these names) --
    Private lblPort As Label
    Private lvPorts As ListView
    Private rbTDS210 As RadioButton          ' 9600 baud, TDS210/220
    Private rbTDS2014 As RadioButton         ' 19200 baud, TDS2014
    Private rbTDS3014 As RadioButton         ' 19200 baud (assumed, see OpenComPort), color hardcopy
    Private btnID As Button
    Private btnClear As Button
    Private btnHardCopy As Button
    Private btnDeviceManager As Button
    Private pbProgress As ProgressBar
    Private lblStatus As Label               ' Label1 equivalent
    Private lblByteCount As Label             ' Label3/Label4 equivalent
    Private taLog As TextArea                ' Text1 equivalent
    Private ivBitmap As ImageView             ' Picture1 equivalent
    Private mnuBar As MenuBar
    Private fc As FileChooser

    Private savedBMPData() As Byte           ' last captured bitmap, for Save
    Private ScopeType As String = ""
End Sub

' ============================================================================
' REQUIRED DESIGNER LAYOUT ("Main")
' Add these nodes via the B4J Designer and set their Name property to match:
'   lblPort (Label), lvPorts (ListView, initially Visible=False)
'   rbTDS210, rbTDS2014, rbTDS3014 (RadioButton, all same ToggleGroup)
'   btnID  (Button)
'   btnHardCopy (Button), btnDeviceManager (Button)
'   pbProgress (ProgressBar)
'   lblStatus, lblByteCount (Label)
'   taLog (TextArea)
'   mnuBar (MenuBar) - leave it empty in the Designer; the File/Help menus and
'                      their items are built in code (see CreateMenu, called
'                      from AppStart)
' ============================================================================

Sub AppStart(Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Main")
    MainForm.Show
    MainForm.Title = "TDS Screen Dump"

    sp.Initialize("sp")
    cmdTimeoutTimer.Initialize("cmdTimeoutTimer", 100) ' 100ms tick, mirrors VB6 Timer1 (was 50ms)
    bmpBytes.Initialize

    'lvPorts.Visible = False
    rbTDS3014.Selected = True

    CreateMenu
    RefreshPortList
End Sub

' Builds the File/Help menus in code and attaches them to the mnuBar node
' that already exists in the "Main" Designer layout. EventName ("mnuOpen" etc)
' matches the existing mnuOpen_Action/mnuSave_Action/... subs below, so each
' item routes straight to its own handler with no Select Case needed.
Sub CreateMenu
    Dim mFile As Menu
    mFile.Initialize("File", "")

    Dim miOpen As MenuItem
    miOpen.Initialize("Open", "mnuOpen")
    mFile.MenuItems.Add(miOpen)

    Dim miSave As MenuItem
    miSave.Initialize("Save", "mnuSave")
    mFile.MenuItems.Add(miSave)

    Dim miExit As MenuItem
    miExit.Initialize("Exit", "mnuExit")
    mFile.MenuItems.Add(miExit)

    mnuBar.Menus.Add(mFile)

    Dim mHelp As Menu
    mHelp.Initialize("Help", "")

    Dim miAbout As MenuItem
    miAbout.Initialize("About", "mnuAbout")
    mHelp.MenuItems.Add(miAbout)

    mnuBar.Menus.Add(mHelp)
End Sub

Sub RefreshPortList
    lvPorts.Items.Clear
    For Each s As String In sp.ListPorts
        lvPorts.Items.Add(s)
    Next
End Sub

Sub lblPort_MouseClicked(EventData As MouseEvent)
    lvPorts.Visible = Not(lvPorts.Visible)
End Sub

Sub lvPorts_SelectedIndexChanged(Index As Int)
    If Index > -1 Then
        COMPortName = lvPorts.SelectedItem
        'lblPort.Text = COMPortName
        'lvPorts.Visible = False
    End If
End Sub

' -- Opens the port with the settings for the currently selected scope model.
' Returns True on success. Mirrors VB6's OpenComPort().
Sub OpenComPort As Boolean
    If COMPortName = "" Then
        MsgBox.Show("Select a COM port first", "TDS2xx")
        Return False
    End If
    Try
        sp.Close
    Catch
        Log( LastException )
    End Try
    Try
        sp.Open(COMPortName)
    Catch
        MsgBox.Show("Error opening port " & COMPortName, "TDS2xx")
        Return False
    End Try

    If rbTDS210.Selected Then
        sp.SetParams(9600, 8, 1, 0)  ' N,8,1
    Else If rbTDS2014.Selected Then
        sp.SetParams(19200, 8, 1, 0)
    Else If rbTDS3014.Selected Then
        sp.SetParams(38400, 8, 1, 0)
    End If
    AStream.Initialize(sp.GetInputStream, sp.GetOutputStream, "AStream")
    Return True
End Sub

Sub CloseComPort
    Try
        AStream.Close
    Catch
        Log( LastException )
    End Try
    Try
        sp.Close
    Catch
        Log( LastException )
    End Try
End Sub

' ============================================================================
' Diagnostic SCPI command buttons - all follow the same pattern as VB6's
' cmdCMD_Click / cmdESR_Click / etc: send text + LF, wait (with timeout) for
' an LF-terminated text reply, log it.
' ============================================================================

Sub SendTextCommand(label As String, cmd As String)
    Try
        AStream.Close
    Catch
        Log( LastException )
    End Try
    Try
        sp.Close
    Catch
        Log( LastException )
    End Try
    If Not(OpenComPort) Then Return
    commMode = MODE_COMMAND
    respText = ""
    pendingCmdLabel = label
    AStream.Write((cmd & Chr(10)).GetBytes("UTF8"))
    msRemaining = 750 ' matches VB6's 750ms timeout
    cmdTimeoutTimer.Enabled = True
End Sub

Sub btnID_Action
    SendTextCommand("*IDN?", "*IDN?")
End Sub

Sub btnClear_Action
    taLog.Text = ""
End Sub

Sub btnDeviceManager_Action
    ' Equivalent of VB6's ShellExecute call to launch Device Manager
    Dim proc As Shell
    proc.Initialize("proc", "cmd", Array As String("/c", "devmgmt.msc"))
    proc.Run(-1)
End Sub

' ============================================================================
' HardCopy / bitmap capture - mirrors VB6's BitMapTDS2xx() / BitMapTDS2xxx()
' ============================================================================

Sub btnHardCopy_Action
    Try
        AStream.Close
    Catch
        Log( LastException )
    End Try
    Try
        sp.Close
    Catch
        Log( LastException )
    End Try
    If Not(OpenComPort) Then Return
    bmpBytes.Initialize
    lblStatus.Text = "Setting up hardcopy"
    pbProgress.Progress = 0
    lblByteCount.Text = "0"
    btnHardCopy.Text = "Capturing..."
    btnHardCopy.Enabled = False

    Dim fmtCmd As String
    If rbTDS210.Selected Then
        expectedBMPSize = 38464
        fmtCmd = "HARDC:FORMAT BMP"
    Else If rbTDS2014.Selected Then
        expectedBMPSize = 27250
        fmtCmd = "HARDC:FORMAT BMP"
    Else ' rbTDS3014
        expectedBMPSize = 308278
        fmtCmd = "HARDC:FORMAT BMPColor"
    End If

    AStream.Write(("HARDC:PORT RS232" & Chr(10)).GetBytes("UTF8"))
    AStream.Write((fmtCmd & Chr(10)).GetBytes("UTF8"))
    AStream.Write(("HARDC:LAYOUT PORTRAIT" & Chr(10)).GetBytes("UTF8"))
    AStream.Write(("HARDC START" & Chr(10)).GetBytes("UTF8"))

    commMode = MODE_BITMAP
    lblStatus.Text = "Waiting for scope"
    msRemaining = 5000 ' VB6 used up to 5000ms initial timeout for the TDS2014 path
    cmdTimeoutTimer.Enabled = True
End Sub

Sub FinishBitmapCapture(success As Boolean)
    cmdTimeoutTimer.Enabled = False
    btnHardCopy.Text = "HardCopy"
    btnHardCopy.Enabled = True
    commMode = MODE_IDLE

    If Not(success) Or bmpBytes.Size = 0 Then
        lblStatus.Text = "Received nothing"
        MsgBox.Show("Received nothing", "TDS2xx")
        Return
    End If

    savedBMPData = ArrayFromList(bmpBytes)
    lblStatus.Text = "Done - " & bmpBytes.Size & " bytes"

    Try
        Dim jo As JavaObject
        jo.InitializeNewInstance("java.io.ByteArrayInputStream", Array(savedBMPData))
        Dim bmpStream As InputStream = jo
        Dim img As Image
        img.Initialize2(bmpStream)
        ivBitmap.SetImage(img)
        bmpStream.Close
    Catch
        MsgBox.Show("Received data does not appear to be a valid BMP", "TDS2xx")
    End Try
End Sub

' Helper: B4J's List doesn't give you a byte array directly - convert.
Sub ArrayFromList(lst As List) As Byte()
    Dim arr(lst.Size) As Byte
    For i = 0 To lst.Size - 1
        arr(i) = lst.Get(i)
    Next
    Return arr
End Sub

' ============================================================================
' AsyncStreams event handlers - this replaces VB6's polling
' (Do / DoEvents / InBufferCount) loops with an event-driven model.
' ============================================================================

Sub AStream_NewData(Buffer() As Byte)
    Select commMode
        Case MODE_COMMAND
            respText = respText & bc.StringFromBytes(Buffer, "UTF8")
            If respText.EndsWith(Chr(13)) Then
                cmdTimeoutTimer.Enabled = False
                commMode = MODE_IDLE
                AddLog(pendingCmdLabel & " > " & respText)
                respText = ""
            Else
                msRemaining = 750 ' data is still arriving in fragments - keep the watchdog alive
            End If

        Case MODE_BITMAP
            For i = 0 To Buffer.Length - 1
                bmpBytes.Add(Buffer(i))
            Next
            lblByteCount.Text = bmpBytes.Size & "/" & expectedBMPSize
            If expectedBMPSize > 0 Then
                pbProgress.Progress = bmpBytes.Size / expectedBMPSize
            End If
            If Left3(lblStatus.Text) <> "Rec" Then lblStatus.Text = "Receiving"
            msRemaining = 500 ' safety net only now - normal completion is by byte count below
            If bmpBytes.Size >= expectedBMPSize Then
                FinishBitmapCapture(True)
            End If
    End Select
End Sub

Sub Left3(s As String) As String
    If s.Length >= 3 Then
        Return s.SubString2(0, 3)
    Else
        Return s
    End If
End Sub

Sub AStream_Error
    Log("Serial error: " & LastException)
    cmdTimeoutTimer.Enabled = False
    If commMode = MODE_BITMAP Then
        FinishBitmapCapture(False)
    Else
        commMode = MODE_IDLE
    End If
End Sub

Sub AddLog(s As String)
    If taLog.Text.Length > 20000 Then
        taLog.Text = taLog.Text.SubString(taLog.Text.Length - 15000)
    End If
    If taLog.Text.Length > 0 Then
        taLog.Text = taLog.Text & CRLF & s
    Else
        taLog.Text = s
    End If
End Sub

' -- Timeout watchdog: replaces VB6's TimeOut countdown that ran off Timer1 --
Sub cmdTimeoutTimer_Tick
    msRemaining = msRemaining - 100
    If msRemaining <= 0 Then
        cmdTimeoutTimer.Enabled = False
        If commMode = MODE_BITMAP Then
            AddLog("Timed out waiting for bitmap data")
            FinishBitmapCapture(bmpBytes.Size > 0)
        Else If commMode = MODE_COMMAND Then
            AddLog(pendingCmdLabel & " > (no response / timeout)")
            commMode = MODE_IDLE
        End If
    End If
End Sub

' ============================================================================
' Menu handlers
' ============================================================================

Sub mnuOpen_Action
    fc.Initialize
    fc.Title = "Open BMP"
    fc.SetExtensionFilter("BMP Files", Array As String("*.bmp"))
    Dim path As String = fc.ShowOpen(MainForm)
    If path <> "" And File.Exists(File.GetFileParent(path), File.GetName(path)) Then
        Dim img As Image
        img.Initialize(File.GetFileParent(path), File.GetName(path))
        ivBitmap.SetImage(img)
    End If
End Sub

Sub mnuSave_Action
    If savedBMPData.Length = 0 Then
        MsgBox.Show("Nothing captured yet to save", "TDS2xx")
        Return
    End If
    fc.Initialize
    fc.Title = "Save BMP"
    fc.InitialFileName = ScopeType & ".bmp"
    fc.SetExtensionFilter("BMP Files", Array As String("*.bmp"))
    Dim path As String = fc.ShowSave(MainForm)
    If path <> "" Then
        If path.ToLowerCase.EndsWith(".bmp") = False Then path = path & ".bmp"
        File.WriteBytes(File.GetFileParent(path), File.GetName(path), savedBMPData)
    End If
End Sub

Sub mnuExit_Action
    MainForm.Close
End Sub

Sub mnuAbout_Action
    MsgBox.Show( _
        "Tek TDS2xx, TDS2xxx and TDS30xx Series Screen Dump Utility" & CRLF & _
        "Works with TDS200/TDS2000/TPS2000/TDS3000 Series Scopes with" & CRLF & _
        "TDS2CM/TDS2CMA/TDS2CMAX/TDS2MM/TDS2MEM options" & CRLF & _
        "TDS3000 Series: 38400 bauds," & CRLF & _
        "TDS2000 Series: 19200 bauds," & CRLF & _
        "TDS200 Series: 9600 bauds" & CRLF & _
        "Flow Control: None   EOL: LF   Parity: None", _
        "TDS Screen Dump")
End Sub

Sub MainForm_Closed
    CloseComPort
End Sub
 
Last edited:

JohnC

Expert
Licensed User
Longtime User
until VB 6.0 stopped working under Windows 10/11.
I have a lot of client legacy VB6 apps.

What exactly was the problem trying to run VB6 apps on Windows 10/11?
 

aminoacid

Well-Known Member
Licensed User
Longtime User
I have a lot of client legacy VB6 apps.

Me too! All my VB6 Applicatons work fine under Windows 10/11 and even the latest versions of Windows Server. They also work great with WINE/Ubuntu. So I have no reason to convert. The VB6 Complier & Development tools can be also installed to run under Windows 10/11 (it's a semi-manual process - do a search for info on this), so maintaining the code is not an issue.

But it is nice to know that you were successful in converting the application to B4J so easily.
 

Didier9

Well-Known Member
Licensed User
Longtime User
I have a lot of client legacy VB6 apps.

What exactly was the problem trying to run VB6 apps on Windows 10/11?
COM objects are no longer installed by default, and installing them manually does not always work for some mysterious reasons (they will not register, so they cannot be accessed).
I found a library on SourceForge that actually seems to solve the problem of running old executables under W11 https://sourceforge.net/projects/vb6extendedruntime/ but the old VB 6.0 development tools just will not work under Windows 11, so you can't change the old code, unless you have an older machine.
 

Didier9

Well-Known Member
Licensed User
Longtime User
Me too! All my VB6 Applicatons work fine under Windows 10/11 and even the latest versions of Windows Server. They also work great with WINE/Ubuntu. So I have no reason to convert. The VB6 Complier & Development tools can be also installed to run under Windows 10/11 (it's a semi-manual process - do a search for info on this), so maintaining the code is not an issue.

But it is nice to know that you were successful in converting the application to B4J so easily.
I have been able to install the dev tools on some earlier W11 machines, but at best it was hit and miss (works on some machines but not all) then it came to the point where as Windows updated itself over time, even those that worked early on stopped so I got the message :)
It may have been due to the fact those machines are on a corporate network with a lot of IT restrictions, but even at home I ended having issues.
The VB6 runtime I liked above does make the process considerably less painful to run old executables though.
 

JohnC

Expert
Licensed User
Longtime User
they will not register,
Are these Microsoft COM objects or third-party components?

I ask because the COM objects may depend on other DLLs, runtime files, or COM components that need to be installed and registered first. If one of those dependencies is missing or the wrong 32-bit/64-bit registration tool is used, the main COM object may fail to register or may register successfully but still be inaccessible to the application.
 

aminoacid

Well-Known Member
Licensed User
Longtime User
the old VB 6.0 development tools just will not work under Windows 11, so you can't change the old code, unless you have an older machine.

I disagree. I have installed the full VB6.0 Professional development tools to run under Windows 11 and it works flawlessly. It's been a while since I did that, but If you are really interested I'd be glad to dig up my notes and share it with you. You just need to have your original VB6 Professional CD available.

As far as installing VB6 apps on a Windows 10/11/Server machine is concerned, all my VB6 Apps were packaged using InstallShield so all the dependencies were packaged in one EXE. Just running that old "setup.exe" on the target machine installed the apps perfectly with no manual intervention. Now every time I need to update the Apps, all I do is copy over the compiled runtime EXE file.
 
Last edited:

Didier9

Well-Known Member
Licensed User
Longtime User
I disagree. I have installed the full VB6.0 Professional development tools to run under Windows 11 and it works flawlessly. It's been a while since I did that, but If you are really interested I'd be glad to dig up my notes and share it with you. You just need to have your original VB6 Professional CD available.

As far as installing VB6 apps on a Windows 10/11/Server machine is concerned, all my VB6 Apps were packaged using InstallShield so all the dependencies were packaged in one EXE. Just running that old "setup.exe" on the target machine installed the apps perfectly with no manual intervention. Now every time I need to update the Apps, all I do is copy over the compiled runtime EXE file.
Well, I don't want you to do any hard work, but if you have a clear definition of the steps involved, I still have my original VB 6.0 CD and I might try to install it at home for old time's sake. I have tried, but Windows 10 is where it stopped and I never managed to get everything I needed installed on Windows 11.

At work, it's a different story. The old VB 6.0 runtime including the dlls, ocxs and other libraries are deemed unsafe and they are only authorized on a couple of old Dell D620s that are still running the old software under Windows XP with no connection to the network :) We have bought the last 2 we have on eBay. These machines are pretty hard to kill, I recon that we will still be able to find working ones 10 years from now :)
 

Didier9

Well-Known Member
Licensed User
Longtime User
Are these Microsoft COM objects or third-party components?

I ask because the COM objects may depend on other DLLs, runtime files, or COM components that need to be installed and registered first. If one of those dependencies is missing or the wrong 32-bit/64-bit registration tool is used, the main COM object may fail to register or may register successfully but still be inaccessible to the application.
The original VB 6.0 software was using those:
COMDLG32.OCX
MSCOMM32.OCX
MSCOMCTL.OCX
 

JohnC

Expert
Licensed User
Longtime User
The original VB 6.0 software was using those:
COMDLG32.OCX
MSCOMM32.OCX
MSCOMCTL.OCX
I just asked AI what kind of things could cause problems on a win10/11 PC with these Microsoft controls and it reported a bunch of stuff.
The strongest suspects are:
  1. The OCX controls were not installed by a proper installer (and in the proper directories on win10/11).
  2. The 64-bit regsvr32 was used instead of the 32-bit one (and if done in with Admin rights).
  3. An incompatible or differently registered MSCOMCTL.OCX version is present.
  4. MSCOMM32.OCX is encountering a COM-port or USB-driver problem (or trying to use a com port higher than COM9).
  5. The application is trying to write into a protected directories (try running the app in admin mode).
  6. High-DPI scaling is making MSCOMCTL controls render incorrectly.
So, I would enter the exact error messages or issues you are having into an AI and try it's suggestions.
 
Top