|
| 1 | +--- |
| 2 | +title: Enhanced API Declarations |
| 3 | +parent: Advanced Features |
| 4 | +nav_order: 4 |
| 5 | +permalink: /Features/Advanced/API-Declarations |
| 6 | +--- |
| 7 | + |
| 8 | +# Enhancements to API and Method Declarations |
| 9 | + |
| 10 | +twinBASIC provides several enhancements to API and method declarations to make working with external libraries easier. |
| 11 | + |
| 12 | +## DeclareWide |
| 13 | + |
| 14 | +The `DeclareWide` keyword, in place of `Declare`, disables ANSI<->Unicode conversion for API calls. This applies both directly to arguments, and to String arguments inside a UDT. For example, the following are equivalent in functionality: |
| 15 | + |
| 16 | +```vb |
| 17 | +Public Declare PtrSafe Sub FooW Lib "some.dll" (ByVal bar As LongPtr) |
| 18 | +Public DeclareWide PtrSafe Sub Foo Lib "some.dll" Alias "FooW" (ByVal bar As String) |
| 19 | +``` |
| 20 | + |
| 21 | +Both represent a fully Unicode operation, but the allows direct use of the `String` datatype without requiring the use of `StrPtr` to prevent conversion. |
| 22 | + |
| 23 | +> [!WARNING] |
| 24 | +> This does **not** change the underlying data types-- the `String` type is a `BSTR`, not an `LPWSTR`, so in the event an API returns a pre-allocated `LPWSTR`, rather than filling a buffer you have created, it will not provide a valid `String` type. This would be the case where an API parameter is given as `[out] LPWSTR *arg`. |
| 25 | +
|
| 26 | +## CDecl Support |
| 27 | + |
| 28 | +The cdecl calling convention is supported both for API declares and methods in your code. This includes DLL exports in standard DLLs. |
| 29 | + |
| 30 | +### Examples |
| 31 | + |
| 32 | +```vb |
| 33 | +Private DeclareWide PtrSafe Function _wtoi64 CDecl Lib "msvcrt" (ByVal psz As String) As LongLong` |
| 34 | +``` |
| 35 | + |
| 36 | +``` |
| 37 | +[ DllExport ] |
| 38 | +Public Function MyExportedFunction CDecl(foo As Long, Bar As Long) As Long |
| 39 | +``` |
| 40 | + |
| 41 | +### CDecl Callbacks |
| 42 | + |
| 43 | +Support for callbacks using `CDecl` is also available. You would pass a delegate that includes `CDecl` as the definition in the prototype. Here is an example code that performs a quicksort using the [`qsort` function](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-wsprintfw): |
| 44 | + |
| 45 | +```vb |
| 46 | +Private Delegate Function LongComparator CDecl ( _ |
| 47 | + ByRef a As Long, _ |
| 48 | + ByRef b As Long _ |
| 49 | +) As Long |
| 50 | + |
| 51 | +Private Declare PtrSafe Sub qsort CDecl _ |
| 52 | +Lib "msvcrt" ( _ |
| 53 | + ByRef pFirst As Any, _ |
| 54 | + ByVal lNumber As Long, _ |
| 55 | + ByVal lSize As Long, _ |
| 56 | + ByVal pfnComparator As LongComparator _ |
| 57 | +) |
| 58 | + |
| 59 | +Public Sub CallMe() |
| 60 | + Dim z() As Long |
| 61 | + Dim i As Long |
| 62 | + Dim s As String |
| 63 | + |
| 64 | + ReDim z(10) As Long |
| 65 | + For i = 0 To UBound(z) |
| 66 | + z(i) = Int(Rnd * 1000) |
| 67 | + Next i |
| 68 | + qsort z(0), UBound(z) + 1, LenB(z(0)), AddressOf Comparator |
| 69 | + For i = 0 To UBound(z) |
| 70 | + s = s & CStr(z(i)) & vbNewLine |
| 71 | + Next i |
| 72 | + MsgBox s |
| 73 | +End Sub |
| 74 | + |
| 75 | +Private Function Comparator CDecl( _ |
| 76 | + ByRef a As Long, _ |
| 77 | + ByRef b As Long _ |
| 78 | +) As Long |
| 79 | + Comparator = a - b |
| 80 | +End Function |
| 81 | +``` |
| 82 | + |
| 83 | +## Support for Passing User-Defined Types ByVal |
| 84 | + |
| 85 | +Simple UDTs can now be passed ByVal in APIs, interfaces, and any other method. In VBx this previously required workarounds like passing each argument separately. |
| 86 | + |
| 87 | +```vb |
| 88 | +Public Declare PtrSafe Function LBItemFromPt Lib "comctl32" (ByVal hLB As LongPtr, ByVal PXY As POINT, ByVal bAutoScroll As BOOL) As Long |
| 89 | + |
| 90 | +Interface IDropTarget Extends stdole.IUnknown |
| 91 | + Sub DragEnter(ByVal pDataObject As IDataObject, ByVal grfKeyState As KeyStateMouse, ByVal pt As POINT, pdwEffect As DROPEFFECTS) |
| 92 | +``` |
| 93 | + |
| 94 | +and so on. For this feature, a "simple" UDT is one that does not have members that are reference counted or are otherwise managed in the background, so may not contain interface, String, or Variant types. They may contain other UDTs. |
| 95 | + |
| 96 | +## Variadic Arguments Support |
| 97 | + |
| 98 | +With `cdecl` calling convention fully supported, twinBASIC can also handle variadic functions. In C/C++, those functions contain an ellipsis `...` as part of their arguments. This is represented in tB As `{ByRef | ByVal} ParamArray ... As Any()`. Note that `ByRef` or `ByVal` must be explicitly marked; implicit `ByRef` is not allowed. |
| 99 | + |
| 100 | +### Example Using wsprintfW |
| 101 | + |
| 102 | +Using the [given C/C++ prototype](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-wsprintfw): |
| 103 | + |
| 104 | +```cpp |
| 105 | +int WINAPIV wsprintfW( |
| 106 | + [out] LPWSTR unnamedParam1, |
| 107 | + [in] LPCWSTR unnamedParam2, |
| 108 | + ... |
| 109 | +); |
| 110 | +``` |
| 111 | + |
| 112 | +The twinBASIC declaration and function using it can be written as shown: |
| 113 | + |
| 114 | +```vb |
| 115 | +Private DeclareWide PtrSafe Function wsprintfW CDecl _ |
| 116 | +Lib "user32" ( _ |
| 117 | + ByVal buf As String, _ |
| 118 | + ByVal format As String, _ |
| 119 | + ByVal ParamArray args As Any() _ |
| 120 | +) As Long |
| 121 | + |
| 122 | +Private Sub Test() |
| 123 | + Dim buf As String = Space(1024) |
| 124 | + wsprintfW(buf, "%d %d %d", 1, 2, 3) |
| 125 | + MsgBox buf |
| 126 | +End Sub |
| 127 | +``` |
| 128 | + |
| 129 | +### va_list Arguments |
| 130 | + |
| 131 | +For functions which contain the `va_list` type as part of their arguments the ParamArray declaration must be `ByRef`. |
| 132 | + |
| 133 | +## PreserveSig |
| 134 | + |
| 135 | +The `[PreserveSig]` attribute was described earlier for COM methods, but it can also be used on API declares. For APIs, the default is `True`. So therefore, you can specify `False` in order to rewrite the last parameter as a return. |
| 136 | + |
| 137 | +### Example |
| 138 | + |
| 139 | +```vb |
| 140 | +Public Declare PtrSafe Function SHGetDesktopFolder Lib "shell32" (ppshf As IShellFolder) As Long |
| 141 | +``` |
| 142 | + |
| 143 | +can be rewritten as: |
| 144 | + |
| 145 | +```vb |
| 146 | +[PreserveSig(False)] |
| 147 | +Public Declare PtrSafe Function SHGetDesktopFolder Lib "shell32" () As IShellFolder` |
| 148 | +``` |
0 commit comments