They say its fixed but

I emailed Geoff before posting this here
So far no response

There are several reports about System.Version and how faulty string parsing breaks under different situations

System.version causes an out of bounds exception when the system language is Japanese

Make System.Version not parse string data for initialization - and avoid out of bounds exceptions (with src for doing this RIGHT)

iOS - Calling System.Version crashes on devices running iOS 16.2

System.version throws and exception when system language set to Japanese

The newest betas started and I poked at it with Hopper JUST to see what the hell they did to “fix” this error


In the left hand pane in the image attached you can see the method being viewed (_System.VersionData._Initialize)

And the disassembly in the middle, circled in red, is the CALL TO THE OS ROUTINE used

It calls operatingSystemVersionString

This is, was, and STILL IS wrong

Despite being literally GIVEN the code more than once by me and Christian and Sam its STILL doing it in the way Apple literally says it is NOT suitable for parsing

And you wonder why some people have no faith in the team ?


But are you sure this call is still used to be parsed later?
Perhaps they actually have followed the recommendations and use operatingSystemVersionString to, e.g. just show it in the about box?
Or perhaps it’s a call they forgot to remove but is now unused?

Just wondering if you considered these options.

1 Like

100% sure

Want me to show you the rest of the disassembly ?
Disassemble it for yourself its you dont believe me

Theres lots and lots of disassembled code
All of it string manipulations like ToArray etc


Its just more tacky string parsing

IF I could figure out how to get that call to operatingSystemVersionString to return a really bad string I’d easily be able to get their code to fail
That I’m 100% certain of

1 Like

It’s not that I don’t believe you, I do most of the time. It’s just you didn’t said you knew it was parsed later, so you could have assumed the call is still there so they’ve not changed their code.

oh heck no I scrolled through the disassembly and can see calls to ToArray , String.length etc following the call to the wrong OS API

and, it NEVER calls the correct OS API before that method exits

that I can see in the disassembly


Even if apple says that this string should never be parsed…

Now that you dared to said he is wrong, expect an improved parsing method because xojo knows better how to do things :crazy_face:


Heck I’ll post the code HERE so they can just lift it

Attributes( StructureAlignment = 1 ) Protected Structure OSVersionInfo
  major as integer
  minor as integer
  bug as integer
End Structure

this is in a module that has a few properties

Private Property m_Build As string
Private Property m_Bug As integer
Private Property m_cputype As string
Private Property m_MajorVersion As integer
Private Property m_MinorVersion As integer
Private Property m_Initialized As Boolean

The first time ANY method in the module tries to grab one of those properties its initialized

Private Sub Initialize()
  If m_Initialized Then 
  End If
  m_Initialized = True
  #If TargetMacOS
    // --- We're using 10.10
    Declare Function NSClassFromString Lib "AppKit" ( className As CFStringRef ) As Ptr
    Declare Function processInfo Lib "AppKit" Selector "processInfo" ( ClassRef As Ptr ) As Ptr
    Dim myInfo As Ptr = processInfo( NSClassFromString( "NSProcessInfo" ) )
    Declare Function operatingSystemVersion Lib "AppKit" Selector "operatingSystemVersion" ( NSProcessInfo As Ptr ) As OSVersionInfo
    Dim rvalue As OSVersionInfo = operatingSystemVersion( myInfo )
    m_MajorVersion = rValue.major
    m_MinorVersion = rvalue.minor
    m_Bug = rvalue.bug
    Declare Function sysctlbyname Lib "/usr/lib/libSystem.dylib" (name As cString, out As ptr, ByRef size As UInteger, newP As ptr, newPSize As UInteger) As Integer
    Dim size As UInteger = 128
    Dim mb As New memoryblock(size)
    If sysctlbyname( "kern.osversion", mb, size, Nil, 0 ) = 0 Then
      m_build = Trim(mb.CString(0))
    End If
    size = 256
    mb = New memoryblock(size)
    Dim retvalue As Integer = sysctlbyname( "machdep.cpu.brand_string", mb, size, Nil, 0 )
    If sysctlbyname( "machdep.cpu.brand_string", mb, size, Nil, 0 ) = 0 Then
      m_cputype = Trim(mb.CString(0))
    End If
  #ElseIf TargetWindows
    Dim m As MemoryBlock
    Dim wsuitemask As Integer
    Dim ret As Integer
    Dim szCSDVersion As String
    Dim s As String
    Soft Declare Function GetVersionExA Lib "kernel32" (lpVersionInformation As ptr) As Integer
    Soft Declare Function GetVersionExW Lib "kernel32" (lpVersionInformation As ptr) As Integer
    Dim retryUsingAVersion As Boolean = True
    If System.IsFunctionAvailable( "GetVersionExW", "Kernel32" ) Then
      retryUsingAVersion = False
      m = NewMemoryBlock(284) ''use this for osversioninfoex structure (2000+ only)
      m.long(0) = m.size 'must set size before calling getversionex 
      ret = GetVersionExW(m) 'if not 2000+, will return 0
      If ret = 0 Then
        // need to rety since 0 means "FAILED"
        m = NewMemoryBlock(276)
        m.long(0) = m.size 'must set size before calling getversionex 
        ret = GetVersionExW(m)
        If ret = 0 Then
          // Something really strange has happened, so use the A version
          // instead
          retryUsingAVersion = True
    End If
    If retryUsingAVersion = True Then
      m = NewMemoryBlock(156) ''use this for osversioninfoex structure (2000+ only)
      m.long(0) = m.size 'must set size before calling getversionex
      ret = GetVersionExA(m) 'if not 2000+, will return 0
      If ret = 0 Then
        m = NewMemoryBlock(148) ' 148 sum of the bytes included in the structure (long = 4bytes, etc.)
        m.long(0) = m.size 'must set size before calling getversionex
        ret = GetVersionExA(m)
        If ret = 0 Then
    End If
    m_MajorVersion = m.long(4)
    m_MinorVersion = m.long(8)
    m_Bug = m.long(12)
