What’s Coming in X# 3: Modern .Net Features and .NET 8+ Compatibility
- Robert van der Hulst
As we approach the release of X# 3, we’re excited to share some insights into what’s coming next for the X# language and development platform.
While X# 2.24 will mark the final version in the 2.x series—with continued support in the form of critical bug fixes—our focus is now shifting toward modernization and .NET 8+ support. X# 3 is designed to bridge the gap between traditional X# development and the evolving .NET ecosystem.
Let’s take a closer look at the modern .Net features that X# 3 will bring to the table.
XSharp Notebook
- Fabrice Foray
If you want to learn and practice XSharp Core, you can start using our new XSharp Notebooks.
A Notebook is a file with cells, and each cell can be of different types.
A cell can contain text in Markdown format with instructions and help, or some code in a specific language, currently XSharp, that you will need to test and amend to your needs.
A series of Notebooks can be found here :
https://github.com/X-Sharp/xsharp-notebooks
You will find instructions to setup your learning environment : What you need, How to install, etc
You will also find folders with different levels and topics. Currently, the First Steps series focuses on XSharp Core, but we plan to offer Notebooks in other dialects in the future as well. We also plan to offer several other topics (like SQL-basic with X#, etc).
These Notebooks are still under development, but you can already start your journey, and we hope you enjoy it! ;)
Calling conventions
- Robert van der Hulst
Calling conventions are something from the unmanaged world. They describe how parameters should be passed when you call a function or method and they also describe who is responsible for adjusting the stack when the called function / method returns.
Different compilers have different default strategies for passing parameters to functions.
|
STRICT |
This is the most common calling convention in the C/C++ world. With this convention parameters are pushed on the stack. Value types are pushed completely and for reference types the address of the variable is pushed. When a method is called then also the address of the "this" object is pushed on the stack. After the function / method returns then the calling method adjusts the stack. In C/C++ this is also called __cdecl In VO (and X#) there is also a synonym "ASPEN" for this. |
|
PASCAL |
This calling convention is used a lot in the Pascal world. It looks a lot like the STRICT calling convention, but now the function / method that gets called adjusts the stack when it returns. Of course, this also means that there cannot be a variable number of arguments. If and when that is necessary, then usually the last parameter becomes an array of values, so there still is some flexibility. In VO this is also called WINCALL or CALLBACK. In 16bits windows WINCALL was different from PASCAL but 32 bits windows and later dropped that difference. |
|
THISCALL |
This is a special variant of the PASCAL calling convention, where the "this" pointer is not pushed on the stack but passed in a register (usually the ECX register). Passing the "this" in the register can be faster, especially when the register is not used for something else, so repeated calls for the same object to not have to push the "this" pointer. In C/C++ this is called __thisccall |
|
FASTCALL |
This calling convention tries to pass as many parameters in registers as possible. |
|
CLIPPER |
This is a special calling convention in the Xbase world, where parameters to a function are (technically) all optional and where you can also pass more values than you have declared parameters. Originally in the Xbase languages the calling code would push the values on the stack and would also pass the parameter count, so the function that is called "knows" how many parameters are passed. In .Net there is no real equivalent for that. To emulate the CLIPPER calling convention we generate a special PARAMS parameter that contains an array of USUAL values. Parameters of type PARAMS must be the last (or only) parameter in the list of parameters. The Roslyn compiler (that we use for x#) will automatically wrap all values that are passed to a function / method with clipper calling convention in an array. Of course, when you declare a function like this FUNCTION Foo(a,b,c) Then you expect that you will have 3 local variables in your code with the names "a", "b" and "c". The compiler however generates a function with a params argument. Something like this: FUNCTION Foo(args PARAMS USUAL[]) Inside the function we then generate local variables with the name of the parameters that you have declared LOCAL a := args[1] as USUAL In reality, the code is a bit more complex, because you may decide to all the function with less parameters than were declared. We have to take that into account. It looks like this then: LOCAL numParams := args:Length The names for "numParams" and "args" are generated by the complier with a special character in them, to make sure that we do not introduce variable names that conflict with names in your code. The X# debugger support layer also hides these special variables. |
For "normal" managed code, you really only have to deal with 2 calling conventions:
- For untyped methods the compiler uses the CLIPPER calling convention
- For typed methods the compiler there is no difference between STRICT and PASCAL. They both produce the same code
Only when you call unmanaged code in other DLLs then you need to use one of the other calling conventions. You have to "know" what the DLL uses. One problem is that quite often the calling convention in C/C++ code is hidden in a compiler macro.
As a rule of thumb you should use STRICT for C/C++ code and PASCAL for windows api funtions.
If it does not work (for example, the .Net runtime complains about stack problems), then switch to the oher calling convention.
Indexes, Sorting and how a bug was not a bug
- Robert van der Hulst
This week I was approached by a (valued) customer that was convinced that he had found a problem in the DBFCDX driver in X# and the customer had a reproducible example of a seek in a DBF that failed in X# but worked in Visual Objects(VO). And to be honest, I also had the feeling that this could indeed be a bug
In this (long) article I would like to explain the problem and also give a little bit of background info about CDX indexes and sorting.
So I loaded the code from the customer and indeed the seek failed in X# and succeeded in VO.
Then I ran an internal command that dumps the contents of a tag inside a CDX to a text file, so I could inspect the various index pages.
After that I recreated the index in X# and dumped that index too and I found that there was difference in the sort order.
I thought: Bingo there is indeed a problem in our sorting routine and I confirmed the bug to the customer. But I was wrong....
VO Compatible Arithmetic in X#
- Chris Pyrgas
In the last days, we have been trying to (among other things) complete the VO compatibility aspect of X#, by implementing one final missing piece, behavior of numeric arithmetic that is completely compatible to Visual Objects.
The area where this becomes visible is when dealing with integral numbers (either variables or constants) with a size < 32 bits, so of the type BYTE, SHORT and WORD.
For example, consider this code:
LOCAL b1,b2 AS BYTE LOCAL n AS INT b1 := 250 b2 := 10 n := b1 + b2 ? n
Currently this prints 260 in X#, as you may (or not!) expect. But in VO, the same code returns 4; the operation is performed between 2 BYTE vars and so is the result, which first overflows from the max value of 255 that can be stored in a byte and results to that value of 4 (260 mod 256), which is then in turn assigned to the INT var.
Progress on VFP runtime, SQL functions
- Robert van der Hulst
Those of you that watch our commits on Github may have already noticed it:
we are working on the SQL functions for FoxPro support.
What have have so far:
SQLConnect()
SQLStringConnect()
SQLDisconnect()
These three create a SqlStatement object with an embedded SqlConnection object. When the statement is marked as "Shared" then the Connection can be shared between multiple statements.
To get and set properties for these SQLStatements you can call
SQLSetProp()
SQLGetProp()
ILSpy plugin for V5.02
- Fabrice Foray
The XSharp language ILSpy5 plugin was not working with the latest stable version of ILSpy : V5.02
This has been addresses and you can get that version in the Downloads/General/Tools section.
Unzip the DLL in the same folder as the ILSpy Binaries and you're done.
If you are looking to the ILSpy V5.02 binary, go to ILSpy binaries.
Now, when running ISpy, you can set the language as XSharp.
Don't forget that the full source code of the Plugin is available in the public XSharp Repository on Github; you can also view there the current state of developement of the tool ( What is working currently, Changelog, ... )
ILSpy is the open-source .NET assembly browser and decompiler, and as shown during the xBase Future 2018, you can now use ILSpy to view and decompile .NET assembly as XSharp Language.
Debugging Tips
- Robert van der Hulst
We have received some questions about missing features in the Debugger.
In VO you had the ability to open windows in the debugger to see the list of open workareas, settings or privates. In X# we don't have these options yet. However the Visual Studio debugger is very powerfull and with the help of some clever watch settings you can see almost everything (until we have written a dedicated window for this).
Open workareas
To see the open workareas of an application, add a watch with the following contents(note that this is CaSe SenSiTiVe):
XSharp.RuntimeState.Workareas
I did this in the transported Explorer example just after opening the files and then the result is this:

You can see that there are 2 aliases opened. If you expand that then you'll see their names and workarea numbers.
The Current workarea is of type DBFNTX and has an alias Orders
Page 1 of 4

