OptionParser (Brilliant!) MIGHT be broken in 64-bit Windows

@Jeremy_Cowgar 's BRILLIANT xojo-option-parser might be broken (or this may be a 64-bit Xojo bug). Easily reproducible with OptionParser’s included test app.

When I try to parse any command line with a built 64-bit .exe running a Desktop Windows app from the windows CMD command line, it crashes XojoGUIFramework64.dll. I was able to reproduce by changing the test app to 64-bit, then executing the command line:

C:\Windows\system32>“C:\Users\SMG\dev\Xojo Test Apps\xojo-option-parser-master\xojo-option-parser-master\Desktop Example\Builds - OptionParser Example\Windows 64 bit\OptionParser Example\OptionParser Example.exe” -s=‘some text’

Note that there is no problem running a DEBUG version from inside the IDE debugger.

Anyone else using OptionParser with 2021 Release 3.1?

It works when its run in the IDE because there’s a constant at the top of OptionParser.CommandLineArgs called kDebugDeclares that skips the declares and uses a bit of regex instead, not sure why. You’ll need to set that to True to test the declare code.

There’s a bug in the TargetWin32 section, the code that moves along the pointers to the args is hard coded with a length of 4, when you switch to 64bit this needs to be 8 or you run into issues.

Declare Function GetCommandLineW Lib "kernel32.dll" () As Ptr
Declare Function CommandLineToArgvW Lib "shell32.dll" (lpCmdLine As Ptr, ByRef pNumArgs As Integer) As Ptr
Declare Sub LocalFree Lib "kernel32.dll" (p As Ptr)

dim cl as Ptr = GetCommandLineW()
dim n as Integer
dim argList as Ptr = CommandLineToArgvW (cl, n)
For idx As Integer = 0 To n - 1
  Dim mb As MemoryBlock = argList.Ptr(idx * 4) '<----- **** PROBLEM HERE ****
  // mb points to a UTF16 0-terminated string. It seems we have to scan its length ourselves now.
  dim len as Integer
  while mb.UInt16Value(len) <> 0
    len = len + 2
  wend
  dim s as String = mb.StringValue(0,len).DefineEncoding(Encodings.UTF16)
  s = s.ConvertEncoding(Encodings.UTF8)
  args.Append s
next
LocalFree(argList)

You can either change the line:

Dim mb As MemoryBlock = argList.Ptr(idx * 4)

to

Dim mb As MemoryBlock = argList.Ptr(idx * COM.SIZEOF_PTR)

or replace the whole block with the one I wrote a long time ago which is a little bit more elegant:

Declare Function GetCommandLineW Lib "Kernel32.dll" () As WString
Declare Function CommandLineToArgvW Lib "Shell32.dll" (lpCmdLine As WString, ByRef pNumArgs As Int32) As Ptr
Declare Function LocalFree Lib "Kernel32.dll" (HLOCAL As Ptr) As Integer

Dim numberOfArgs As Int32
Dim argsMemoryBlock As MemoryBlock = CommandLineToArgvW(GetCommandLineW(), numberOfArgs)

For c As Integer = 0 To numberOfArgs - 1
  args.Append(argsMemoryBlock.Ptr(c * COM.SIZEOF_PTR).WString(0).ConvertEncoding(Encodings.UTF8))
Next

Call LocalFree(argsMemoryBlock)

I’ve not looked at any of the other code.

2 Likes

@JulianS – that was exactly the problem – and the solution! Thank you!

1 Like

@JulianS, would you mind submitting a Pull Request in GitHub for your fix? Thank you for fixing the code and helping out here!

Jeremy

1 Like

No problemo. I was going to do it the other day but I wasn’t sure if it was going to be a waste of time or not as I didn’t know if you were active on there. I’ll send it over when I get a chance.

Please send your optimized code, and do it against the develop branch.