Open WaynePhillipsEA opened 3 years ago
Btw, there's an add-in for VB6 which "fixes" CDecl calling convention on Windows -- VBCDeclFix
Added in v0.9.1750
Thanks for feature update. The API declaration works fine. However, I couldn't figure out how the CDecl callback works.
Private Declare Function snwprintf1 CDecl Lib "msvcrt" Alias "_snwprintf" ( ByVal pszBuffer As Long, ByVal lCount As Long, ByVal pszFormat As Long, ByRef pArg1 As Any) As Long
Private Declare Sub qsort CDecl Lib "msvcrt" ( ByRef pFirst As Any, ByVal lNumber As Long, ByVal lSize As Long, ByVal pfnComparator As Long)
Public Sub Main()
Dim sBuf As String
sBuf = Space$(255)
msgbox Left$(sBuf, snwprintf1(StrPtr(sBuf), Len(sBuf), StrPtr("Test %ld"), ByVal 123&))
' Works great ! :)))
Dim z() As Long
Dim i As Long
Dim s As String
ReDim z(10) as long
For i = 0 To UBound(z)
z(i) = Int(Rnd * 1000)
Next i
qsort z(0), UBound(z) + 1, LenB(z(0)), AddressOf Comparator
For i = 0 To UBound(z)
s = s & CStr(z(i)) & vbNewLine
Next i
MsgBox s
End Sub
Private Function Comparator CDecl ( _
ByRef a As Long, _
ByRef b As Long) As Long
Comparator = a - b
End Function
End Module
I tried different ways of declaring the Comparator function. Maybe I missed something.
Alternatives which are not working:
Private Function CDecl Comparator ( _
ByRef a As Long, _
ByRef b As Long) As Long
Comparator = a - b
End Function
Private CDecl Function Comparator ( _
ByRef a As Long, _
ByRef b As Long) As Long
Comparator = a - b
End Function
Or are CDecl callbacks planned at later stage ? Anyhow thanks already.
Hi @Kr00l, sorry only had time to add the CDecl support for the API declares at the moment. Plus I forgot 😜
I'll re-open this and edit it so that we can add support for all members / callbacks too.
Some CDecl API declarations have a vararg. ...
Example API:
STRSAFEAPI StringCchPrintfW(
STRSAFE_LPWSTR pszDest,
size_t cchDest,
STRSAFE_LPCWSTR pszFormat,
...
);
Would it be technically possible to allow a vararg VB-style declaration in the API declare?
For example the As Any
is something special, something similar special for vararg would be needed?
Just a thought.
It can be done. We could potentially just use ParamArray () As Any syntax
A follow up to the request regarding handling the varargs for Cdecl calling syntax. Apparently there are some that accepts a va_list
instead of ...
. Will that be also handled by the ParamArray () As Any
as well?
For the records.
Since v0.13.79 tB now supports ByRef ParamArray Args As Any()
which handles va_list
and ByVal ParamArray Args As Any()
which handles ...
for API declares.
It is mandatory to state either ByRef or ByVal. (no implicit ByRef possible)
Thus, only support for CDecl callbacks is missing (AddressOf) that tB can handle flat CDecl dll's for all aspects.
So how should the syntax for callback functions look like? I gave above some few examples. Or is it better to have an Attribute for this?
IMO as for API declares it's solved by syntax it would be more harmonic to have it by syntax for functions also. Also the intent looks stronger by language syntax. So for everyone right away to see.
I know that this is low-prio but maybe Wayne can comment if this is an quick missing piece or it has some side-effects to solve also.
Just to offer an alternative... Instead of decorating a function, we decorate the AddressOf
instead:
Private Declare DoIt CDecl Lib "foo.dll" (ByVal callback As LongPtr)
...
DoIt AddressOf CDecl MyCallback
...
Private Function MyCallback()
...
End Function
The advantage of that approach is that it avoids polluting the language with functions that requires calling conventions to be used, making it easy and simple for me to directly call my MyCallback
without any change in the syntax. The compiler is then free to generate the necessary stub to wrap the MyCallback
to comply with the cdecl calling convention.
Yes, there's two approaches here... either change the real procedure to use CDecl calling convention, or generate small stubs when needed at the callsite to fixup cdecl callbacks to the real stdcall convention. Handily, it only affects 32bit as CDecl is already the calling convention on 64-bit.
Yes, there's two approaches here... either change the real procedure to use CDecl calling convention, or generate small stubs when needed at the callsite to fixup cdecl callbacks to the real stdcall convention. Handily, it only affects 32bit as CDecl is already the calling convention on 64-bit.
Two approaches.. ok, why not allowing the two in paralell? It would allow native CDecl functions or with the stub forward. Scenarios:
FWIW, I am not sold on the idea of being able to specify calling conventions within the functions. For one, it seems to go against the grain of BASIC -- we want to focus on the solution, not implementation. That kind of specification belongs at the boundaries, which would be the Declare
statement and somewhat indirectly AddressOf
operator. Within the language, there should be no need to even think about it.
Even in .NET, we don't get to control calling convention except via the DllImport
and UnmanagedFunctionPointer
attributes.
BTW, looking at the UnmanagedFunctionPointer
, it seems to me that it would be far more elegant to handle calling conventions for callback by introducing delegates to tB (discussed in twinbasic/lang-design#44). That way it stays at the boundary where it belongs.
My thoughts;
I didn’t consider the fact that a type library allows for different calling conventions. A follow up question would be whether allowing different calling conventions to be used among other tB procedures would negatively affect tB performance or stability. I was thinking that restricting it to the boundary would avoid issues. Is that true?
CDecl is a very small tweak to the function epilogue, and so shouldn't affect much at all since we already support CDecl at the call sites for DLLs. It will require more tests to be written than the AddressOf version though, since we will want to make sure that generated type libraries are correct, and that we can consume existing type-libraries that have CDecl members.
Few more comments..
The CDecl syntax keyword will anyhow be ignored on x64 build. So that's an easy going for x64 at all. It's just a necessity for x86 build targets. What I want to say : a project using this syntax on x86 can migrate to x64 without refactoring something.
The CDecl syntax is optional. If it will get misused by the AddressOf operator or the function itself is no difference.
Concerning the two way approach. Effectively we normally would like to avoid it. Because for many it would not be clear about the difference. So IMO a native solution is in favor of something simulated.
when will cdecl callbacks be support?
As of BETA 287, the CDecl calling convention is now supported for all internal procedures, so for example, you can now define a CDecl callback:
Function MyCDeclCallback CDecl(ByVal A As Long)
End Function
And thus using AddressOf MyCDeclCallback
now gives you a function pointer that is CDecl compatible.
(note: syntax copied to match the existing CDecl support in declare statements, where the CDecl
keyword is expected immediately following the procedure name)
have some test, it is working.
Is your feature request related to a problem? Please describe. Some DLL declarations require CDecl calling convention support, which is lacking in VB6 and VBA (except on Mac). twinBASIC now supports CDecl for DLL calls (in v0.9.1750+), but some APIs require callback functions that also use the CDecl calling convention.
Describe the solution you'd like To support the CDecl keyword on all procedure declarations.
Additional context We must offer the same CDecl feature for regular procedure declarations, so that they can be passed as callbacks to APIs that require cdecl callbacks through the AddressOf operator.
Source Email [krool]