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.
Convention |
Description |
---|---|
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, the address of the “this” object is pushed on the stack as well. After the function/method returns, the calling method adjusts the stack. This calling convention allows for functions or methods with a variable number of arguments, like printf(). The caller knows the # of parameters that were passed, so the calling is the best candidate for adjusting 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, the last parameter usually becomes an array of values, so there still is some flexibility. In C/C++ this is called the __stdcall calling convention. This calling convention is used by most functions in the windows API. In VO this is also called WINCALL or CALLBACK. In 16bits windows, WINCALL was different from PASCAL, but 32 bits windows and later versions 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. In C/C++ this is called __fastcall. |
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 three 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 call 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, 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 other calling convention.