End Sub

have at it

This is all in GitHub - npalardy/KitchenSink: A starter project that has piles of utilities and extensions for Xojo (one of everything including the kitchen sink)

Fun port is because there’s NO parsing whatsoever I’d guess this is LESS code than they have now !!!


I fear that you are wasting your time. It is entirely possible that the Xojo team lacks the modesty necessary for acknowledging that they made a bad mistake.
You can’t help them with code…


You might be able to swizzle the class so that you can handle when Xojo asks for the version string. But only from your application.

Oh, they’ve been told publicly multiple times. There was even a whole thread on it.

Does filing feedback help? No.
Does communicating with them? No.
Does public lobbying help? No.
Does providing them with source code help? No.
Does public shaming them help? No.

The complaints on this will pass, as the complainers will eventually go away, we don’t need experienced customers anyway.

Hmmmmm we should talk as I’d LOVE to demo to them in a separate project just how F’d up parsing the string is

Yet again


Perhaps its
Bah. Customers. WTF do THEY know ?
We didnt write that. We can’t use that
or some other ridiculous reason

They are optimising Norman’s code to become low code … takes a bit of time. All busy on Android. If you are on the last mile after 7 years it is quite challenging to brake down that speedy car.

Don’t get tooo excited, not all classes can be swizzled (or they require it be done in a way I don’t know how).

Chances are, they’ll just ignore you anyway.

I honestly get this feeling, that challenging them on things upsets them. Like they don’t want to acknowledge that they can make mistakes.

Like they’d rather die on this hill, than accept they made a mistake.


Which I can understand. We are all making mistake. I for instance choose Xojo, not easy to accept the truth that it was the wrong choice :-), and a lot(!) of work to escape.


At the end of the day it is simple. The biggest error is not to choose something. The biggest error is then not to change. There is no help and no chance for getting out otherwise. That you have done. With JavaScript based Webapps in a really impressive way. With javaFX in a real professional job you have done in learning it.

There was and is no other way. But instead I can listen only: “yes not for me while… And while… And while” but all of this without real arguments but the evidence that they did not get that it is the solution for exactly their problems.

To realize that there decisions are wrong is one thing. To do it right after another


Here’s the bizarre thing…

I have hacked NSProcessInfo and can make it return whatever the fuck I want for [NSProcessInfo operatingSystemVersionString].

But with 2021r2.1, Xojo returns the actual OS information…
Screen Shot 2023-06-16 at 11.53.16 AM

Here’s a Xojo project with the code. Look in the NSProcessInfohacker module to see how to swizzle.

1 Like

That is awesome !!!

the TRICK to breaking their code is to put the call to set up the hack in a CONSTRUCTOR for the APP instance so its replaced VERY VERY early on - before they call their set up

like this

and sure enough it breaks :slight_smile:


Tried your example with my old version of Xojo and it still reports the actual version. So they made this dumb ass change after 2021r2.1

But I’m glad it helps you break Xojo… Ha ha :slight_smile:

Actually what makes this even worse IMHO, is that they’re doing this on startup and caching the value. Wasting CPU cycles and memory. Tsck…

Yeah I’d have to compile & disassemble the app to see how early they do it BUT its nice to see I can swizzle this early enough to show that their code IS fragile

But, still no reply from Geoff so its clear to me that something as simple as this to get right just isnt something that they care about doing right


1 Like