Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ _site
.jekyll-cache
.jekyll-metadata
*.af~lock~
_bak
25 changes: 25 additions & 0 deletions docs/Features/64bit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: 64bit Compilation
parent: Features
nav_order: 9
permalink: /Features/64bit
---

# 64bit Compilation

twinBASIC can compile native 64bit executables in addition to 32bit. The syntax is compatible with VBA7 for this: the `LongPtr` data type and the standard to mark APIs `PtrSafe`.

## Example Syntax

```vb
Public Declare PtrSafe Sub foo Lib "bar" (ByVal hWnd As LongPtr)
```

## Important Considerations

> [!IMPORTANT]
> There is a lot more required to get most 32bit apps to work properly as 64bit. Only some `Long` variables are to be changed, and this is determined by their C/C++ data types, of which there are many. Examples that need to be `LongPtr` include handles like `HWND, HBITMAP, HICON,` and `HANDLE`; pointers like `void*, PVOID, ULONG_PTR, DWORD_PTR,` and `LPWSTR/PWSTR/LPCWSTR/WCHAR*` when passed as `Long`; and the `SIZE_T` type found in CopyMemory and memory allocation functions.

While the `PtrSafe` keyword is not mandatory, these changes still must be made. Additionally, any code working with memory pointers must account for the fact all the types mentioned (and the many more not), as well as v-table entries, are now either 4 or 8 bytes, when most programmers have traditionally hard coded 4 bytes. There are also UDT alignment issues more frequently. This is all very complex and you should seek resources and advice when moving to 64bit (though remember, 32bit is still supported so this isn't a requirement).

For common Windows APIs and COM interfaces, a community-developed package is available that provides 64bit compatible definitions: [Windows Development Library for twinBASIC (WinDevLib)](https://github.com/fafalone/WinDevLib).
148 changes: 148 additions & 0 deletions docs/Features/Advanced/API-Declarations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
title: Enhanced API Declarations
parent: Advanced Features
nav_order: 4
permalink: /Features/Advanced/API-Declarations
---

# Enhancements to API and Method Declarations

twinBASIC provides several enhancements to API and method declarations to make working with external libraries easier.

## DeclareWide

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:

```vb
Public Declare PtrSafe Sub FooW Lib "some.dll" (ByVal bar As LongPtr)
Public DeclareWide PtrSafe Sub Foo Lib "some.dll" Alias "FooW" (ByVal bar As String)
```

Both represent a fully Unicode operation, but the allows direct use of the `String` datatype without requiring the use of `StrPtr` to prevent conversion.

> [!WARNING]
> 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`.

## CDecl Support

The cdecl calling convention is supported both for API declares and methods in your code. This includes DLL exports in standard DLLs.

### Examples

```vb
Private DeclareWide PtrSafe Function _wtoi64 CDecl Lib "msvcrt" (ByVal psz As String) As LongLong`
```

```
[ DllExport ]
Public Function MyExportedFunction CDecl(foo As Long, Bar As Long) As Long
```

### CDecl Callbacks

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):

```vb
Private Delegate Function LongComparator CDecl ( _
ByRef a As Long, _
ByRef b As Long _
) As Long

Private Declare PtrSafe Sub qsort CDecl _
Lib "msvcrt" ( _
ByRef pFirst As Any, _
ByVal lNumber As Long, _
ByVal lSize As Long, _
ByVal pfnComparator As LongComparator _
)

Public Sub CallMe()
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
```

## Support for Passing User-Defined Types ByVal

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.

```vb
Public Declare PtrSafe Function LBItemFromPt Lib "comctl32" (ByVal hLB As LongPtr, ByVal PXY As POINT, ByVal bAutoScroll As BOOL) As Long

Interface IDropTarget Extends stdole.IUnknown
Sub DragEnter(ByVal pDataObject As IDataObject, ByVal grfKeyState As KeyStateMouse, ByVal pt As POINT, pdwEffect As DROPEFFECTS)
```

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.

## Variadic Arguments Support

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.

### Example Using wsprintfW

Using the [given C/C++ prototype](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-wsprintfw):

```cpp
int WINAPIV wsprintfW(
[out] LPWSTR unnamedParam1,
[in] LPCWSTR unnamedParam2,
...
);
```

The twinBASIC declaration and function using it can be written as shown:

```vb
Private DeclareWide PtrSafe Function wsprintfW CDecl _
Lib "user32" ( _
ByVal buf As String, _
ByVal format As String, _
ByVal ParamArray args As Any() _
) As Long

Private Sub Test()
Dim buf As String = Space(1024)
wsprintfW(buf, "%d %d %d", 1, 2, 3)
MsgBox buf
End Sub
```

### va_list Arguments

For functions which contain the `va_list` type as part of their arguments the ParamArray declaration must be `ByRef`.

## PreserveSig

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.

### Example

```vb
Public Declare PtrSafe Function SHGetDesktopFolder Lib "shell32" (ppshf As IShellFolder) As Long
```

can be rewritten as:

```vb
[PreserveSig(False)]
Public Declare PtrSafe Function SHGetDesktopFolder Lib "shell32" () As IShellFolder`
```
33 changes: 33 additions & 0 deletions docs/Features/Advanced/Assembly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: Direct Assembly Insertion
parent: Advanced Features
nav_order: 2
permalink: /Features/Advanced/Assembly
---

# Emit() and Naked Functions

Raw bytecode can be inserted into a binary with tB's `Emit()` function. To support this, functions can be marked as `Naked` to remove hidden tB code.

## Example

For example, the following is an implementation of the InterlockedIncrement compiler intrinsic that replaces the API in Microsoft C/C++ (adds one to `Addend` and returns the result, as an atomic operation which isn't guaranteed with regular code):

```vb
Public Function InlineInterlockedIncrement CDecl Naked(Addend As Long) As Long
#If Win64 Then
Emit(&Hb8, &H01, &H00, &H00, &H00) ' mov eax,0x1
Emit(&Hf0, &H0f, &Hc1, &H41, &H00) ' lock xadd DWORD PTR [rcx+0x4],eax
Emit(&Hff, &Hc0) ' inc eax
Emit(&Hc3) ' ret
#Else
Emit(&H8b, &H4c, &H24, &H04) ' mov ecx, DWORD PTR _Addend$[esp-4]
Emit(&Hb8, &H01, &H00, &H00, &H00) ' mov eax, 1
Emit(&Hf0, &H0f, &Hc1, &H01) ' lock xadd DWORD PTR [ecx], eax
Emit(&H40) ' inc eax
Emit(&Hc3) ' ret 0
#End If
End Function
```

(Note: The `CDecl` calling convention is optional; you can write x86 assembly using `_stdcall` and simply omit the notation.)
57 changes: 57 additions & 0 deletions docs/Features/Advanced/Classes-and-Modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Class Features
parent: Advanced Features
nav_order: 5
permalink: /Features/Advanced/Classes-and-Modules
---

# Class and Module Enhancements

twinBASIC provides several enhancements for classes and modules.

## Parameterized Class Constructors

Classes now support a `New` sub with ability to add arguments, called as the class is constructed prior to the `Class_Initialize` event.

### Example

For example a class can have:

```
[ComCreatable(False)]
Class MyClass
Private MyClassVar As Long
Sub New(Value As Long)
MyClassVar = Value
End Sub
End Class
```

then created by `Dim mc As MyClass = New MyClass(123)` which sets `MyClassVar` on create. Note: Classes using this must be private, have the `[ComCreatable(False)]` attribute, or also contain `Class_Initialize()`. `Class_Initialize()` will replace `New` in callers of a compiled OCX. Within the project, only `New` will be used if present.

## Private/Public Modifiers for Modules and Classes

A private module or class won't have its members entered into the type library in an ActiveX project.

## ReadOnly Variables

In a class, module-level variables can be declared as `ReadOnly`, e.g. `Private ReadOnly mStartDate As Date`. This allows more complex constant assignments: you can use a function return to set it inline, `Private ReadOnly mStartDate As Date = Now()`, or `ReadOnly` constants can be set in `Class_Initialize` or `Sub New(...)` (see parameterized class constructors above), but everywhere else, they can only be read, not changed.

## Exported Functions and Variables

It's possible to export a function or variable from standard modules, including with CDecl.

### Examples

```
[DllExport]
Public Const MyExportedSymbol As Long = &H00000001

[DllExport]
Public Function MyExportedFunction(ByVal arg As Long) As Long

[DllExport]
Public Function MyCDeclExport CDecl(ByVal arg As Long)
```

This is primarily used to create Standard DLLs (see [Project Types](../Project-Configuration/Project-Types.md)), but this functionality is also available in Standard EXE and other compiled project types.
50 changes: 50 additions & 0 deletions docs/Features/Advanced/Multithreading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: Multithreading
parent: Advanced Features
nav_order: 1
permalink: /Features/Advanced/Multithreading
---

# Thread Safety / Multithreading Support

While there's no native language syntax yet (planned), you can call `CreateThread` directly with no hacks. Previously, VBx and other BASIC languages typically required elaborate workarounds to be able to use `CreateThread` for anything but some specialized, extremely simple things. In twinBASIC, you can call it and all other threading APIs without any special steps, other than of course the careful management of doing threading at a low level like this.

## Example

In a new Standard EXE project, add a CommandButton and TextBox to your form:

```vb
Private Declare PtrSafe Function GetCurrentThreadId Lib "kernel32" () As Long

Private Declare PtrSafe Function CreateThread Lib "kernel32" ( _
ByRef lpThreadAttributes As Any, _
ByVal dwStackSize As Long, _
ByVal lpStartAddress As LongPtr, _
ByRef lpParameter As Any, _
ByVal dwCreationFlags As Long, _
ByRef lpThreadId As Long) As LongPtr

Private Declare PtrSafe Function WaitForSingleObject Lib "kernel32" ( _
ByVal hHandle As LongPtr, _
ByVal dwMilliseconds As Long) As Long

Private Const INFINITE = -1&

Private Sub Command1_Click() Handles Command1.Click
Dim lTID As Long
Dim lCurTID As Long
Dim hThreadNew As LongPtr
lCurTID = GetCurrentThreadId()
hThreadNew = CreateThread(ByVal 0, 0, AddressOf TestThread, ByVal 0, 0, lTID)
Text1.Text = "Thread " & lCurTID & " is waiting on thread " & lTID
Dim hr As Long
hr = WaitForSingleObject(hThreadNew, 30000&) 'Wait 30s as a default. You can use INFINITE instead if you never want to time out.
Text1.Text = "Wait end code " & CStr(hr)
End Sub

Public Sub TestThread()
MsgBox "Hello thread"
End Sub
```

Under a single-threaded code, if you called `TestThread` before updating `Text1.Text`, the text wouldn't update until you clicked ok on the message box. But here, the message box in launched in a separate thread, so execution continues and updates the text, after which we manually choose to wait for the message box thread to exit.
53 changes: 53 additions & 0 deletions docs/Features/Advanced/Static-Linking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: Static Linking
parent: Advanced Features
nav_order: 3
permalink: /Features/Advanced/Static-Linking
---

# Static Linking of OBJ and LIB Files

tB allows you to use properly compiled .lib and .obj files as statically linked libraries, using declares similar to DLLs, only referring a lib/obj file in your Miscellaneous files folder of your project. Once the file is in the project, it's set up with this syntax outside of declares.

## Example

Example from the sqlite sample:

```vb
#If Win64 Then
Import Library "/Miscellaneous/sqlite3_64.obj" As SQLITE3 Link "stdlib", "kernel32"
#Else
Import Library "/Miscellaneous/sqlite3_32.obj" As SQLITE3 Link "stdlib", "kernel32"
#End If
```

### Generic Syntax

```
Import Libary "Relative resource path" As NAMESPACE Link "dependency1", "dependency2", '...
```

## Using Imported Libraries

After that, you can use NAMESPACE in place of a DLL name, inside class/module declares:

```vb
' Compiled sqlite-amalgamation-3440200 (v3.44.2)
' using cmdline (MSVC): cl /c /Gw /Gy /GS- /DSQLITE_OMIT_SEH sqlite3.c
#If Win64 Then
Import Library "/Miscellaneous/sqlite3_64.obj" As SQLITE3 Link "stdlib", "kernel32"
#Else
Import Library "/Miscellaneous/sqlite3_32.obj" As SQLITE3 Link "stdlib", "kernel32"
#End If

Module MainModule

Declare PtrSafe Function sqlite3_open CDecl Lib SQLITE3 (ByVal filename As String, ByRef ppDb As LongPtr) As Long
Declare PtrSafe Function sqlite3_exec CDecl Lib SQLITE3 (ByVal pDb As LongPtr, ByVal sql As String, ByVal exec_callback As LongPtr, ByVal udp As LongPtr, ByRef errmsg As LongPtr) As Long
'...
```

> [!NOTE]
> StdCall names will be mangled with argument sizes, e.g. `int myfunc(int x, short y);` would be `myfunc@6`. It therefore may be better to use `CDecl`.

A documentation page will be dedicated to more fully explaining this in the future; for now if you need help with it, visit the tB Discord or Discussions section of the GitHub repository and ask.
19 changes: 19 additions & 0 deletions docs/Features/Advanced/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: Advanced Features
parent: Features
nav_order: 7
permalink: /Features/Advanced/
has_toc: false
---

# Advanced Features

Advanced twinBASIC features for low-level programming and system integration.

## Topics

- [Multithreading](Multithreading) - Thread safety and multithreading support
- [Assembly](Assembly) - Direct assembly insertion with Emit()
- [Static Linking](Static-Linking) - Static linking of OBJ and LIB files
- [API Declarations](API-Declarations) - Enhanced API and method declarations
- [Class and Module Features](Classes-and-Modules) - Parameterized constructors, ReadOnly, and exports
Loading